摘要:動(dòng)態(tài)代理深度解析引言說(shuō)起動(dòng)態(tài)代理,很多人可能都沒(méi)有直接去使用過(guò)。因?yàn)榈膭?dòng)態(tài)代理只能代理接口,而不能代理原始的類(lèi)。接下來(lái)是真正壓軸的環(huán)節(jié),實(shí)現(xiàn)自己的動(dòng)態(tài)代理類(lèi)。
Java動(dòng)態(tài)代理深度解析 引言
說(shuō)起動(dòng)態(tài)代理,很多人可能都沒(méi)有直接去使用過(guò)。但是只要用過(guò)Spring,那動(dòng)態(tài)代理就是一個(gè)是個(gè)繞不過(guò)的坎,因?yàn)镾pring的核心特性之一AOP就是基于動(dòng)態(tài)代理來(lái)實(shí)現(xiàn)的,那么什么情況下需要用到動(dòng)態(tài)代理呢?
場(chǎng)景考慮這樣一個(gè)教師的接口:
public interface Teacher { void teach(); }
假設(shè)我們有一個(gè)TeacherChan的實(shí)現(xiàn)類(lèi),陳老師教的是攝影:
public class TeacherChan implements Teacher { @Override public void teach() { System.out.println("大家好,我是陳老師,我教大家攝影!"); } }
另外還有一個(gè)TeacherCang的實(shí)現(xiàn)類(lèi),蒼老師教的是生物:
public class TeacherCang implements Teacher { @Override public void teach() { System.out.println("大家好,我是蒼老師,我教大家生物!"); } }
不管是陳老師還是蒼老師,只要實(shí)現(xiàn)了Teacher這個(gè)接口,給我們傳道授業(yè)解惑,為了禮貌起見(jiàn),我們總應(yīng)該給人家問(wèn)聲好吧。而問(wèn)好這件事不需要老師主動(dòng)要求,可以交給代理來(lái)做,每次有老師來(lái)上課,代理自動(dòng)做了問(wèn)好這件事。而代理類(lèi)又分為靜態(tài)代理和動(dòng)態(tài)代理,靜態(tài)代理在編寫(xiě)代碼時(shí)已經(jīng)確定了要代理的類(lèi),只能代理單一的類(lèi)型,在此略過(guò),今天重點(diǎn)講動(dòng)態(tài)代理。
Java動(dòng)態(tài)代理Java動(dòng)態(tài)代理創(chuàng)建代理類(lèi)的方法為:
Proxy.newProxyInstance(ClassLoader loader, Class>[] interfaces, InvocationHandler h)
其中ClassLoader是用來(lái)定義代理類(lèi)的class文件的,用系統(tǒng)默認(rèn)的就好,interfaces是要代理的接口,InvocationHandler是用來(lái)實(shí)際執(zhí)行代理方法的接口,常用做法是實(shí)現(xiàn)該接口,并將需要代理的類(lèi)實(shí)例對(duì)象傳進(jìn)去。
實(shí)現(xiàn)自己的方法執(zhí)行器:
public class JdkDynamicProxy implements InvocationHandler { private Object proxied; public JdkDynamicProxy(Object object) { this.proxied = object; } /** * proxy為創(chuàng)建的代理類(lèi)實(shí)例,method是本次被代理的方法,args是方法的參數(shù) */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("-------------------老師好[by jdk動(dòng)態(tài)代理]-------------------"); // 執(zhí)行代理方法 Object obj = method.invoke(proxied, args); System.out.println("-------------------老師再見(jiàn)[by jdk動(dòng)態(tài)代理]-------------------"); // 返回方法執(zhí)行結(jié)果 return obj; } }
創(chuàng)建代理類(lèi)對(duì)象并執(zhí)行:
// 代理陳老師 Teacher proxy1 = (Teacher) Proxy.newProxyInstance(Teacher.class.getClassLoader(), new Class[]{Teacher.class}, new JdkDynamicProxy(new TeacherChan())); proxy1.teach(); // 代理蒼老師 Teacher proxy2 = (Teacher) Proxy.newProxyInstance(Teacher.class.getClassLoader(), new Class[]{Teacher.class}, new JdkDynamicProxy(new TeacherCang())); proxy2.teach();
輸出結(jié)果:
-------------------老師好[by jdk動(dòng)態(tài)代理]------------------- 大家好,我是陳老師,我教大家攝影! -------------------老師再見(jiàn)[by jdk動(dòng)態(tài)代理]------------------- -------------------老師好[by jdk動(dòng)態(tài)代理]------------------- 大家好,我是蒼老師,我教大家生物! -------------------老師再見(jiàn)[by jdk動(dòng)態(tài)代理]-------------------
實(shí)際上,Java會(huì)過(guò)濾掉接口所有final、native等方法,并為剩下的所有符合條件的方法生成代理方法。而且,熟悉Spring的朋友應(yīng)該知道,Spring的AOP機(jī)制的實(shí)現(xiàn)不僅使用了Java的動(dòng)態(tài)代理,而且還引入了CGLib。因?yàn)镴ava的動(dòng)態(tài)代理只能代理接口,而不能代理原始的類(lèi)。那么為什么Java不能代理類(lèi)呢,答案是Java的單繼承機(jī)制。
深入Java動(dòng)態(tài)代理的實(shí)現(xiàn)Java的動(dòng)態(tài)代理是怎么實(shí)現(xiàn)的呢?其實(shí)很簡(jiǎn)單,就是運(yùn)行時(shí)生成一個(gè)代理類(lèi),該類(lèi)實(shí)現(xiàn)了需要代理的接口,并返回這個(gè)代理類(lèi)的實(shí)例對(duì)象給調(diào)用者。調(diào)試進(jìn)入Proxy.newProxyInstance()的方法內(nèi)部,可以看到在Proxy內(nèi)部生成class字節(jié)碼的方法:
// 生成的代理類(lèi)名前綴 private static final String proxyClassNamePrefix = "$Proxy"; // 生成的代理類(lèi)名序號(hào) private static final AtomicLong nextUniqueNumber = new AtomicLong(); // 序號(hào)值加1 long num = nextUniqueNumber.getAndIncrement(); // 代理類(lèi)名:$ProxyN String proxyName = proxyPkg + proxyClassNamePrefix + num; ... // 生成代理類(lèi)的class字節(jié)碼 byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
不難看出,生成的代理類(lèi)的類(lèi)名是$ProxyN的形式,所以我們經(jīng)常會(huì)看到$Proxy0這個(gè)類(lèi),就是動(dòng)態(tài)代理在運(yùn)行時(shí)生成的。
既然知道了動(dòng)態(tài)代理是怎么生成代理類(lèi)的了,那我們不妨把它生成的類(lèi)打印出來(lái)看看,到底里面是怎么實(shí)現(xiàn)的。
// 調(diào)用Java生成字節(jié)碼文件的方法 byte[] proxyClassFile = ProxyGenerator.generateProxyClass( "com.test.$proxy0.class", new Class[]{Teacher.class}, Modifier.FINAL); // 輸出文件到本地 FileOutputStream out = new FileOutputStream(new File("/temp/$Proxy0.class")); out.write(proxyClassFile); out.flush(); out.close();
用java反編譯軟件打開(kāi)生成的$Proxy0.class文件,內(nèi)容如下:
package com.test.$proxy0; import com.demos.java.basedemo.proxy.bean.Teacher; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; final class class extends Proxy implements Teacher { private static Method m1; private static Method m2; private static Method m3; private static Method m0; public class(InvocationHandler paramInvocationHandler) throws { super(paramInvocationHandler); } public final boolean equals(Object paramObject) throws { try { return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue(); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final String toString() throws { try { return (String)this.h.invoke(this, m2, null); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final void teach() throws { try { this.h.invoke(this, m3, null); return; } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final int hashCode() throws { try { return ((Integer)this.h.invoke(this, m0, null)).intValue(); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); m3 = Class.forName("com.demos.java.basedemo.proxy.bean.Teacher").getMethod("teach", new Class[0]); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); return; } catch (NoSuchMethodException localNoSuchMethodException) { throw new NoSuchMethodError(localNoSuchMethodException.getMessage()); } catch (ClassNotFoundException localClassNotFoundException) { throw new NoClassDefFoundError(localClassNotFoundException.getMessage()); } } }
可以看出,代理類(lèi)不僅代理了teach()這個(gè)方法,還代理了toString()、equals()和hashCode()方法,因?yàn)閖ava中所有的類(lèi)都是繼承Object類(lèi)的,所以自然有這些方法。其中還有一個(gè)地方要特別注意:生成的代理類(lèi)繼承了Proxy這個(gè)類(lèi),因此它只能通過(guò)實(shí)現(xiàn)接口來(lái)代理其他的類(lèi),現(xiàn)在知道為什么Java動(dòng)態(tài)代理只能代理接口了。
既然都講到這個(gè)份上了,當(dāng)然不能不繼續(xù)深入了。接下來(lái)是真正壓軸的環(huán)節(jié),實(shí)現(xiàn)自己的動(dòng)態(tài)代理類(lèi)。
手動(dòng)實(shí)現(xiàn)動(dòng)態(tài)代理首先分析一下實(shí)現(xiàn)動(dòng)態(tài)代理需要的步驟:
1.拼接java類(lèi)實(shí)現(xiàn),并生成class字節(jié)碼 2.加載class字節(jié)碼到JVM 3.實(shí)現(xiàn)自己的InvocationHandler處理器 4.對(duì)外提供接口
既然知道了Java動(dòng)態(tài)代理的原理,我們不妨借鑒Java生成的class文件格式,同時(shí)去掉默認(rèn)繼承的Proxy,使得我們自己的動(dòng)態(tài)代理既可以代理接口,也可以代理類(lèi)。先寫(xiě)出代理類(lèi)的格式(假設(shè)代理的是類(lèi)TeacherChan):
public class $Proxy0 extends TeacherChan { private InvocationHandler handler; private static Method m0; static { try { // 利用反射獲取TeacherChan的teach()方法 m0 = TeacherChan.class.getMethod("teach", new Class[]{}); } catch (NoSuchMethodException ne) { throw new NoSuchMethodError(ne.getMessage()); } } // 構(gòu)造方法中傳入代理類(lèi)處理器 public $Proxy0(InvocationHandler handler) { this.handler = handler; } public void teach() { try { // 收集teach()方法傳入的參數(shù),此處參數(shù)為空 Object[] args = new Object[]{}; // 執(zhí)行代理類(lèi)的teach() Object result = handler.invoke(this, m0, args); // 如果有返回值,此處要返回result } catch (Error|RuntimeException e) { throw e; } catch (Throwable t) { throw new UndeclaredThrowableException(t); } } }
好了,生成類(lèi)的格式大概就是這樣設(shè)計(jì),實(shí)際編寫(xiě)代碼時(shí)需要處理參數(shù)、返回值和異常的情況,略微有點(diǎn)繁瑣。下面是動(dòng)態(tài)類(lèi)生成器:
public class MyProxyGenerator { // 換行符 public static final String LINE_SEPARATOR = " "; // 動(dòng)態(tài)代理類(lèi)包名 public static final String PROXY_CLASS_PACKAGE = "com.demos.proxy"; // 動(dòng)態(tài)代理類(lèi)名前綴 public static final String PROXY_CLASS_NAME_PREFIX = "$Proxy"; // 動(dòng)態(tài)代理類(lèi)文件索引 public static final AtomicLong INDEX_GENERATOR = new AtomicLong(); // 動(dòng)態(tài)代理生成文件臨時(shí)目錄 public static final String PROXY_CLASS_FILE_PATH = "/temp"; /** * 生成代理類(lèi)并加載到JVM * @param clazz * @param methods * @throws Exception */ public static Class> generateAndLoadProxyClass(Class> clazz, Method[] methods) throws Exception { long index = INDEX_GENERATOR.getAndIncrement(); // 代理類(lèi)類(lèi)名 String className = PROXY_CLASS_NAME_PREFIX + index; String fileName = PROXY_CLASS_FILE_PATH + File.separator + className + ".java"; FileWriter writer = null; try { // 生成.java文件 writer = new FileWriter(new File(fileName)); writer.write(generateClassCode(PROXY_CLASS_PACKAGE, className, clazz, methods)); writer.flush(); // 編譯.java文件 compileJavaFile(fileName); // 加載class到JVM String classPath = PROXY_CLASS_FILE_PATH + File.separator + className + ".class"; Class> proxyClass = MyClassLoader.getInstance().findClass(classPath, PROXY_CLASS_PACKAGE + "." + className); return proxyClass; } finally { if (writer != null) { writer.close(); } } } /** * 編譯.java文件 * @param fileName * @throws IOException */ private static void compileJavaFile(String fileName) throws IOException { compileByTools(fileName); // compileByExec(fileName); } /** * 使用Runtime執(zhí)行javac命令 * 注意: 需要指定classpath, 否則找不到依賴(lài)的類(lèi) * 建議使用compileByTools() * @param fileName * @throws IOException */ @Deprecated private static void compileByExec(String fileName) throws IOException { // 獲取當(dāng)前的classpath String classpath = MyProxyGenerator.class.getResource("/").getPath(); // 運(yùn)行命令: javac -classpath ${classpath} ${filepath} String command = "javac -classpath " + classpath + " " + fileName; Process process = Runtime.getRuntime().exec(command); // 等待執(zhí)行, 并輸出錯(cuò)誤日志 try { InputStream errorStream = process.getErrorStream(); InputStreamReader inputStreamReader = new InputStreamReader(errorStream); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String line = null; while ((line = bufferedReader.readLine()) != null) { System.out.println(line); } int exitVal = process.waitFor(); System.out.println("Process exitValue: " + exitVal); } catch (InterruptedException e) { e.printStackTrace(); } } /** * 使用JDK自帶的JavaCompiler * @param fileName * @throws IOException */ private static void compileByTools(String fileName) throws IOException { // 獲取系統(tǒng)Java編譯器 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); // 獲取標(biāo)準(zhǔn)文件管理器實(shí)例 StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null); try { Iterable units = fileManager.getJavaFileObjects(fileName); JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, null, null, units); task.call(); } finally { fileManager.close(); } } /** * 拼接 class 代碼片段 * @param packageName 代理類(lèi)包名 * @param clazz 要代理的類(lèi)型 * @return */ private static String generateClassCode(String packageName, String className, Class> clazz, Method[] methods) throws Exception { StringBuilder classCodes = new StringBuilder(); /*--------------------包名和依賴(lài) start--------------------*/ classCodes.append("package ").append(packageName).append(";").append(LINE_SEPARATOR); classCodes.append(LINE_SEPARATOR); classCodes.append("import java.lang.reflect.*;").append(LINE_SEPARATOR); classCodes.append(LINE_SEPARATOR); /*--------------------包名和依賴(lài) start--------------------*/ /*--------------------類(lèi)定義 start--------------------*/ classCodes.append("public class ").append(className); if (clazz.isInterface()) { classCodes.append(" implements "); } else { classCodes.append(" extends "); } classCodes.append(clazz.getName()).append(" {").append(LINE_SEPARATOR); classCodes.append(LINE_SEPARATOR); /*--------------------類(lèi)定義 end--------------------*/ /*--------------------聲明變量InvocationHandler start--------------------*/ classCodes.append("private InvocationHandler handler;").append(LINE_SEPARATOR); classCodes.append(LINE_SEPARATOR); /*--------------------聲明變量InvocationHandler end--------------------*/ /*--------------------聲明代理方法 start--------------------*/ for (int i = 0; i < methods.length; i++) { classCodes.append("private static Method m").append(i).append(";").append(LINE_SEPARATOR); } classCodes.append(LINE_SEPARATOR); /*--------------------聲明代理方法 end--------------------*/ /*--------------------代理方法對(duì)象初始化 start--------------------*/ classCodes.append("static {").append(LINE_SEPARATOR); classCodes.append(" ").append("try {").append(LINE_SEPARATOR); for (int i = 0; i < methods.length; i++) { Method method = methods[i]; classCodes.append(" ").append(" ").append("m").append(i).append(" = ").append(clazz.getName()) .append(".class.getMethod("").append(method.getName()).append("", new Class[]{"); // 方法參數(shù) Parameter[] params = method.getParameters(); if (params.length != 0) { for (int j = 0; j < params.length; j++) { if (j != 0) { classCodes.append(", "); } Parameter param = params[j]; classCodes.append(param.getType().getName()).append(".class"); } } classCodes.append("});").append(LINE_SEPARATOR); } classCodes.append(" ").append("} catch (NoSuchMethodException ne) {").append(LINE_SEPARATOR); classCodes.append(" ").append(" ").append("throw new NoSuchMethodError(ne.getMessage());").append(LINE_SEPARATOR); classCodes.append(" ").append("}").append(LINE_SEPARATOR); classCodes.append("}").append(LINE_SEPARATOR); classCodes.append(LINE_SEPARATOR); /*--------------------代理方法對(duì)象初始化 end--------------------*/ /*--------------------定義構(gòu)造函數(shù) start--------------------*/ classCodes.append("public ").append(className).append("(InvocationHandler handler) {").append(LINE_SEPARATOR); classCodes.append(" ").append("this.handler = handler;").append(LINE_SEPARATOR); classCodes.append("}").append(LINE_SEPARATOR); classCodes.append(LINE_SEPARATOR); /*--------------------定義構(gòu)造函數(shù) end--------------------*/ /*--------------------填充其他函數(shù) start--------------------*/ classCodes.append(generateMethodCode(clazz, methods)); /*--------------------填充其他函數(shù) end--------------------*/ // 類(lèi)結(jié)束 classCodes.append("}").append(LINE_SEPARATOR); return classCodes.toString(); } /** * 拼接 method 代碼片段 * @param clazz * @param methods * @return * @throws Exception */ private static String generateMethodCode(Class> clazz, Method[] methods) throws Exception { StringBuilder methodCodes = new StringBuilder(); for (int i = 0; i < methods.length; i++) { Method method = methods[i]; // 返回類(lèi)型 String returnType = method.getReturnType().getName(); // 參數(shù)列表 Parameter[] params = method.getParameters(); // 異常列表 Class>[] exceptionTypes = method.getExceptionTypes(); /*--------------------方法定義 start--------------------*/ methodCodes.append("public ").append(returnType).append(" ").append(method.getName()); methodCodes.append("("); // 填充參數(shù) if (params.length != 0) { for (int j = 0; j < params.length; j++) { if (j != 0) { methodCodes.append(", "); } Parameter param = params[j]; methodCodes.append(param.getType().getName()).append(" ").append(param.getName()); } } methodCodes.append(")"); // 填充異常 if (exceptionTypes.length != 0) { methodCodes.append(" throws "); for (int j = 0; j < exceptionTypes.length; j++) { if (j != 0) { methodCodes.append(", "); } methodCodes.append(exceptionTypes[j].getName()); } } methodCodes.append(" {").append(LINE_SEPARATOR); /*--------------------方法定義 end--------------------*/ /*--------------------方法體 start--------------------*/ methodCodes.append(" ").append("try {").append(LINE_SEPARATOR); // 方法參數(shù) methodCodes.append(" ").append(" ").append("Object[] args = new Object[]{"); if (params.length != 0) { for (int j = 0; j < params.length; j++) { if (j != 0) { methodCodes.append(", "); } Parameter param = params[j]; methodCodes.append(param.getName()); } } methodCodes.append("};").append(LINE_SEPARATOR); // 執(zhí)行InvocationHandler.invoke() methodCodes.append(" ").append(" ").append("Object result = handler.invoke(this, m").append(i) .append(", args);").append(LINE_SEPARATOR); // 返回結(jié)果 if (!"void".equals(returnType)) { methodCodes.append(" ").append(" ").append("return (").append(returnType).append(") result;").append(LINE_SEPARATOR); } // 異常處理 methodCodes.append(" ").append("} catch (Error|RuntimeException"); for (Class> exceptionType : exceptionTypes) { methodCodes.append("|").append(exceptionType.getName()); } methodCodes.append(" e) {").append(LINE_SEPARATOR); methodCodes.append(" ").append(" ").append("throw e;").append(LINE_SEPARATOR); methodCodes.append(" ").append("} catch (Throwable t) {").append(LINE_SEPARATOR); methodCodes.append(" ").append(" ").append("throw new UndeclaredThrowableException(t);").append(LINE_SEPARATOR); methodCodes.append(" ").append("}").append(LINE_SEPARATOR); /*--------------------方法體 end--------------------*/ // 方法結(jié)束 methodCodes.append("}").append(LINE_SEPARATOR).append(LINE_SEPARATOR); } return methodCodes.toString(); } }
實(shí)際上只是拼接前面給出的代理類(lèi)實(shí)現(xiàn)而已,代碼量有點(diǎn)大,但并不難理解。
下一步,實(shí)現(xiàn)自己的類(lèi)加載器,來(lái)加載生成的class字節(jié)碼:
public class MyClassLoader extends ClassLoader { private static MyClassLoader loader; private MyClassLoader() { } public static MyClassLoader getInstance() { if (loader == null) { synchronized (MyClassLoader.class) { // 得到鎖首先檢查loader是否已經(jīng)存在, 避免重復(fù)創(chuàng)建 if (loader == null) { loader = new MyClassLoader(); } } } return loader; } /** * 加載class文件,并返回類(lèi)型對(duì)象 * * @param filePath * @param className * @return * @throws ClassNotFoundException */ public Class> findClass(String filePath, String className) throws ClassNotFoundException { try { // 讀取指定class文件的字節(jié)碼 byte[] classBytes = Files.readAllBytes(Paths.get(filePath)); // 加載類(lèi)并返回class類(lèi)型對(duì)象 Class> clazz = defineClass(className, classBytes, 0, classBytes.length); return clazz; } catch (IOException e) { e.printStackTrace(); } throw new ClassNotFoundException(className); } }
到這一步,復(fù)雜的工作基本做完了,接下來(lái)只剩下自定義處理器類(lèi)和對(duì)外接口了
自定義處理器類(lèi)與Java動(dòng)態(tài)代理的方式相同:
public class MyInvocationHandler implements InvocationHandler { private Object proxied; public MyInvocationHandler(Object object) { this.proxied = object; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("-------------------老師好[by 自定義動(dòng)態(tài)代理]-------------------"); Object obj = method.invoke(proxied, args); System.out.println("-------------------老師再見(jiàn)[by 自定義動(dòng)態(tài)代理]-------------------"); return obj; } }
對(duì)外接口:
public class MyDynamicProxy { public staticT newProxyInstance(Class clazz, InvocationHandler handler) throws Exception { // 要代理的方法: public & !final Method[] proxyMethods = Arrays.stream(clazz.getMethods()) .filter(method -> !Modifier.isFinal(method.getModifiers())) .collect(Collectors.toList()) .toArray(new Method[0]); // 生成的代理類(lèi) Class> proxyClass = MyProxyGenerator.generateAndLoadProxyClass(clazz, proxyMethods); // 代理類(lèi)的構(gòu)造方法 Constructor c = proxyClass.getConstructor(InvocationHandler.class); // 創(chuàng)建代理類(lèi)對(duì)象 Object proxyObj = c.newInstance(handler); return (T) proxyObj; } }
搞定,測(cè)試一下效果:
TeacherChan proxy1 = MyDynamicProxy.newProxyInstance( TeacherChan.class, new MyInvocationHandler(new TeacherChan())); proxy1.teach(); TeacherCang proxy2 = MyDynamicProxy.newProxyInstance( TeacherCang.class, new MyInvocationHandler(new TeacherCang())); proxy2.teach();
輸出:
-------------------老師好[by 自定義動(dòng)態(tài)代理]------------------- 大家好,我是陳老師,我教大家攝影! -------------------老師再見(jiàn)[by 自定義動(dòng)態(tài)代理]------------------- -------------------老師好[by 自定義動(dòng)態(tài)代理]------------------- 大家好,我是蒼老師,我教大家生物! -------------------老師再見(jiàn)[by 自定義動(dòng)態(tài)代理]-------------------
完美!大功告成!
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/70841.html
摘要:內(nèi)部使用了的動(dòng)態(tài)代理為目標(biāo)接口生成了一個(gè)動(dòng)態(tài)代理類(lèi),這里會(huì)生成一個(gè)動(dòng)態(tài)代理原理統(tǒng)一的方法攔截器,同時(shí)為接口的每個(gè)方法生成一個(gè)攔截器,并解析方法上的元數(shù)據(jù),生成一個(gè)請(qǐng)求模板。的核心源碼解析到此結(jié)束了,不知道是否對(duì)您有無(wú)幫助,可留言跟我交流。 Feign是一個(gè)聲明式的Web服務(wù)客戶(hù)端。這使得Web服務(wù)客戶(hù)端的寫(xiě)入更加方便 要使用Feign創(chuàng)建一個(gè)界面并對(duì)其進(jìn)行注釋。它具有可插拔注釋支持,包...
摘要:是一個(gè)分布式服務(wù)框架,以及治理方案。手寫(xiě)注意要點(diǎn)手寫(xiě)注意要點(diǎn)基于上文中對(duì)于協(xié)議的理解,如果我們自己去實(shí)現(xiàn),需要考慮哪些技術(shù)呢其實(shí)基于圖的整個(gè)流程應(yīng)該有一個(gè)大概的理解?;谑謱?xiě)實(shí)現(xiàn)基于手寫(xiě)實(shí)現(xiàn)理解了協(xié)議后,我們基于來(lái)實(shí)現(xiàn)一個(gè)通信框架。閱讀這篇文章之前,建議先閱讀和這篇文章關(guān)聯(lián)的內(nèi)容。[1]詳細(xì)剖析分布式微服務(wù)架構(gòu)下網(wǎng)絡(luò)通信的底層實(shí)現(xiàn)原理(圖解)[2][年薪60W的技巧]工作了5年,你真的理解N...
摘要:如問(wèn)到是否使用某框架,實(shí)際是是問(wèn)該框架的使用場(chǎng)景,有什么特點(diǎn),和同類(lèi)可框架對(duì)比一系列的問(wèn)題。這兩個(gè)方向的區(qū)分點(diǎn)在于工作方向的側(cè)重點(diǎn)不同。 [TOC] 這是一份來(lái)自嗶哩嗶哩的Java面試Java面試 32個(gè)核心必考點(diǎn)完全解析(完) 課程預(yù)習(xí) 1.1 課程內(nèi)容分為三個(gè)模塊 基礎(chǔ)模塊: 技術(shù)崗位與面試 計(jì)算機(jī)基礎(chǔ) JVM原理 多線(xiàn)程 設(shè)計(jì)模式 數(shù)據(jù)結(jié)構(gòu)與算法 應(yīng)用模塊: 常用工具集 ...
摘要:爬蟲(chóng)又一個(gè)爬蟲(chóng)實(shí)現(xiàn)原文簡(jiǎn)介小強(qiáng)當(dāng)時(shí)不知道為啥選了這么個(gè)名字,又長(zhǎng)又難記,導(dǎo)致編碼的過(guò)程中因?yàn)閱卧~的拼寫(xiě)問(wèn)題耽誤了好長(zhǎng)時(shí)間。我是一個(gè)小強(qiáng)爬蟲(chóng)線(xiàn)程數(shù)健壯說(shuō)到健壯,這里主要體現(xiàn)在以下幾個(gè)方面應(yīng)對(duì)封鎖這里我們使用動(dòng)態(tài)代理來(lái)解決這個(gè)問(wèn)題。 cockroach 爬蟲(chóng):又一個(gè) java 爬蟲(chóng)實(shí)現(xiàn) 原文 簡(jiǎn)介 cockroach[小強(qiáng)] 當(dāng)時(shí)不知道為啥選了這么個(gè)名字,又長(zhǎng)又難記,導(dǎo)致編碼的過(guò)程中因?yàn)閱?..
摘要:自定義注解不生效原因解析及解決方法背景項(xiàng)目中,自己基于實(shí)現(xiàn)了一套緩存注解。但是最近出現(xiàn)一種情況緩存竟然沒(méi)有生效,大量請(qǐng)求被擊穿到層,導(dǎo)致壓力過(guò)大。至此,問(wèn)題得到解決。 自定義注解不生效原因解析及解決方法 背景: 項(xiàng)目中,自己基于spring AOP實(shí)現(xiàn)了一套java緩存注解。但是最近出現(xiàn)一種情況:緩存竟然沒(méi)有生效,大量請(qǐng)求被擊穿到db層,導(dǎo)致db壓力過(guò)大?,F(xiàn)在我們看一下具體代碼情形(代...
閱讀 3810·2021-11-12 10:34
閱讀 2824·2021-09-22 15:14
閱讀 793·2019-08-30 15:53
閱讀 3207·2019-08-30 12:53
閱讀 1296·2019-08-29 18:32
閱讀 2775·2019-08-29 16:41
閱讀 1071·2019-08-26 13:40
閱讀 1816·2019-08-23 18:07