解析動態代理jdk的Proxy與spring的CGlib(包括區別介紹)
1. 為什麼要使用動態代理?
動態代理:在不改變原有代碼的情況下上進行對象功能增強 使用代理對象代替原來的對象完成功能 進而達到拓展功能的目的
2.JDK Proxy 動態代理面向接口的動態代理
特點:
- 一定要有接口和實現類的存在 代理對象增強的是實現類 在實現接口的方法重寫的方法
- 生成的代理對象隻能轉換成 接口的不能轉換成 被代理類
- 代理對象隻能增強接口中定義的方法 實現類中其他和接口無關的方法是無法增強的
- 代理對象隻能讀取到接口中方法上的註解 不能讀取到實現類方法上的註解
- 使用方法:
public class Test1 { public static void main(String[] args) { Dinner dinner=new Person("張三"); // 通過Porxy動態代理獲得一個代理對象,在代理對象中,對某個方法進行增強 // ClassLoader loader,被代理的對象的類加載器 ClassLoader classLoader = dinner.getClass().getClassLoader(); // Class<?>[] interfaces,被代理對象所實現的所有接口 Class[] interaces= dinner.getClass().getInterfaces(); // InvocationHandler h,執行處理器對象,專門用於定義增強的規則 InvocationHandler handler = new InvocationHandler(){ // invoke 當我們讓代理對象調用任何方法時,都會觸發invoke方法的執行 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // Object proxy, 代理對象 // Method method,被代理的方法 // Object[] args,被代理方法運行時的實參 Object res=null; if(method.getName().equals("eat")){ System.out.println("飯前洗手"); // 讓原有的eat的方法去運行 res =method.invoke(dinner, args); System.out.println("飯後刷碗"); }else{ // 如果是其他方法,那麼正常執行就可以瞭 res =method.invoke(dinner, args); } return res; } }; Dinner dinnerProxy =(Dinner) Proxy.newProxyInstance(classLoader,interaces,handler); //dinnerProxy.eat("包子"); dinnerProxy.drink(); } } interface Dinner{ void eat(String foodName); void drink(); } class Person implements Dinner{ private String name; public Person(String name) { this.name = name; } @Override public void eat(String foodName) { System.out.println(name+"正在吃"+foodName); } @Override public void drink( ) { System.out.println(name+"正在喝茶"); } } class Student implements Dinner{ private String name; public Student(String name) { this.name = name; } @Override public void eat(String foodName) { System.out.println(name+"正在食堂吃"+foodName); } @Override public void drink( ) { System.out.println(name+"正在喝可樂"); } }
3.CGlib動態代理
cglib動態代理模式是面向父類
特點:
面向父類的和接口沒有直接關系
2.不僅可以增強接口中定義的方法還可以增強其他方法
3.可以讀取父類中方法上的所有註解
使用實例
public class Test1 { @Test public void testCglib(){ Person person =new Person(); // 獲取一個Person的代理對象 // 1 獲得一個Enhancer對象 Enhancer enhancer=new Enhancer(); // 2 設置父類字節碼 enhancer.setSuperclass(person.getClass()); // 3 獲取MethodIntercepter對象 用於定義增強規則 MethodInterceptor methodInterceptor=new MethodInterceptor() { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { /*Object o, 生成之後的代理對象 personProxy Method method, 父類中原本要執行的方法 Person>>> eat() Object[] objects, 方法在調用時傳入的實參數組 MethodProxy methodProxy 子類中重寫父類的方法 personProxy >>> eat() */ Object res =null; if(method.getName().equals("eat")){ // 如果是eat方法 則增強並運行 System.out.println("飯前洗手"); res=methodProxy.invokeSuper(o,objects); System.out.println("飯後刷碗"); }else{ // 如果是其他方法 不增強運行 res=methodProxy.invokeSuper(o,objects); // 子類對象方法在執行,默認會調用父類對應被重寫的方法 } return res; } }; // 4 設置methodInterceptor enhancer.setCallback(methodInterceptor); // 5 獲得代理對象 Person personProxy = (Person)enhancer.create(); // 6 使用代理對象完成功能 personProxy.eat("包子"); } } class Person { public Person( ) { } public void eat(String foodName) { System.out.println("張三正在吃"+foodName); } }
原理區別:
java動態代理是利用反射機制生成一個實現代理接口的匿名類,在調用具體方法前調用InvokeHandler來處理。
而cglib動態代理是利用asm開源包,對代理對象類的class文件加載進來,通過修改其字節碼生成子類來處理。
1、如果目標對象實現瞭接口,默認情況下會采用JDK的動態代理實現AOP
2、如果目標對象實現瞭接口,可以強制使用CGLIB實現AOP
3、如果目標對象沒有實現瞭接口,必須采用CGLIB庫,spring會自動在JDK動態代理和CGLIB之間轉換
如何強制使用CGLIB實現AOP?
(1)添加CGLIB庫,SPRING_HOME/cglib/*.jar
(2)在spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class="true"/>
JDK動態代理和CGLIB字節碼生成的區別?
(1)JDK動態代理隻能對實現瞭接口的類生成代理,而不能針對類
(2)CGLIB是針對類實現代理,主要是對指定的類生成一個子類,覆蓋其中的方法
因為是繼承,所以該類或方法最好不要聲明成final
兩個動態代理的區別
- JDK動態代理是面向接口的,隻能增強實現類中接口中存在的方法。CGlib是面向父類的,可以增強父類的所有方法
- JDK得到的對象是JDK代理對象實例,而CGlib得到的對象是被代理對象的子類 生活雖然苦悶,但跑起來總是帶風!
到此這篇關於解析動態代理jdk的Proxy與spring的CGlib的文章就介紹到這瞭,更多相關動態代理jdk的Proxy與spring的CGlib內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- 帶你深入瞭解java-代理機制
- Spring AOP的底層實現方式-代理模式
- Java 動態代理的多種實現方式
- Java基礎之動態代理Cglib詳解
- java.lang.OutOfMemoryError: Metaspace異常解決的方法