CGLIB經常使用使用示例:html
目標類java
public class Todo { public void doString(String desc) { System.out.println("doString: " + desc); } }
代理生成類 編程
public class ProxyInstance implements MethodInterceptor { public <T> T getInstance(Class<T> klass) { Enhancer enhancer = new Enhancer(); /* * 設置父類型,用於生成代理的子類 */ enhancer.setSuperclass(klass); /* * 設置實現Callback接口的實例對象 * 這裏爲MethodInterceptor對象 */ enhancer.setCallback(this); /* * create()用於建立代理類對象 * createClass()用於生成java字節碼 */ return (T) enhancer.create(); } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("proxy before"); Object obj= methodProxy.invokeSuper(o, objects); System.out.println("proxy after"); return obj; } }
測試類app
public class Test { public static void main(String[] args) { String path = Test.class.getClassLoader().getResource("").getPath(); System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, path + "cglib"); Todo todo=new ProxyInstance().getInstance(Todo.class); todo.doString("------ nothing-------- "); } }
控制檯框架
CGLIB debugging enabled, writing to '/E:/ideaspace/demo/demo/target/classes/cglib' proxy before doString: ------ nothing-------- proxy after
ASM(assembly):彙編的縮寫,是java的一個字節碼級別的編程框架,它能夠動態生成Class的字節碼文件,CGLIB以及Groovy等都是基於ASM實現動態語言特性的,在CGLIB的代理中主要用於生成類文件,實現方式爲動態生成繼承了目標類的代理類。maven
maven依賴ide
<dependency> <groupId>org.ow2.asm</groupId> <artifactId>asm</artifactId> <version>8.0.1</version> </dependency>
針對示例的目標類,生成類過多這裏只展現主要代碼:注意全部生成的類都會加載到JVM中測試
// 代理類相關的生成類this
Todo$$EnhancerByCGLIB$$939b412c$$FastClassByCGLIB$$b6c5bc7f.class Todo$$EnhancerByCGLIB$$939b412c.class // 代理類 Todo$$FastClassByCGLIB$$9f944a58.class
//其餘類url
core/MethodWrapper$MethodWrapperKey$$KeyFactoryByCGLIB$$d45e49f7.class proxy/Enhancer$EnhancerKey$$KeyFactoryByCGLIB$$7fb24d72.class
代理類描述
// 經過繼承方式來實現代理,目標類成爲了父類supper。經過繼承實現也附帶了繼承的缺點,注意final類型和final方法的坑
public class Todo$$EnhancerByCGLIB$$939b412c extends Todo implements Factory {...}
針對方法的靜態處理
static void CGLIB$STATICHOOK1() { CGLIB$THREAD_CALLBACKS = new ThreadLocal(); CGLIB$emptyArgs = new Object[0]; // 當前代理類 Class var0 = Class.forName("cn.tinyice.demo.proxy.cglib.Todo$$EnhancerByCGLIB$$939b412c"); Class var1; // 經過反射獲取java.lang.Object聲明的全部方法(不包括繼承的方法,這裏也不會有)中的equals、toString、hashCode、clone方法,準備重寫 // 注意 這裏 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()); // 對每一個方法獲取其Method對象$Method以及代理對象$Proxy CGLIB$equals$1$Method = var10000[0]; CGLIB$equals$1$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1"); CGLIB$toString$2$Method = var10000[1]; CGLIB$toString$2$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$2"); CGLIB$hashCode$3$Method = var10000[2]; CGLIB$hashCode$3$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$3"); CGLIB$clone$4$Method = var10000[3]; CGLIB$clone$4$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4"); // 獲取目標類用戶方法doString的Method對象$Method以及代理對象$Proxy,注意這裏var1賦值 CGLIB$doString$0$Method = ReflectUtils.findMethods(new String[]{"doString", "(Ljava/lang/String;)V"}, (var1 = Class.forName("cn.tinyice.demo.proxy.cglib.Todo")).getDeclaredMethods())[0]; CGLIB$doString$0$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/String;)V", "doString", "CGLIB$doString$0"); }
代理方法描述
public final void doString(String var1) { // 方法攔截器:從callBack中獲取,這裏爲ProxyInstance MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if (var10000 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } // 具備方法攔截器則執行其攔截方法 if (var10000 != null) { // 參數: 代理類對象、原方法對象、參數對象、代理方法對象 var10000.intercept(this, CGLIB$doString$0$Method, new Object[]{var1}, CGLIB$doString$0$Proxy); } else { // 不攔截方法則直接調用原方法 super.doString(var1); } }
到這裏已經執行到了ProxyInstance#intercept,而後調用methodProxy.invokeSuper(o, objects);
public Object invokeSuper(Object obj, Object[] args) throws Throwable { try { // 初始化MethodProxy實例,注意在調用時才初始化的 this.init(); // 獲取FastClass MethodProxy.FastClassInfo fci = this.fastClassInfo; // 調用FastClassInfo的FastClass2的invoke,參數爲方法簽名下標,調用對象、方法參數 return fci.f2.invoke(fci.i2, obj, args); } catch (InvocationTargetException var4) { throw var4.getTargetException(); } } private void init() { if (this.fastClassInfo == null) { synchronized(this.initLock) { if (this.fastClassInfo == null) { MethodProxy.CreateInfo ci = this.createInfo; MethodProxy.FastClassInfo fci = new MethodProxy.FastClassInfo(); // fastClass1 生成 ,c1爲MethodProxy.create的第一個參數,目標類 // 生成類名稱前綴=參數2的類名稱+$$FastClassByCGLIB$$Integer.toHexString(hashCode) fci.f1 = helper(ci, ci.c1); // fastClass2生成,c2爲MethodProxy.create的第二個參數,代理類Class fci.f2 = helper(ci, ci.c2); // 調用getIndex獲取下標,參數爲方法簽名,兩者一一映射,這裏使用switch語句來提升效率 fci.i1 = fci.f1.getIndex(this.sig1); fci.i2 = fci.f2.getIndex(this.sig2); // 實例構建完畢 this.fastClassInfo = fci; this.createInfo = null; } } } }
法攔截器中的調用 : fci.f2.invoke(fci.i2, obj, args)
目標類$$EnhancerByCGLIB$$FastClassByCGLIB$$Integer.toHexString(hashCode).invoke(代理類的方法簽名、傳入代理對象,傳入的方法參數)
調用時一樣使用switch語句來調用對應的方法
總結:
經過Enhancer能夠生成代理類,傳入CallBack使代碼執行時進入CallBack代碼中,從而執行用戶自定義代碼;
用戶自定義代碼能夠決定使用目標類調用仍是代理類調用
-
目標類使用$$FastClass1進行調用
-
代理類使用$$FastClass2進行調用
-
$$FastClass都在調用時觸發生成
$$FastClass爲動態生成類,經過index與方法簽名一一映射產生switch case 語句來提升調用效率
代理類是經過繼承方式實現,不能對final類和方法使用CGLIB代理。
所以 CGLB代理調用效率高,可是生成效率慢,至少3個文件,1個代理類,2個FastClass類。全部生成的類都會加載到JVM堆中。
原文地址:
CGLIB代理使用示例與底層原理解析