文章目錄
- 靜態代理
- Jdk動態代理
- cglib動態代理
- 使用案例
- 低配Mybatis
- 低配Feign
- 攔截器
- 附錄代碼
大家好,我是入錯行的bug貓。(http://blog.csdn.net/qq_41399429,謝絕轉載)
每天進步一點,今日再接再勵~
動態代理在Java中有著廣泛的應用,比如Spring AOP、Mybatis數據查詢、RPC遠程調用、性能監控,甚至事務處理等。
代理模式,根據代碼的生成時機,分為兩種:
- 靜態代理:代碼塊在源碼階段已存在,經過編譯之后生成在class文件中;
- 動態代理:代碼塊在運行過程中,根據運行環境參數決定代碼如何生成,自動生成class字節碼,然后再加載到JVM中;
所謂靜態,也就是在程序運行前,就已經存在代理類的字節碼文件,代理類和被代理類的關系在運行前就確定了。
而動態代理的源碼,是在程序運行期間由JVM根據反射等機制動態的生成,所以在運行前并不存在代理類的字節碼文件。
而根據動態生成字節碼的技術手段,又分兩種:
- Jdk動態代理
- cglib動態代理
靜態代理
先了解靜態代理,然后理解靜態代理的優缺點缺點,再來學習動態代理;
編寫一個接口IUserService
,以及該接口的一個實現類UserService
interface IUserService {void findById(String uid);void update(Object user, String uid);
}class UserService implements IUserService {public void findById(String uid) {System.out.println("查詢 findById");}public void update(Object user, String uid) {System.out.println("更新 update");}
}
通過靜態代理對IUserService
已存在的實例,進行功能增強;在調用findById
和update
之前記錄一些日志,模擬開啟事務。
寫一個代理類UserServiceProxy
,代理類需要實現IUserService
;整體結構有些類似裝飾模式;
class UserServiceProxy implements IUserService {private final IUserService target; // 被代理的對象public UserServiceProxy() {this.target = new UserService(); //被代理對象,是在代理類中生成}@Overridepublic void findById(String uid) {try {before();target.findById(uid); // 這里才實際調用真實對象的方法after();} catch ( Exception ex ) {exception(ex);throw ex;}}@Overridepublic void update(Object user, String uid) {try {before();target.update(user, uid); // 這里才實際調用真實對象的方法after();} catch ( Exception ex ) {exception(ex);throw ex;}}private void before() { // 在執行方法之前執行System.out.println(String.format("log start time [%s] ", new Date()));System.out.println("開啟事務");}private void after() { // 在執行方法之后執行System.out.println(String.format("log end time [%s] ", new Date()));System.out.println("提交事務");}private void exception(Exception ex){System.out.println(String.format("log error [%s] ", ex.getMessage()));System.out.println("提交回滾");}
}
客戶端測試:
@Testpublic void demo1(){IUserService proxy = new UserServiceProxy();proxy.findById("1");System.out.println("");proxy.update(new Object(), "4");}
輸出:
log start time [Sat Jun 17 09:34:05 CST 2023]
開啟事務
查詢 selectById
log end time [Sat Jun 17 09:34:05 CST 2023]
提交事務log start time [Sat Jun 17 09:34:05 CST 2023]
開啟事務
更新 update
log end time [Sat Jun 17 09:34:05 CST 2023]
提交事務
通過靜態代理,我們達到了功能增強的目的,而且沒有侵入原代碼,這是靜態代理的一個優點。
靜態代理實現簡單,且不侵入原代碼,但是,當場景稍微復雜一些的時候,靜態代理的缺點也會暴露出來:
-
當需要代理多個類的時候,由于代理對象要實現與目標對象一致的接口,有兩種方式:
1.1 只維護一個代理類,由這個代理類實現多個接口,但是這樣就導致代理類過于龐大;
1.2 新建多個代理類,每個目標對象對應一個代理類,但是這樣會產生過多的代理類 -
當接口需要增加、刪除、修改方法的時候,目標對象與代理類都要同時修改,不易維護。
如何改進?
可以發現,代理類的代碼塊流程絕大部分是相似的:在執行真實方法之前執行before
,在執行真實方法成功之后執行after
,發生異常執行exception
;
類似固定的模板,就可以使用程序來自動編寫代碼,用程序自動寫程序,也就是動態代理。
哪些類可以動態生成代碼?Enhancer
、InterfaceMaker
、BeanGenerator
有機會再單獨寫一篇
實現動態代理的思考方向:
為了讓生成的代理類與目標對象保持一致性,從現在開始將介紹以下兩種最常見的方式:
通過實現接口的方式:JDK動態代理
通過繼承類的方式:CGLIB動態代理
Jdk動態代理
JDK動態代理主要涉及兩個類:java.lang.reflect.Proxy
和java.lang.reflect.InvocationHandler
編寫一個調用邏輯處理器JdkProxyHandler
類,提供日志增強功能,并實現InvocationHandler
接口;
在JdkProxyHandler
中維護一個目標對象,這個對象是被代理的對象;在invoke
方法中編寫方法調用的邏輯處理;
class JdkProxyHandler implements InvocationHandler {private final Object target; // 被代理的對象,實際的方法執行者public JdkProxyHandler(Object target) {this.target = target;}/*** @param proxy 代理對象實例 todo ①* @param method 被代理類的Interface中的方法; todo ②* @param args * */@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {before();//反射調用 target 的 method 方法。//proxy是代理對象實例,因此在反射調用的時候,需要替換成被代理類target對象;Object result = method.invoke(target, args); after();return result; // 返回方法的執行結果} catch ( Exception ex ) {exception(ex);throw ex;}}private void before() { // 在執行方法之前執行System.out.println(String.format("log start time [%s] ", new Date()));System.out.println("開啟事務");}private void after() { // 在執行方法之后執行System.out.println(String.format("log end time [%s] ", new Date()));System.out.println("提交事務");}private void exception(Exception ex){System.out.println(String.format("log error [%s] ", ex.getMessage()));System.out.println("提交回滾");}
}
編寫客戶端,獲取動態生成的代理類的對象須借助java.lang.reflect.Proxy
類的newProxyInstance
方法:
@Testpublic void demo2(){// 設置變量可以保存動態代理類,默認名稱以 $Proxy0 格式命名System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");// 1. 創建被代理的對象。實際代碼中,可以使用到對象工廠IUserService userService = new UserService();// 2. 代理類請求處理器:攔截處理代理對象上的所有的方法調用。// 和demo1中的UserServiceProxy類相似。// 由于JdkProxyHandler可以復用,被代理類(userService)可以使多例,所以JdkProxyHandler也應該是多例,被代理類應該顯示傳入。// 主要JdkProxyHandler構造器使用Interface作為入參,因此JdkProxyHandler可以代理一切Interface的實現類。InvocationHandler proxyHandler = new JdkProxyHandler(userService);// 3. 獲取對應的 ClassLoader。類加載機制,如果搞錯ClassLoader,可能會導致動態生成的代理類,無法被加載:提示 ClassNotFoundException;保持和被代理類在一個ClassLoader中ClassLoader classLoader = userService.getClass().getClassLoader();// 4. 獲取所有接口的Class,這里的UserService只實現了一個接口IUserService,Class[] interfaces = userService.getClass().getInterfaces();/*5.根據上面提供的信息,創建代理對象 在這個過程中,a.JDK會通過根據傳入的參數信息動態地在內存中創建和.class 文件等相關字節碼;b.然后根據相應的字節碼轉換成對應的class加載到JVM中;c.然后調用newInstance()創建代理實例;*/IUserService proxy = (IUserService) Proxy.newProxyInstance(classLoader, interfaces, proxyHandler);//輸出動態代理之后的class代碼//generateClassFile(proxy.getClass(), "UserServiceProxy"); //在代碼在文章最后// 調用代理的方法proxy.findById("3");System.out.println("");proxy.update(new Object(), "4");//JDK動態代理,底層本質是使用方法反射;性能瓶頸受Jdk版本影響}
執行之后可以查看動態生成的代理類:
//Jdk動態生成的類片段
public final class UserServiceProxy extends Proxy implements IUserService {private static Method m3;private static Method m4;//注意這個入參,就是JdkProxyHandlerpublic UserServiceProxy(InvocationHandler var1) throws {super(var1);}public final void findById(String var1) throws {try {//注意第一個參數this,即代表JdkProxyHandler.invoke的第一個入參,是代理類本身; todo ①//第二個參數m4,是從cn.bugcat.code.IUserService中獲取 todo ②super.h.invoke(this, m4, new Object[]{var1});} catch (RuntimeException | Error var3) {throw var3;} catch (Throwable var4) {throw new UndeclaredThrowableException(var4);}}public final void update(Object var1, String var2) throws {try {super.h.invoke(this, m3, new Object[]{var1, var2});} catch (RuntimeException | Error var4) {throw var4;} catch (Throwable var5) {throw new UndeclaredThrowableException(var5);}}static {try {//findById、update方法,都是從Interface(IUserService)中獲取 todo ②m4 = Class.forName("cn.bugcat.code.IUserService").getMethod("findById", Class.forName("java.lang.String"));m3 = Class.forName("cn.bugcat.code.IUserService").getMethod("update", Class.forName("java.lang.Object"), Class.forName("java.lang.String"));} catch (NoSuchMethodException var2) {throw new NoSuchMethodError(var2.getMessage());} catch (ClassNotFoundException var3) {throw new NoClassDefFoundError(var3.getMessage());}}
}
UserServiceProxy
繼承了Proxy類,并且實現了被代理的所有接口,以及equals、hashCode、toString等方法;- 由于
UserServiceProxy
繼承了Proxy
類,所以每個代理類都會關聯一個InvocationHandler
方法調用處理器; - 類和所有方法都被
public final
修飾,所以代理類只可被使用,不可以再被繼承; - 每個方法都有一個Method對象來描述,Method對象在
static
靜態代碼塊中創建,以m + 數字
的格式命名; - 調用方法的時候通過
super.h.invoke(this, m1, new Object[]{});
調用,其中的super.h.invoke
實際上是在創建代理的時候傳遞給Proxy.newProxyInstance
的 JdkProxyHandler對象,它繼承InvocationHandler
類,負責實際的調用處理邏輯; - 而
JdkProxyHandler
的invoke方法接收到method、args等參數后,進行一些處理,然后通過反射讓被代理的對象target執行方法;
仔細觀察JdkProxyHandler
,構造器中入參使用Object類型接受,也就是意味JdkProxyHandler
類不光可以傳入UserService
實現類,也可以傳入其他任何對象實例;
Proxy.newProxyInstance
是根據第二個參數Interfaces動態生成方法,而這些方法恰好UserService
也實現了,代理類和具體類,通過interfaces
聯系到一起;
最終執行JdkProxyHandler
構造器 入參對象的所有方法 ,都會統一執行JdkProxyHandler.invoke
代碼。
cglib動態代理
spring 框架中內置了cglib
相關Jar包內容,spring項目可以直接使用:
被代理類:原對象;
代理子類:由cglib自動根據原對象生成的子類;
class CglibProxyHandler implements MethodInterceptor {/*** @param target 代理子類對象;* @param method 被代理類的方法;* @param args * @param methodProxy 被代理的句柄方法 todo ③* */@Overridepublic Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {try {before();//注意這里是調用invokeSuper而不是invoke,否則死循環。 todo ③//method是被代理的方法,但是由于tagert是代理子類,執行method.invoke,實際上是表示執行代理子類的方法,代理子類又會繼續執行MethodInterceptor.intercept方法,導致又回到此處代碼,造成死循環;//此處應該直接執行invokeSuper,表示直接調用被代理類的句柄方法;Object result = methodProxy.invokeSuper(target, args); after();return result; // 返回方法的執行結果} catch ( Exception ex ) {exception(ex);throw ex;}}private void before() { // 在執行方法之前執行System.out.println(String.format("log start time [%s] ", new Date()));System.out.println("開啟事務");}private void after() { // 在執行方法之后執行System.out.println(String.format("log end time [%s] ", new Date()));System.out.println("提交事務");}private void exception(Exception ex){System.out.println(String.format("log error [%s] ", ex.getMessage()));System.out.println("提交回滾");}
}
調用方式:
@Test public void demo3(){System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, paths);MethodInterceptor proxyHandler = new CglibProxyHandler();Enhancer enhancer = new Enhancer();enhancer.setSuperclass(UserService.class); // 設置超類,cglib是通過繼承來實現的enhancer.setCallback(proxyHandler);IUserService userService = (IUserService)enhancer.create(); // 創建代理類userService.findById("4");userService.update(new Object(), "4");}
動態生成類片段:
public class UserService$$EnhancerByCGLIB$$a1b35990 extends UserService implements Factory {final void CGLIB$update$0(Object var1, String var2) {super.update(var1, var2); //調用父類方法 => UserService.update}public final void update(Object var1, String var2) { //代理類暴露的方法,等價于重寫了父類UserService的update方法MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; // 實際上是我們自定義的CglibProxyHandler對象if (var10000 == null) {CGLIB$BIND_CALLBACKS(this);var10000 = this.CGLIB$CALLBACK_0;}if (var10000 != null) {// 第一個參數,代理類對象 todo ③// CGLIB$update$0$Method 父類對應的方法// CGLIB$update$0$Proxy當前代理的 CGLIB$update$0方法,該方法會繼續調用父類的對應方法var10000.intercept(this, CGLIB$update$0$Method, new Object[]{var1, var2}, CGLIB$update$0$Proxy);} else {super.update(var1, var2);}}
}
Jdk動態代理與cglib動態代理對比
- Jdk動態代理:基于Java反射機制實現,必須要實現了接口的類,才能用這種辦法生成代理對象。
- cglib動態代理:基于ASM機制實現,通過操作字節碼生成子類,代理代碼塊集成在子類上,實際調用被代理類的方法時,和原生調用效率一樣;
- cglib由于使用繼承被代理實現,因此如果是類、方法被
final
修飾,則無法使用!被cglib代理的類,無法再次被代理! - Jdk不受
final
關鍵詞影響;被代理之后,仍然可以被Jdk再次代理; - cglib在創建代理子類的時候,可以通過
CallbackFilter
,可以為每個方法創建單獨的MethodInterceptor
;后續調用時,方法與方法之間,代碼在物理層面隔離,互相不影響; - Jdk只能在
InvocationHandler
實現類中,需要自行做分支處理;
使用案例
低配Mybatis
interface UserDao {String findById(String uid);String update(Object user, String uid);
}class MapperProxyHandler implements MethodInterceptor {private static Map<String, Map<String, String>> mapperMap = new HashMap<>();static {//初始化,模擬Mapper.xml文件Map<String, String> mapper = new HashMap<>();//Map的key,及為UserDao中的方法名,以及Mapper.xml的標簽idmapper.put("findById", "select * from user where id = ?");mapper.put("update", "update user set name = ? where id = ?");//UserDao.class.getName(),對應到Mapper.xml的namespacemapperMap.put(UserDao.class.getName(), mapper); }@Overridepublic Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {String methodName = method.getName();Class<?> mapperClass = method.getDeclaringClass();Map<String, String> mapper = mapperMap.get(mapperClass.getName());if( mapper != null ){return mapper.get(methodName);}return null;}
}@Testpublic void demo4(){System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, paths);MethodInterceptor proxyHandler = new MapperProxyHandler();Enhancer enhancer = new Enhancer();enhancer.setSuperclass(Object.class); // 設置超類;此時UserDao只是一個Interface,沒有任何具體的實現類,因此超類設置為默認的Object;enhancer.setInterfaces(new Class[]{UserDao.class});enhancer.setCallback(proxyHandler);UserDao userDao = (UserDao)enhancer.create(); // 創建代理類String findById = userDao.findById("4");System.out.println(findById); //打印sqlString update = userDao.update(new Object(), "4");System.out.println(update);}
低配Feign
interface MyFeign {@RequestMapping(value = "http://127.0.0.1:8080/aq", method = RequestMethod.POST)ResponseEntity<Void> aq();@RequestMapping(value = "https://www.baidu.com/s", method = RequestMethod.GET)String baidu(@RequestParam("wd") String keyword);}class FeignProxyHandler implements MethodInterceptor {/*** target:代理子類* method:被代理類方法*/@Overridepublic Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {//method是被代理類中的方法,因此可以通過method獲取到:方法上注解;入參列表;入參上的注解;方法的返回類型RequestMapping requestMapping = AnnotationUtils.findAnnotation(method, RequestMapping.class);RequestMethod[] requestMethods = requestMapping.method();String[] paths = requestMapping.value();Type returnType = method.getGenericReturnType();Parameter[] parameters = method.getParameters();Map<String, Integer> argsIndex = new HashMap<>();for ( int idx = 0; idx < parameters.length; idx++ ) {Parameter parameter = parameters[idx];RequestParam requestParam = AnnotationUtils.findAnnotation(parameter, RequestParam.class);argsIndex.put(requestParam.name(), idx);}Map<String, Object> reqMap = new HashMap<>();argsIndex.forEach((pname, idx) -> {reqMap.put(pname, args[idx]);});if( requestMethods[0] == RequestMethod.GET ){String resp = HttpFactory.mutate().doGet().send(paths[0], reqMap);return resultType(resp, returnType);} else if (requestMethods[0] == RequestMethod.POST ){String resp = HttpFactory.mutate().doPost().send(paths[0], reqMap);return resultType(resp,returnType);}return null;}private <R> R resultType(String resp, Type type){if( resp == null ){return null;}if( type instanceof Class ){if( String.class.isAssignableFrom((Class) type) ){return (R) resp;}}return JSONObject.parseObject(resp, type);}
}@Testpublic void demo5(){System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, paths);MethodInterceptor proxyHandler = new FeignProxyHandler();Enhancer enhancer = new Enhancer();enhancer.setSuperclass(Object.class); // 設置超類;此時MyFeign只是一個Interface,沒有任何具體的實現類,因此超類設置為默認的Object;enhancer.setInterfaces(new Class[]{MyFeign.class});enhancer.setCallback(proxyHandler);MyFeign feign = (MyFeign)enhancer.create(); // 創建代理類ResponseEntity<Void> aq = feign.aq();System.out.println(JSONObject.toJSONString(aq));String baidu = feign.baidu("csdn");System.out.println(baidu);}
攔截器
攔截器,不屬于sevlet
、filter
這一類物理層面存在的組件,而是通過代碼邏輯出來的一種代碼結構;通過對動態代理學習,我們可以在Object result = method.invoke(target, args);
Object result = methodProxy.invokeSuper(target, args);
代碼的前后,添加自定義的代碼,增強被代理類方法;
但是,這部分屬于核心代碼JdkProxyHandler CglibProxyHandler,一般編寫完之后,會統一存放在公共位置。如果業務模塊想添加功能,勢必需要修改到這部分核心代碼;違背了支持擴展、封閉修改原則;
因此在此基礎之上,邏輯出一套攔截器模式,對業務模塊開放修改;
//切入點對象
class Point {private final Object target;private final Method method;private final Object[] args;private final MethodProxy methodProxy;public Point(Object target, Method method, Object[] args, MethodProxy methodProxy) {this.target = target;this.method = method;this.args = args;this.methodProxy = methodProxy;}public Object postHandle() throws Throwable{Object result = methodProxy.invokeSuper(target, args);return result;}
}//攔截器接口
interface MyInterceptor {default Object doInterceptor(Point point) throws Throwable {return point.postHandle();}
}class MyProxyHandler implements MethodInterceptor {private final MyInterceptor interceptor;public MyProxyHandler(MyInterceptor interceptor) {this.interceptor = interceptor;}@Overridepublic Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {//注意,本應該在此處執行的「Object result = methodProxy.invokeSuper(target, args);」,被轉移到Point#postHandle方法內了 todo ⑤Point point = new Point(target, method, args, methodProxy);return interceptor.doInterceptor(point);}
}@Testpublic void demo6() {MyInterceptor interceptor = new MyInterceptor(){@Overridepublic Object doInterceptor(Point point ) throws Throwable {try {System.out.println("befor");Object resp = point.postHandle(); //在此處,才真正執行被代理類的方法 todo ⑤;//本應該在MethodInterceptor子類中執行的代碼,被轉移到MyInterceptor子類了;MethodInterceptor子類只能有一個,但是MyInterceptor子類,結合責任鏈設計模式,卻可以有很多個!System.out.println("success");return resp;} catch ( Throwable e ) {System.out.println("error");throw e;} finally {System.out.println("finally");}}};MethodInterceptor proxyHandler = new MyProxyHandler(interceptor);Enhancer enhancer = new Enhancer();enhancer.setSuperclass(UserService.class); // 設置超類,cglib是通過繼承來實現的enhancer.setCallback(proxyHandler);IUserService userService = (IUserService)enhancer.create(); // 創建代理類userService.findById("4");userService.update(new Object(), "4");}
可用來動態生成代碼的有很多,這里推薦cglib的Enhancer
、InterfaceMaker
、BeanGenerator
、ClassWriter
類;
畢竟現在基本上都是spring全家桶,而且cglib也被spring收編,只要是spring項目就可以直接使用;
動態代理也是面試必考:涉及到AOP、攔截器;而AOP的運用又是不勝枚舉,攔截器結合責任鏈模式,也是在各種場所都用運用;
以上,就醬~
~the end~
附錄代碼
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.io.FileUtils;
import org.junit.Test;
import org.springframework.cglib.core.DebuggingClassWriter;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.client.RestTemplate;
import sun.misc.ProxyGenerator;import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;public class ProxyDemo {private static String paths = "E:\\tmp\\proxy\\";/*** 將根據類信息動態生成的二進制字節碼保存到硬盤中,默認的是clazz目錄下* params: clazz 需要生成動態代理類的類* proxyName: 為動態生成的代理類的名稱*/public static void generateClassFile(Class clazz, String proxyName) {try {File file = new File(paths);FileUtils.deleteDirectory(new File(paths));file.mkdirs();} catch ( IOException e ) {e.printStackTrace();}// 根據類信息和提供的代理類名稱,生成字節碼byte[] classFile = ProxyGenerator.generateProxyClass(proxyName, clazz.getInterfaces());try (FileOutputStream out = new FileOutputStream(paths + proxyName + ".class")){out.write(classFile);out.flush();} catch (Exception e) {e.printStackTrace();}}@Testpublic void demo1(){IUserService proxy = new UserServiceProxy();proxy.findById("1");System.out.println("");proxy.update(new Object(), "4");}@Testpublic void demo2(){// 設置變量可以保存動態代理類,默認名稱以 $Proxy0 格式命名System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");// 1. 創建被代理的對象。實際代碼中,可以使用到對象工廠IUserService userService = new UserService();// 2. 代理類請求處理器:攔截處理代理對象上的所有的方法調用。// 和demo1中的UserServiceProxy類相似。// 由于JdkProxyHandler可以復用,被代理類(userService)可以使多例,所以JdkProxyHandler也應該是多例,被代理類應該顯示傳入。// 主要JdkProxyHandler構造器使用Interface作為入參,因此JdkProxyHandler可以代理一切Interface的實現類。InvocationHandler proxyHandler = new JdkProxyHandler(userService);// 3. 獲取對應的 ClassLoader。類加載機制,如果搞錯ClassLoader,可能會導致動態生成的代理類,無法被加載:提示 ClassNotFoundException;保持和被代理類在一個ClassLoader中ClassLoader classLoader = userService.getClass().getClassLoader();// 4. 獲取所有接口的Class,這里的UserService只實現了一個接口IUserService,Class[] interfaces = userService.getClass().getInterfaces();/*5.根據上面提供的信息,創建代理對象 在這個過程中,a.JDK會通過根據傳入的參數信息動態地在內存中創建和.class 文件等相關字節碼;b.然后根據相應的字節碼轉換成對應的class加載到JVM中;c.然后調用newInstance()創建代理實例;*/IUserService proxy = (IUserService) Proxy.newProxyInstance(classLoader, interfaces, proxyHandler);//輸出動態代理之后的class代碼//generateClassFile(proxy.getClass(), "UserServiceProxy"); //在代碼在文章最后// 調用代理的方法proxy.findById("3");System.out.println("");proxy.update(new Object(), "4");//JDK動態代理,底層本質是使用方法反射;性能瓶頸受Jdk版本影響}@Test public void demo3(){System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, paths);MethodInterceptor proxyHandler = new CglibProxyHandler();Enhancer enhancer = new Enhancer();enhancer.setSuperclass(UserService.class); // 設置超類,cglib是通過繼承來實現的enhancer.setCallback(proxyHandler);IUserService userService = (IUserService)enhancer.create(); // 創建代理類userService.findById("4");userService.update(new Object(), "4");}@Testpublic void demo4(){System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, paths);MethodInterceptor proxyHandler = new MapperProxyHandler();Enhancer enhancer = new Enhancer();enhancer.setSuperclass(Object.class); // 設置超類;此時UserDao只是一個Interface,沒有任何具體的實現類,因此超類設置為默認的Object;enhancer.setInterfaces(new Class[]{UserDao.class});enhancer.setCallback(proxyHandler);UserDao userDao = (UserDao)enhancer.create(); // 創建代理類String findById = userDao.findById("4");System.out.println(findById); //打印sqlString update = userDao.update(new Object(), "4");System.out.println(update);}@Testpublic void demo5(){System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, paths);MethodInterceptor proxyHandler = new FeignProxyHandler();Enhancer enhancer = new Enhancer();enhancer.setSuperclass(Object.class); // 設置超類;此時MyFeign只是一個Interface,沒有任何具體的實現類,因此超類設置為默認的Object;enhancer.setInterfaces(new Class[]{MyFeign.class});enhancer.setCallback(proxyHandler);MyFeign feign = (MyFeign)enhancer.create(); // 創建代理類ResponseEntity<Void> aq = feign.aq();System.out.println(JSONObject.toJSONString(aq));String baidu = feign.baidu("csdn");System.out.println(baidu);}@Testpublic void demo6() {MyInterceptor interceptor = new MyInterceptor(){@Overridepublic Object doInterceptor(Point point ) throws Throwable {try {System.out.println("befor");Object resp = point.postHandle(); //在此處,才真正執行被代理類的方法 todo ⑤;//本應該在MethodInterceptor子類中執行的代碼,被轉移到MyInterceptor子類了;MethodInterceptor子類只能有一個,但是MyInterceptor子類,結合責任鏈設計模式,卻可以有很多個!System.out.println("success");return resp;} catch ( Throwable e ) {System.out.println("error");throw e;} finally {System.out.println("finally");}}};MethodInterceptor proxyHandler = new MyProxyHandler(interceptor);Enhancer enhancer = new Enhancer();enhancer.setSuperclass(UserService.class); // 設置超類,cglib是通過繼承來實現的enhancer.setCallback(proxyHandler);IUserService userService = (IUserService)enhancer.create(); // 創建代理類userService.findById("4");userService.update(new Object(), "4");}}interface IUserService {void findById(String uid);void update(Object user, String uid);
}class UserService implements IUserService {public void findById(String uid) {System.out.println("查詢 findById");}public void update(Object user, String uid) {System.out.println("更新 update");}
}class UserServiceProxy implements IUserService {private final IUserService target; // 被代理的對象public UserServiceProxy() {this.target = new UserService(); //被代理對象,是在代理類中生成}@Overridepublic void findById(String uid) {try {before();target.findById(uid); // 這里才實際調用真實對象的方法after();} catch ( Exception ex ) {exception(ex);throw ex;}}@Overridepublic void update(Object user, String uid) {try {before();target.update(user, uid); // 這里才實際調用真實對象的方法after();} catch ( Exception ex ) {exception(ex);throw ex;}}private void before() { // 在執行方法之前執行System.out.println(String.format("log start time [%s] ", new Date()));System.out.println("開啟事務");}private void after() { // 在執行方法之后執行System.out.println(String.format("log end time [%s] ", new Date()));System.out.println("提交事務");}private void exception(Exception ex){System.out.println(String.format("log error [%s] ", ex.getMessage()));System.out.println("提交回滾");}
}class JdkProxyHandler implements InvocationHandler {private final Object target; // 被代理的對象,實際的方法執行者public JdkProxyHandler(Object target) {this.target = target;}/*** @param proxy 代理對象實例 todo ①* @param method 被代理類的Interface中的方法; todo ②* @param args * */@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {before();//反射調用 target 的 method 方法。//proxy是代理對象實例,因此在反射調用的時候,需要替換成被代理類target對象;Object result = method.invoke(target, args);after();return result; // 返回方法的執行結果} catch ( Exception ex ) {exception(ex);throw ex;}}private void before() { // 在執行方法之前執行System.out.println(String.format("log start time [%s] ", new Date()));System.out.println("開啟事務");}private void after() { // 在執行方法之后執行System.out.println(String.format("log end time [%s] ", new Date()));System.out.println("提交事務");}private void exception(Exception ex){System.out.println(String.format("log error [%s] ", ex.getMessage()));System.out.println("提交回滾");}
}class CglibProxyHandler implements MethodInterceptor {/*** @param target 代理子類對象;* @param method 被代理類的方法;* @param args * @param methodProxy 被代理的句柄方法 todo ③* */@Overridepublic Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {try {before();//注意這里是調用invokeSuper而不是invoke,否則死循環。 todo ③//method是被代理的方法,但是由于tagert是代理子類,執行method.invoke,實際上是表示執行代理子類的方法,代理子類又會繼續執行MethodInterceptor.intercept方法,導致又回到此處代碼,造成死循環;//此處應該直接執行invokeSuper,表示直接調用被代理類的句柄方法;Object result = methodProxy.invokeSuper(target, args);after();return result; // 返回方法的執行結果} catch ( Exception ex ) {exception(ex);throw ex;}}private void before() { // 在執行方法之前執行System.out.println(String.format("log start time [%s] ", new Date()));System.out.println("開啟事務");}private void after() { // 在執行方法之后執行System.out.println(String.format("log end time [%s] ", new Date()));System.out.println("提交事務");}private void exception(Exception ex){System.out.println(String.format("log error [%s] ", ex.getMessage()));System.out.println("提交回滾");}
}interface MyProxyInterceptor {boolean preHandler();void before();void after();void exception(Exception ex);
}class CglibProxyHandler1 implements MethodInterceptor {private final List<MyProxyInterceptor> interceptors; // 100public CglibProxyHandler1(List<MyProxyInterceptor> interceptors) {this.interceptors = interceptors;}@Overridepublic Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {//2List<MyProxyInterceptor> interceptors = this.interceptors.stream().filter(interceptor -> interceptor.preHandler()).collect(Collectors.toList());try {interceptors.forEach(intercept -> {intercept.before();});Object result = methodProxy.invokeSuper(target, args);interceptors.forEach(intercept -> {intercept.after();});return result; // 返回方法的執行結果} catch ( Exception ex ) {interceptors.forEach(intercept -> {intercept.exception(ex);});throw ex;}}
}interface UserDao {String findById(String uid);String update(Object user, String uid);
}class MapperProxyHandler implements MethodInterceptor {private static Map<String, Map<String, String>> mapperMap = new HashMap<>();static {//初始化,模擬Mapper.xml文件Map<String, String> mapper = new HashMap<>();//Map的key,及為UserDao中的方法名,以及Mapper.xml的標簽idmapper.put("findById", "select * from user where id = ?");mapper.put("update", "update user set name = ? where id = ?");//UserDao.class.getName(),對應到Mapper.xml的namespacemapperMap.put(UserDao.class.getName(), mapper);}@Overridepublic Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {String methodName = method.getName();Class<?> mapperClass = method.getDeclaringClass();Map<String, String> mapper = mapperMap.get(mapperClass.getName());if( mapper != null ){return mapper.get(methodName);}return null;}
}interface MyFeign {@RequestMapping(value = "http://127.0.0.1:8080/aq", method = RequestMethod.POST)ResponseEntity<Void> aq();@RequestMapping(value = "https://www.baidu.com/s", method = RequestMethod.GET)String baidu(@RequestParam("wd") String keyword);}class FeignProxyHandler implements MethodInterceptor {/*** target:代理子類* method:被代理類方法*/@Overridepublic Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {//method是被代理類中的方法,因此可以通過method獲取到:方法上注解;入參列表;入參上的注解;方法的返回類型RequestMapping requestMapping = AnnotationUtils.findAnnotation(method, RequestMapping.class);RequestMethod[] requestMethods = requestMapping.method();String[] paths = requestMapping.value();Type returnType = method.getGenericReturnType();Parameter[] parameters = method.getParameters();Map<String, Integer> argsIndex = new HashMap<>();for ( int idx = 0; idx < parameters.length; idx++ ) {Parameter parameter = parameters[idx];RequestParam requestParam = AnnotationUtils.findAnnotation(parameter, RequestParam.class);argsIndex.put(requestParam.name(), idx);}Map<String, Object> reqMap = new HashMap<>();argsIndex.forEach((pname, idx) -> {reqMap.put(pname, args[idx]);});RestTemplate rest = new RestTemplate();if( requestMethods[0] == RequestMethod.GET ){String resp = rest.getForObject(paths[0], String.class, reqMap);return resultType(resp, returnType);} else if (requestMethods[0] == RequestMethod.POST ){String resp = rest.postForObject(paths[0], reqMap, String.class);return resultType(resp,returnType);}return null;}private <R> R resultType(String resp, Type type){if( resp == null ){return null;}if( type instanceof Class ){if( String.class.isAssignableFrom((Class) type) ){return (R) resp;}}return JSONObject.parseObject(resp, type);}
}class Point {private final Object target;private final Method method;private final Object[] args;private final MethodProxy methodProxy;public Point(Object target, Method method, Object[] args, MethodProxy methodProxy) {this.target = target;this.method = method;this.args = args;this.methodProxy = methodProxy;}public Object postHandle() throws Throwable{Object result = methodProxy.invokeSuper(target, args);return result;}
}//攔截器接口
interface MyInterceptor {default Object doInterceptor(Point point) throws Throwable {return point.postHandle();}
}class MyProxyHandler implements MethodInterceptor {private final MyInterceptor interceptor;public MyProxyHandler(MyInterceptor interceptor) {this.interceptor = interceptor;}@Overridepublic Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {//注意,本應該在此處執行的[Object result = methodProxy.invokeSuper(target, args);],被轉移到Point#postHandle方法內了 todo ⑤Point point = new Point(target, method, args, methodProxy);return interceptor.doInterceptor(point);}
}