CGLIB代理的使用與原理解析
CGLIB概述
Cglib代理
靜態(tài)代理和JDK 代理模式都要求目標(biāo)對(duì)象是實(shí)現(xiàn)一個(gè)接口,但是有時(shí)候目標(biāo)對(duì)象只是一個(gè)單獨(dú)的對(duì)象,并沒有實(shí)現(xiàn)任何的接口,這個(gè)時(shí)候可使用目標(biāo)對(duì)象子類來實(shí)現(xiàn)代理-這就是Cglib 代理。
JDK中提供的生成動(dòng)態(tài)代理類的機(jī)制有個(gè)鮮明的特點(diǎn)是:
- 某個(gè)類必須有實(shí)現(xiàn)的接口
- 生成的代理類也只能代理某個(gè)類接口定義的方法。
那么如果一個(gè)類沒有實(shí)現(xiàn)接口怎么辦呢?
這就有CGLIB的誕生了,前面說的JDK的動(dòng)態(tài)代理的實(shí)現(xiàn)方式是實(shí)現(xiàn)相關(guān)的接口成為接口的實(shí)現(xiàn)類,那么我們自然可以想到用繼承的方式實(shí)現(xiàn)相關(guān)的代理類。
Cglib 代理也叫作子類代理,它是在內(nèi)存中構(gòu)建一個(gè)子類對(duì)象從而實(shí)現(xiàn)對(duì)目標(biāo)對(duì)象功能擴(kuò)展, 有些書也將Cglib 代理歸屬到動(dòng)態(tài)代理。
CGLIB是針對(duì)類實(shí)現(xiàn)代理,主要是對(duì)指定的類生成一個(gè)子類,覆蓋其中的方法。因?yàn)槭抢^承,所以該類或方法最好不要聲明成final, static方法,private方法,final方法是不能被代理的
Cglib 是一個(gè)強(qiáng)大的高性能的代碼生成包,它可以在運(yùn)行期擴(kuò)展java 類與實(shí)現(xiàn)java 接口.它廣泛的被許多AOP 的框架使用,例如Spring AOP,實(shí)現(xiàn)方法攔截。
在AOP 編程中如何選擇代理模式:
- 目標(biāo)對(duì)象需要實(shí)現(xiàn)接口,用JDK 代理
- 目標(biāo)對(duì)象不需要實(shí)現(xiàn)接口,用Cglib 代理
Cglib 包的底層是通過使用字節(jié)碼處理框架ASM 來轉(zhuǎn)換字節(jié)碼并生成新的類
應(yīng)用案例
pom依賴
<!-- https://mvnrepository.com/artifact/cglib/cglib --> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.2.5</version> </dependency>
在JDK動(dòng)態(tài)代理的代碼基礎(chǔ)上進(jìn)行修改
① 測(cè)試客戶端
import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; public class TestCglibProxy { public static void main(String[] args) { //創(chuàng)建一個(gè)被代理類的對(duì)象 SuperMan man = new SuperMan(); CGLibProxy cgLibProxy = new CGLibProxy(); //返回一個(gè)代理類的對(duì)象--注意這里現(xiàn)在傳入的是實(shí)現(xiàn)類 Object obj = cgLibProxy.getProxyInstance(man); System.out.println(obj.getClass()); //class com.web.test.SuperMan$$EnhancerByCGLIB$$3be74240 Human hu = (Human)obj; //通過代理類的對(duì)象調(diào)用重寫的抽象方法 hu.info(); System.out.println(); hu.fly(); } }
② 自定義CGLibProxy
class CGLibProxy implements MethodInterceptor { // CGLib需要代理的目標(biāo)對(duì)象 private Object targetObject; public Object getProxyInstance(Object obj) { this.targetObject = obj; //1. 創(chuàng)建一個(gè)工具類 Enhancer enhancer = new Enhancer(); // 2.設(shè)置父類--可以是類或者接口 enhancer.setSuperclass(obj.getClass()); //3. 設(shè)置回調(diào)函數(shù) enhancer.setCallback(this); //4. 創(chuàng)建子類對(duì)象,即代理對(duì)象 Object proxyObj = enhancer.create(); // 返回代理對(duì)象 return proxyObj; } public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { Object obj = null; //模擬功能增強(qiáng) HumanUtil humanUtil = new HumanUtil(); humanUtil.method1(); // 執(zhí)行目標(biāo)目標(biāo)對(duì)象方法 obj = method.invoke(targetObject, args); //模擬功能增強(qiáng) humanUtil.method2(); return obj; } }
HumanUtil 如下:
class HumanUtil { public void method1() { System.out.println("=======方法一======="); } public void method2() { System.out.println("=======方法二======="); } }
測(cè)試結(jié)果
如下所示:
class com.web.test.SuperMan$$EnhancerByCGLIB$$3be74240
=======方法一=======
我是超人!我怕誰!
=======方法二=======
=======方法一=======
I believe I can fly!
=======方法二=======
獲取代理類源碼
有了源碼才好分析驗(yàn)證cglib生成的代理類究竟是個(gè)什么樣子?這里主要用到下面代碼:
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, path);
修改上面的main方法如下:
public static void main(String[] args) { SuperMan man = new SuperMan();//創(chuàng)建一個(gè)被代理類的對(duì)象 // 添加如下代碼,獲取代理類源文件 String path = CGLibProxy.class.getResource(".").getPath(); System.out.println(path); System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, path); CGLibProxy cgLibProxy = new CGLibProxy(); Object obj = cgLibProxy.bind(man);//返回一個(gè)代理類的對(duì)象 System.out.println(obj.getClass()); //class com.web.test.SuperMan$$EnhancerByCGLIB$$3be74240 Human hu = (Human)obj; hu.info();//通過代理類的對(duì)象調(diào)用重寫的抽象方法 System.out.println(); hu.fly(); }
測(cè)試結(jié)果如下:
生成的代理類名字 SuperMan$$EnhancerByCGLIB$$3be74240 ,源碼如下:
可以直接在//javare.cn/網(wǎng)站下在線反編譯。
package com.web.test; import com.web.test.SuperMan; import java.lang.reflect.Method; import net.sf.cglib.core.ReflectUtils; import net.sf.cglib.core.Signature; import net.sf.cglib.proxy.Callback; import net.sf.cglib.proxy.Factory; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; //繼承目標(biāo)被代理類 public class SuperMan$$EnhancerByCGLIB$$3be74240 extends SuperMan implements Factory { private boolean CGLIB$BOUND; public static Object CGLIB$FACTORY_DATA; private static final ThreadLocal CGLIB$THREAD_CALLBACKS; private static final Callback[] CGLIB$STATIC_CALLBACKS; private MethodInterceptor CGLIB$CALLBACK_0; private static Object CGLIB$CALLBACK_FILTER; private static final Method CGLIB$info$0$Method; private static final MethodProxy CGLIB$info$0$Proxy; private static final Object[] CGLIB$emptyArgs; private static final Method CGLIB$fly$1$Method; private static final MethodProxy CGLIB$fly$1$Proxy; private static final Method CGLIB$equals$2$Method; private static final MethodProxy CGLIB$equals$2$Proxy; private static final Method CGLIB$toString$3$Method; private static final MethodProxy CGLIB$toString$3$Proxy; private static final Method CGLIB$hashCode$4$Method; private static final MethodProxy CGLIB$hashCode$4$Proxy; private static final Method CGLIB$clone$5$Method; private static final MethodProxy CGLIB$clone$5$Proxy; // 一系列私有靜態(tài)常量定義 // 常量初始化 static void CGLIB$STATICHOOK1() { CGLIB$THREAD_CALLBACKS = new ThreadLocal(); CGLIB$emptyArgs = new Object[0]; Class var0 = Class.forName("com.web.test.SuperMan$$EnhancerByCGLIB$$3be74240"); Class var1; Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods()); CGLIB$equals$2$Method = var10000[0]; CGLIB$equals$2$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2"); CGLIB$toString$3$Method = var10000[1]; CGLIB$toString$3$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$3"); CGLIB$hashCode$4$Method = var10000[2]; CGLIB$hashCode$4$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$4"); CGLIB$clone$5$Method = var10000[3]; CGLIB$clone$5$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5"); var10000 = ReflectUtils.findMethods(new String[]{"info", "()V", "fly", "()V"}, (var1 = Class.forName("com.web.test.SuperMan")).getDeclaredMethods()); CGLIB$info$0$Method = var10000[0]; CGLIB$info$0$Proxy = MethodProxy.create(var1, var0, "()V", "info", "CGLIB$info$0"); CGLIB$fly$1$Method = var10000[1]; CGLIB$fly$1$Proxy = MethodProxy.create(var1, var0, "()V", "fly", "CGLIB$fly$1"); } //綁定MethodInterceptor callback的方法會(huì)額外實(shí)現(xiàn)一個(gè)和原方法一模一樣的方法 final void CGLIB$info$0() { super.info(); } // 代理對(duì)象的方法調(diào)用將會(huì)轉(zhuǎn)發(fā)到代理對(duì)象的intercept方法 public final void info() { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if(this.CGLIB$CALLBACK_0 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } if(var10000 != null) { var10000.intercept(this, CGLIB$info$0$Method, CGLIB$emptyArgs, CGLIB$info$0$Proxy); } else { super.info(); } } final void CGLIB$fly$1() { super.fly(); } public final void fly() { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if(this.CGLIB$CALLBACK_0 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } if(var10000 != null) { var10000.intercept(this, CGLIB$fly$1$Method, CGLIB$emptyArgs, CGLIB$fly$1$Proxy); } else { super.fly(); } } final boolean CGLIB$equals$2(Object var1) { return super.equals(var1); } public final boolean equals(Object var1) { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if(this.CGLIB$CALLBACK_0 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } if(var10000 != null) { Object var2 = var10000.intercept(this, CGLIB$equals$2$Method, new Object[]{var1}, CGLIB$equals$2$Proxy); return var2 == null?false:((Boolean)var2).booleanValue(); } else { return super.equals(var1); } } final String CGLIB$toString$3() { return super.toString(); } public final String toString() { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if(this.CGLIB$CALLBACK_0 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } return var10000 != null?(String)var10000.intercept(this, CGLIB$toString$3$Method, CGLIB$emptyArgs, CGLIB$toString$3$Proxy):super.toString(); } final int CGLIB$hashCode$4() { return super.hashCode(); } public final int hashCode() { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if(this.CGLIB$CALLBACK_0 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } if(var10000 != null) { Object var1 = var10000.intercept(this, CGLIB$hashCode$4$Method, CGLIB$emptyArgs, CGLIB$hashCode$4$Proxy); return var1 == null?0:((Number)var1).intValue(); } else { return super.hashCode(); } } final Object CGLIB$clone$5() throws CloneNotSupportedException { return super.clone(); } protected final Object clone() throws CloneNotSupportedException { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if(this.CGLIB$CALLBACK_0 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } return var10000 != null?var10000.intercept(this, CGLIB$clone$5$Method, CGLIB$emptyArgs, CGLIB$clone$5$Proxy):super.clone(); } // 獲取方法的 MethodProxy public static MethodProxy CGLIB$findMethodProxy(Signature var0) { String var10000 = var0.toString(); switch(var10000.hashCode()) { case -1271409118: if(var10000.equals("fly()V")) { return CGLIB$fly$1$Proxy; } break; case -508378822: if(var10000.equals("clone()Ljava/lang/Object;")) { return CGLIB$clone$5$Proxy; } break; case 1826985398: if(var10000.equals("equals(Ljava/lang/Object;)Z")) { return CGLIB$equals$2$Proxy; } break; case 1913648695: if(var10000.equals("toString()Ljava/lang/String;")) { return CGLIB$toString$3$Proxy; } break; case 1945358343: if(var10000.equals("info()V")) { return CGLIB$info$0$Proxy; } break; case 1984935277: if(var10000.equals("hashCode()I")) { return CGLIB$hashCode$4$Proxy; } } return null; } //無參構(gòu)造器 public SuperMan$$EnhancerByCGLIB$$3be74240() { CGLIB$BIND_CALLBACKS(this); } public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] var0) { CGLIB$THREAD_CALLBACKS.set(var0); } public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] var0) { CGLIB$STATIC_CALLBACKS = var0; } private static final void CGLIB$BIND_CALLBACKS(Object var0) { SuperMan$$EnhancerByCGLIB$$3be74240 var1 = (SuperMan$$EnhancerByCGLIB$$3be74240)var0; if(!var1.CGLIB$BOUND) { var1.CGLIB$BOUND = true; Object var10000 = CGLIB$THREAD_CALLBACKS.get(); if(var10000 == null) { var10000 = CGLIB$STATIC_CALLBACKS; if(CGLIB$STATIC_CALLBACKS == null) { return; } } var1.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])var10000)[0]; } } public Object newInstance(Callback[] var1) { CGLIB$SET_THREAD_CALLBACKS(var1); SuperMan$$EnhancerByCGLIB$$3be74240 var10000 = new SuperMan$$EnhancerByCGLIB$$3be74240(); CGLIB$SET_THREAD_CALLBACKS((Callback[])null); return var10000; } public Object newInstance(Callback var1) { CGLIB$SET_THREAD_CALLBACKS(new Callback[]{var1}); SuperMan$$EnhancerByCGLIB$$3be74240 var10000 = new SuperMan$$EnhancerByCGLIB$$3be74240(); CGLIB$SET_THREAD_CALLBACKS((Callback[])null); return var10000; } public Object newInstance(Class[] var1, Object[] var2, Callback[] var3) { CGLIB$SET_THREAD_CALLBACKS(var3); SuperMan$$EnhancerByCGLIB$$3be74240 var10000 = new SuperMan$$EnhancerByCGLIB$$3be74240; switch(var1.length) { case 0: var10000.<init>(); CGLIB$SET_THREAD_CALLBACKS((Callback[])null); return var10000; default: throw new IllegalArgumentException("Constructor not found"); } } public Callback getCallback(int var1) { CGLIB$BIND_CALLBACKS(this); MethodInterceptor var10000; switch(var1) { case 0: var10000 = this.CGLIB$CALLBACK_0; break; default: var10000 = null; } return var10000; } public void setCallback(int var1, Callback var2) { switch(var1) { case 0: this.CGLIB$CALLBACK_0 = (MethodInterceptor)var2; default: } } public Callback[] getCallbacks() { CGLIB$BIND_CALLBACKS(this); return new Callback[]{this.CGLIB$CALLBACK_0}; } // 初始化定義的callback public void setCallbacks(Callback[] var1) { this.CGLIB$CALLBACK_0 = (MethodInterceptor)var1[0]; } // 這里,類加載的時(shí)候首先執(zhí)行?。。? static { CGLIB$STATICHOOK1(); } }
可以發(fā)現(xiàn)這個(gè)類繼承自接口實(shí)現(xiàn)類–SuperMan,其在加載的時(shí)候會(huì)先進(jìn)行一系列靜態(tài)常量的初始化且在實(shí)例化的時(shí)候調(diào)用 CGLIB$BIND_CALLBACKS(this); 進(jìn)行call back的綁定。
那么現(xiàn)在的情況就是我們的生成了一個(gè)代理類,這個(gè)代理類是我們需要代理的實(shí)現(xiàn)類的繼承類。我們的被代理類的方法在這個(gè)代理類中幫我們重寫了,并且全部變成了final的。同時(shí)覆蓋了一些Object類中的方法。
以 info 這個(gè)方法舉例,方法中會(huì)調(diào)用 MethodInterceptor 類中的 intercept 方法(也就是我們實(shí)現(xiàn)的邏輯的地方),同時(shí)把自己的Method對(duì)象,參數(shù)列表等傳入進(jìn)去。
兩個(gè)小問題
如果代理的目標(biāo)對(duì)象為接口行不行?·
接口中的方法代理類實(shí)現(xiàn)了,那么類中自定義的方法代理類是否也可以實(shí)現(xiàn)?
毫無疑問,cglib是基于類的動(dòng)態(tài)代理,代理類繼承自目標(biāo)類, 類中的方法除了final自然可以繼承 !
傳入接口為代理對(duì)象進(jìn)行測(cè)試
public class TestCglibProxy2 { public static void main(String[] args) { String path = CGLibProxy.class.getResource(".").getPath(); System.out.println(path); System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, path); CGLibProxy2 cgLibProxy = new CGLibProxy2(); //返回一個(gè)代理類的對(duì)象--這里直接傳入Human接口 Class!!! Object obj = cgLibProxy.getProxyInstance(Human.class); System.out.println(obj.getClass()); //class com.web.test.Human$$EnhancerByCGLIB$$9fc9106 Human hu = (Human)obj; hu.info();//通過代理類的對(duì)象調(diào)用重寫的抽象方法 System.out.println(); hu.fly(); } } class CGLibProxy2 implements MethodInterceptor { public Object getProxyInstance(Class<?> obj) { Enhancer enhancer = new Enhancer(); // 這里傳入Class enhancer.setSuperclass(obj); enhancer.setCallback(this); Object proxyObj = enhancer.create(); return proxyObj;// 返回代理對(duì)象 } public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { Object obj = null; //模擬功能增強(qiáng) HumanUtil humanUtil = new HumanUtil(); humanUtil.method1(); // 執(zhí)行目標(biāo)目標(biāo)對(duì)象方法--這里直接傳入目標(biāo)對(duì)象 obj = method.invoke(new SuperMan(), args); //模擬功能增強(qiáng) humanUtil.method2(); return obj; } }
測(cè)試結(jié)果如下圖:
此時(shí)生成的源碼 Human$$EnhancerByCGLIB$$9fc9106 如下所示:
package com.web.test; import com.web.test.Human; import java.lang.reflect.Method; import net.sf.cglib.core.ReflectUtils; import net.sf.cglib.core.Signature; import net.sf.cglib.proxy.Callback; import net.sf.cglib.proxy.Factory; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; //注意,這里變?yōu)榱藢?shí)現(xiàn)Human接口形式 public class Human$$EnhancerByCGLIB$$9fc9106 implements Human, Factory { private boolean CGLIB$BOUND; public static Object CGLIB$FACTORY_DATA; private static final ThreadLocal CGLIB$THREAD_CALLBACKS; private static final Callback[] CGLIB$STATIC_CALLBACKS; private MethodInterceptor CGLIB$CALLBACK_0; private static Object CGLIB$CALLBACK_FILTER; private static final Method CGLIB$equals$0$Method; private static final MethodProxy CGLIB$equals$0$Proxy; private static final Object[] CGLIB$emptyArgs; private static final Method CGLIB$toString$1$Method; private static final MethodProxy CGLIB$toString$1$Proxy; private static final Method CGLIB$hashCode$2$Method; private static final MethodProxy CGLIB$hashCode$2$Proxy; private static final Method CGLIB$clone$3$Method; private static final MethodProxy CGLIB$clone$3$Proxy; private static final Method CGLIB$info$4$Method; private static final MethodProxy CGLIB$info$4$Proxy; private static final Method CGLIB$fly$5$Method; private static final MethodProxy CGLIB$fly$5$Proxy; static void CGLIB$STATICHOOK1() { CGLIB$THREAD_CALLBACKS = new ThreadLocal(); CGLIB$emptyArgs = new Object[0]; Class var0 = Class.forName("com.web.test.Human$$EnhancerByCGLIB$$9fc9106"); Class var1; Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods()); CGLIB$equals$0$Method = var10000[0]; CGLIB$equals$0$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$0"); CGLIB$toString$1$Method = var10000[1]; CGLIB$toString$1$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$1"); CGLIB$hashCode$2$Method = var10000[2]; CGLIB$hashCode$2$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$2"); CGLIB$clone$3$Method = var10000[3]; CGLIB$clone$3$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$3"); var10000 = ReflectUtils.findMethods(new String[]{"info", "()V", "fly", "()V"}, (var1 = Class.forName("com.web.test.Human")).getDeclaredMethods()); CGLIB$info$4$Method = var10000[0]; CGLIB$info$4$Proxy = MethodProxy.create(var1, var0, "()V", "info", "CGLIB$info$4"); CGLIB$fly$5$Method = var10000[1]; CGLIB$fly$5$Proxy = MethodProxy.create(var1, var0, "()V", "fly", "CGLIB$fly$5"); } final boolean CGLIB$equals$0(Object var1) { return super.equals(var1); } public final boolean equals(Object var1) { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if(this.CGLIB$CALLBACK_0 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } if(var10000 != null) { Object var2 = var10000.intercept(this, CGLIB$equals$0$Method, new Object[]{var1}, CGLIB$equals$0$Proxy); return var2 == null?false:((Boolean)var2).booleanValue(); } else { return super.equals(var1); } } final String CGLIB$toString$1() { return super.toString(); } public final String toString() { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if(this.CGLIB$CALLBACK_0 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } return var10000 != null?(String)var10000.intercept(this, CGLIB$toString$1$Method, CGLIB$emptyArgs, CGLIB$toString$1$Proxy):super.toString(); } final int CGLIB$hashCode$2() { return super.hashCode(); } public final int hashCode() { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if(this.CGLIB$CALLBACK_0 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } if(var10000 != null) { Object var1 = var10000.intercept(this, CGLIB$hashCode$2$Method, CGLIB$emptyArgs, CGLIB$hashCode$2$Proxy); return var1 == null?0:((Number)var1).intValue(); } else { return super.hashCode(); } } final Object CGLIB$clone$3() throws CloneNotSupportedException { return super.clone(); } protected final Object clone() throws CloneNotSupportedException { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if(this.CGLIB$CALLBACK_0 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } return var10000 != null?var10000.intercept(this, CGLIB$clone$3$Method, CGLIB$emptyArgs, CGLIB$clone$3$Proxy):super.clone(); } final void CGLIB$info$4() { super.info(); } public final void info() { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if(this.CGLIB$CALLBACK_0 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } if(var10000 != null) { var10000.intercept(this, CGLIB$info$4$Method, CGLIB$emptyArgs, CGLIB$info$4$Proxy); } else { super.info(); } } final void CGLIB$fly$5() { super.fly(); } public final void fly() { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if(this.CGLIB$CALLBACK_0 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } if(var10000 != null) { var10000.intercept(this, CGLIB$fly$5$Method, CGLIB$emptyArgs, CGLIB$fly$5$Proxy); } else { super.fly(); } } public static MethodProxy CGLIB$findMethodProxy(Signature var0) { String var10000 = var0.toString(); switch(var10000.hashCode()) { case -1271409118: if(var10000.equals("fly()V")) { return CGLIB$fly$5$Proxy; } break; case -508378822: if(var10000.equals("clone()Ljava/lang/Object;")) { return CGLIB$clone$3$Proxy; } break; case 1826985398: if(var10000.equals("equals(Ljava/lang/Object;)Z")) { return CGLIB$equals$0$Proxy; } break; case 1913648695: if(var10000.equals("toString()Ljava/lang/String;")) { return CGLIB$toString$1$Proxy; } break; case 1945358343: if(var10000.equals("info()V")) { return CGLIB$info$4$Proxy; } break; case 1984935277: if(var10000.equals("hashCode()I")) { return CGLIB$hashCode$2$Proxy; } } return null; } public Human$$EnhancerByCGLIB$$9fc9106() { CGLIB$BIND_CALLBACKS(this); } public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] var0) { CGLIB$THREAD_CALLBACKS.set(var0); } public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] var0) { CGLIB$STATIC_CALLBACKS = var0; } private static final void CGLIB$BIND_CALLBACKS(Object var0) { Human$$EnhancerByCGLIB$$9fc9106 var1 = (Human$$EnhancerByCGLIB$$9fc9106)var0; if(!var1.CGLIB$BOUND) { var1.CGLIB$BOUND = true; Object var10000 = CGLIB$THREAD_CALLBACKS.get(); if(var10000 == null) { var10000 = CGLIB$STATIC_CALLBACKS; if(CGLIB$STATIC_CALLBACKS == null) { return; } } var1.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])var10000)[0]; } } public Object newInstance(Callback[] var1) { CGLIB$SET_THREAD_CALLBACKS(var1); Human$$EnhancerByCGLIB$$9fc9106 var10000 = new Human$$EnhancerByCGLIB$$9fc9106(); CGLIB$SET_THREAD_CALLBACKS((Callback[])null); return var10000; } public Object newInstance(Callback var1) { CGLIB$SET_THREAD_CALLBACKS(new Callback[]{var1}); Human$$EnhancerByCGLIB$$9fc9106 var10000 = new Human$$EnhancerByCGLIB$$9fc9106(); CGLIB$SET_THREAD_CALLBACKS((Callback[])null); return var10000; } public Object newInstance(Class[] var1, Object[] var2, Callback[] var3) { CGLIB$SET_THREAD_CALLBACKS(var3); Human$$EnhancerByCGLIB$$9fc9106 var10000 = new Human$$EnhancerByCGLIB$$9fc9106; switch(var1.length) { case 0: var10000.<init>(); CGLIB$SET_THREAD_CALLBACKS((Callback[])null); return var10000; default: throw new IllegalArgumentException("Constructor not found"); } } public Callback getCallback(int var1) { CGLIB$BIND_CALLBACKS(this); MethodInterceptor var10000; switch(var1) { case 0: var10000 = this.CGLIB$CALLBACK_0; break; default: var10000 = null; } return var10000; } public void setCallback(int var1, Callback var2) { switch(var1) { case 0: this.CGLIB$CALLBACK_0 = (MethodInterceptor)var2; default: } } public Callback[] getCallbacks() { CGLIB$BIND_CALLBACKS(this); return new Callback[]{this.CGLIB$CALLBACK_0}; } public void setCallbacks(Callback[] var1) { this.CGLIB$CALLBACK_0 = (MethodInterceptor)var1[0]; } static { CGLIB$STATICHOOK1(); } }
對(duì)比和上面的 SuperMan$$EnhancerByCGLIB$$3be74240 發(fā)現(xiàn)并無差別(此時(shí)類中還無自定義方法)。
需要注意的是,這里只是測(cè)試綁定的代理目標(biāo)對(duì)象為接口的可能性,代理類實(shí)現(xiàn)類接口的方法,并將方法的調(diào)用轉(zhuǎn)發(fā)到intercept—具體業(yè)務(wù)邏輯實(shí)現(xiàn)。且在intercept中, obj = method.invoke(new SuperMan(), args); 將實(shí)際實(shí)現(xiàn)類寫死了。
實(shí)際實(shí)現(xiàn)類(SuperMan)中添加自定義方法
如下,修改SuperMan:
// 被代理類 class SuperMan implements Human { public void info() { System.out.println("我是超人!我怕誰!"); } public void fly() { System.out.println("I believe I can fly!"); } public void self(){ System.out.println("this is suman's method--self !"); } }
測(cè)試代碼如下–傳入實(shí)現(xiàn)類對(duì)象:
import java.lang.reflect.Method; import net.sf.cglib.core.DebuggingClassWriter; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; public class TestCglibProxy { public static void main(String[] args) { //創(chuàng)建一個(gè)被代理類的對(duì)象 SuperMan man = new SuperMan(); String path = CGLibProxy.class.getResource(".").getPath(); System.out.println(path); System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, path); CGLibProxy cgLibProxy = new CGLibProxy(); Object obj = cgLibProxy.getProxyInstance(man);//返回一個(gè)代理類的對(duì)象 System.out.println(obj.getClass()); //class com.web.test.SuperMan$$EnhancerByCGLIB$$3be74240 Suman su = (Suman)obj; su.info();//通過代理類的對(duì)象調(diào)用重寫的抽象方法 System.out.println(); // 注意,這里調(diào)用Suman自定義方法 su.self(); } } class CGLibProxy implements MethodInterceptor { private Object targetObject;// CGLib需要代理的目標(biāo)對(duì)象 public Object getProxyInstance(Object obj) { this.targetObject = obj; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(obj.getClass()); enhancer.setCallback(this); Object proxyObj = enhancer.create(); return proxyObj;// 返回代理對(duì)象 } public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { Object obj = null; //模擬功能增強(qiáng) HumanUtil humanUtil = new HumanUtil(); humanUtil.method1(); // 執(zhí)行目標(biāo)目標(biāo)對(duì)象方法 obj = method.invoke(targetObject, args); //模擬功能增強(qiáng) humanUtil.method2(); return obj; } }
測(cè)試結(jié)果如下圖:
毫無疑問是可以的,因?yàn)榇眍惱^承自目標(biāo)被代理類,故而添加的自定義方法可以被實(shí)現(xiàn)。因?yàn)镃GLIB是繼承自目標(biāo)類-SuperMan,而非實(shí)現(xiàn)目標(biāo)類的上層接口-Human!
此時(shí)生成的 SuperMan$$EnhancerByCGLIB$$3be74240.class 源碼如下:
//... 省略代碼,這里只表明方法 final void CGLIB$info$0() { super.info(); } public final void info() { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if(this.CGLIB$CALLBACK_0 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } if(var10000 != null) { var10000.intercept(this, CGLIB$info$0$Method, CGLIB$emptyArgs, CGLIB$info$0$Proxy); } else { super.info(); } } final void CGLIB$fly$1() { super.fly(); } public final void fly() { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if(this.CGLIB$CALLBACK_0 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } if(var10000 != null) { var10000.intercept(this, CGLIB$fly$1$Method, CGLIB$emptyArgs, CGLIB$fly$1$Proxy); } else { super.fly(); } } final void CGLIB$self$2() { super.self(); } public final void self() { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if(this.CGLIB$CALLBACK_0 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } if(var10000 != null) { var10000.intercept(this, CGLIB$self$2$Method, CGLIB$emptyArgs, CGLIB$self$2$Proxy); } else { super.self(); } }
此時(shí)如果使用①中的代碼–即 enhancer.setSuperclass(obj); 傳入Human.class,intercept中方法反射調(diào)用執(zhí)行Suman.self()是會(huì)拋異常的,且生成的代理類源碼中無self方法!
測(cè)試代碼如下:
public class TestCglibProxy2 { public static void main(String[] args) { String path = CGLibProxy.class.getResource(".").getPath(); System.out.println(path); System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, path); CGLibProxy2 cgLibProxy = new CGLibProxy2(); //這里傳入Human Object obj = cgLibProxy.getProxyInstance(Human.class);//返回一個(gè)代理類的對(duì)象 System.out.println(obj.getClass()); // 強(qiáng)轉(zhuǎn)可能會(huì)拋異常 SuperMan su = (SuperMan)obj; su.info(); System.out.println(); // 嘗試調(diào)用Suman私有方法 su.self(); } } class CGLibProxy2 implements MethodInterceptor { public Object getProxyInstance(Class<?> obj) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(obj); enhancer.setCallback(this); Object proxyObj = enhancer.create(); return proxyObj;// 返回代理對(duì)象 } public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { Object obj = null; //模擬功能增強(qiáng) HumanUtil humanUtil = new HumanUtil(); humanUtil.method1(); // 執(zhí)行目標(biāo)目標(biāo)對(duì)象方法 obj = method.invoke(new SuperMan(), args); //模擬功能增強(qiáng) humanUtil.method2(); return obj; } }
測(cè)試結(jié)果如下:
生成的代理類 Human$$EnhancerByCGLIB$$9fc9106.class并無Suman.self()—很顯然的事情?。?!
Cglib動(dòng)態(tài)代理總結(jié)
① CGlib可以傳入接口也可以傳入普通的類,接口使用實(shí)現(xiàn)的方式,普通類使用會(huì)使用繼承的方式生成代理類。
通常使用Cglib的時(shí)候側(cè)重于實(shí)際實(shí)現(xiàn)類??!
② 由于是繼承方式,如果是 static方法,private方法,final方法是不能被代理的。
③ CGLIB會(huì)默認(rèn)代理Object中equals,toString,hashCode,clone等方法。比JDK代理多了clone。
獲取JDK/Cglib動(dòng)態(tài)代理對(duì)象
至此可以獲取動(dòng)態(tài)代理的class 文件,那么如何在項(xiàng)目中獲取動(dòng)態(tài)代理的目標(biāo)對(duì)象呢?
示例代碼如下:
import java.lang.reflect.Field; import org.springframework.aop.framework.AdvisedSupport; import org.springframework.aop.framework.AopProxy; import org.springframework.aop.support.AopUtils; public class AopTargetUtils { /** * 獲取 目標(biāo)對(duì)象 * @param proxy 代理對(duì)象 * @return * @throws Exception */ public static Object getTarget(Object proxy) throws Exception { if(!AopUtils.isAopProxy(proxy)) { return proxy;//不是代理對(duì)象 } if(AopUtils.isJdkDynamicProxy(proxy)) { return getJdkDynamicProxyTargetObject(proxy); } else { //cglib return getCglibProxyTargetObject(proxy); } } private static Object getCglibProxyTargetObject(Object proxy) throws Exception { Field h = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0"); h.setAccessible(true); Object dynamicAdvisedInterceptor = h.get(proxy); Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised"); advised.setAccessible(true); Object target = ((AdvisedSupport)advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget(); return target; } private static Object getJdkDynamicProxyTargetObject(Object proxy) throws Exception { Field h = proxy.getClass().getSuperclass().getDeclaredField("h"); h.setAccessible(true); AopProxy aopProxy = (AopProxy) h.get(proxy); Field advised = aopProxy.getClass().getDeclaredField("advised"); advised.setAccessible(true); Object target = ((AdvisedSupport)advised.get(aopProxy)).getTargetSource().getTarget(); return target; } }
Spring中動(dòng)態(tài)代理的實(shí)現(xiàn)
Spring代理實(shí)際上是對(duì)JDK代理和CGLIB代理做了一層封裝,并且引入了AOP概念:Aspect、advice、joinpoint等等,同時(shí)引入了AspectJ中的一些注解@pointCut,@after,@before等等。Spring Aop嚴(yán)格的來說都是動(dòng)態(tài)代理。
Spring在選擇用JDK還是CGLiB的依據(jù):
- 當(dāng)Bean實(shí)現(xiàn)接口時(shí),Spring就會(huì)用JDK的動(dòng)態(tài)代理
- 當(dāng)Bean沒有實(shí)現(xiàn)接口時(shí),Spring使用CGlib是實(shí)現(xiàn)
如何強(qiáng)制使用CGLIB實(shí)現(xiàn)AOP?
- 添加CGLIB庫,SPRING_HOME/cglib/*.jar
- 可以強(qiáng)制使用CGlib(在spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>)
到此這篇關(guān)于CGLIB代理的使用與原理解析的文章就介紹到這了,更多相關(guān)CGLIB代理原理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Java中的cglib代理詳解
- Java中Cglib代理和JDK代理的區(qū)別詳解
- 一文搞懂Java常見的三種代理模式(靜態(tài)代理、動(dòng)態(tài)代理和cglib代理)
- Spring之AOP兩種代理機(jī)制對(duì)比分析(JDK和CGLib動(dòng)態(tài)代理)
- 基于jdk動(dòng)態(tài)代理和cglib動(dòng)態(tài)代理實(shí)現(xiàn)及區(qū)別說明
- Java JDK與cglib動(dòng)態(tài)代理有什么區(qū)別
- 解析動(dòng)態(tài)代理jdk的Proxy與spring的CGlib(包括區(qū)別介紹)
相關(guān)文章
解決使用ProcessBuilder踩到的坑及注意事項(xiàng)
這篇文章主要介紹了解決使用ProcessBuilder踩到的坑,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06Spring Cloud Admin健康檢查 郵件、釘釘群通知的實(shí)現(xiàn)
這篇文章主要介紹了Spring Cloud Admin健康檢查 郵件、釘釘群通知的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08springmvc項(xiàng)目使用@Valid+BindingResult遇到的問題
這篇文章主要介紹了springmvc項(xiàng)目使用@Valid+BindingResult遇到的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12vue 使用vuex在頁面跳轉(zhuǎn)的實(shí)現(xiàn)方式
這篇文章主要介紹了vue 使用vuex在頁面跳轉(zhuǎn)的實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08Spring?Boot?使用?Disruptor?做內(nèi)部高性能消息隊(duì)列
這篇文章主要介紹了Spring?Boot?使用?Disruptor?做內(nèi)部高性能消息隊(duì)列,工作中遇到項(xiàng)目使用Disruptor做消息隊(duì)列,對(duì)你沒看錯(cuò),不是Kafka,也不是rabbitmq。Disruptor有個(gè)最大的優(yōu)點(diǎn)就是快,還有一點(diǎn)它是開源的哦,下面做個(gè)簡(jiǎn)單的記錄2022-06-06JAVA實(shí)現(xiàn)經(jīng)典掃雷游戲的示例代碼
windows自帶的游戲《掃雷》是陪伴了無數(shù)人的經(jīng)典游戲,本程序參考《掃雷》的規(guī)則進(jìn)行了簡(jiǎn)化,用java語言實(shí)現(xiàn),采用了swing技術(shù)進(jìn)行了界面化處理。感興趣的可以學(xué)習(xí)一下2022-01-01Java實(shí)現(xiàn)FTP批量大文件上傳下載篇2
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)FTP批量大文件上傳下載的強(qiáng)化篇,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-08-08