1. 程式人生 > >Spring事務Transactional和動態代理(二)-cglib動態代理

Spring事務Transactional和動態代理(二)-cglib動態代理

系列文章索引: 1. [Spring事務Transactional和動態代理(一)-JDK代理實現](http://www.itrensheng.com/archives/spring_transaction_jdk_proxy) 2. [Spring事務Transactional和動態代理(二)-cglib動態代理](Spring事務Transactional和動態代理(一)-JDK代理實現) 3. [Spring事務Transactional和動態代理(三)-事務失效的場景](http://www.itrensheng.com/archives/spring_transactional_uneffect) ### 什麼是cglib Cglib是一個強大的、高效能的程式碼生成包,它廣泛被許多AOP框架使用,為他們提供方法的攔截。它為沒有實現介面的類提供代理,為JDK的動態代理提供了很好的補充。JDK必須強制基於interface介面型別:[Spring事務Transactional和動態代理(上)-JDK代理實現](http://www.itrensheng.com/archives/spring_transaction_jdk_proxy) ### cglib的應用 cglib應用很廣泛,根據cglib在Github上的描述([cglib](https://github.com/cglib/cglib/wiki)),存在以下應用: 1. [ Byte Code Engineering Library ](http://commons.apache.org/proper/commons-bcel/) 也就是JavaClass位元組碼檔案,這個庫可以很方便的分析,建立和操作位元組碼檔案 2. [XORM ](http://xorm.sourceforge.net/) 是一個可擴充套件的ORM框架,使用cglib來生成持久化物件,為RDBMS提供了對映到介面的持久Entity,讓開發人員專注於業務物件模型 3. [Hibernate](http://hibernate.sourceforge.net/) Hibernate是一個又一個強大的、超高效能的Java物件/關係永續性框架。可以開發持久物件,包括關聯、繼承、多型性、組合和Java集合框架 4. [The Java Class File Editor ](http://staff.develop.com/halloway/code/gd.html) Java類檔案編輯器,允許使用者在磁碟上或在執行時載入類時讀取/修改Class檔案,也它可以動態地建立新類 5. [Nanning Aspects](http://nanning.codehaus.org/) 是一個基於java的簡介AOP框架 6. [Spring ](https://spring.io/) 7. iBatis/Mybatis 8. ASM 9. [Proxool](http://proxool.sourceforge.net/) 基於java的連線池 10. [Guice ](https://code.google.com/p/google-guice/) 11. [ModelMapper](http://modelmapper.org/) ### cglib的使用 使用cglib需要先引入jar包,在maven中新增依賴: ```java cglib cglib 3.3.0 ``` 新建一個目標類,其中一個為final方法,一個為非final方法,用於對比cglib對於兩種方法的織入結果: ```java public class Student { public void study(){ System.out.println("study"); } public final void eat(){ System.out.println("eat"); } } ``` Interceptor 代理類如下: ```java public class CglibInterceptor implements MethodInterceptor { //織入前的處理 private void beforeInvoke(Method method){ System.out.println("before " + method.getName()); } //織入後的處理 private void afterInvoke(Method method){ System.out.println("after " + method.getName()); } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { beforeInvoke(method); //呼叫cglib的invokeSuper而不是invoke方法 Object object = methodProxy.invokeSuper(o,objects); afterInvoke(method); return object; } } ``` 測試類的呼叫順序為 1. 建立增強建Enhancer例項 2. 通過setSuperclass方法來設定目標類 3. 通過setCallback設定Interceptor攔截 4. 呼叫Enhancer的create方法生成代理類 程式碼如下: ```java public class CglibTesst { public static void main(String[] args) { //把生產的代理類儲存到磁碟指定資料夾 System.getProperties().put(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "."); Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(Student.class); enhancer.setCallback(new CglibInterceptor()); Student studentProxy = (Student) enhancer.create(); studentProxy.study(); studentProxy.eat(); } } ``` 其中的輸出如下,可以看到只有非final方法study織入了before和after邏輯,而final方法eat是沒有的: ```java before study study after study eat ``` #### cglib生成的代理class檔案分析 通過在測試類中加入了 >
System.getProperties().put(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "."); 程式碼之後,本地就多出來了一些.class檔案如下: ![](https://img2020.cnblogs.com/blog/314515/202003/314515-20200304204249287-1394358571.png) 首先看一下Student$EnhancerByCGLIB$92f3e3f6,繼承了Student並且實現了Factory介面(介面方法主要是newInstance,setCallback和getCallbacks),該類中的程式碼太多,以下程式碼是節選: ```java public class Student$EnhancerByCGLIB$92f3e3f6 extends Student implements Factory { //靜態初始化類 static void CGLIB$STATICHOOK1() { CGLIB$THREAD_CALLBACKS = new ThreadLocal(); CGLIB$emptyArgs = new Object[0]; Class var0 = Class.forName("com.randy.dynamicproxy.cglib.Student$$EnhancerByCGLIB$$92f3e3f6"); 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$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"); CGLIB$study$0$Method = ReflectUtils.findMethods(new String[]{"study", "()V"}, (var1 = Class.forName("com.randy.dynamicproxy.cglib.Student")).getDeclaredMethods())[0]; CGLIB$study$0$Proxy = MethodProxy.create(var1, var0, "()V", "study", "CGLIB$study$0"); } static { CGLIB$STATICHOOK1(); } final void CGLIB$study$0() { super.study(); } public final void study() { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; //檢查當前Callback攔截物件 if (var10000 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } //根據是否存在判定是通過攔截類來呼叫還是直接呼叫父類Student的study方法 if (var10000 != null) { var10000.intercept(this, CGLIB$study$0$Method, CGLIB$emptyArgs, CGLIB$study$0$Proxy); } else { super.study(); } } final boolean CGLIB$equals$1(Object var1) { return super.equals(var1); } public final boolean equals(Object var1) { ... } final String CGLIB$toString$2() { return super.toString(); } public final String toString() { ... } final int CGLIB$hashCode$3() { return super.hashCode(); } public final int hashCode() { ... } final Object CGLIB$clone$4() throws CloneNotSupportedException { return super.clone(); } protected final Object clone() throws CloneNotSupportedException { ... } } ``` 可以看到該生成類中除了實現Factory介面的方法以外,都複寫了Student類以及超類Object中的非final方法(對於Student中的final方法eat和Object中的final方法wati,notify,notifyAll等方法都沒有複寫),**這也就是為什麼cglib無法對final方法進行代理,因為java不允許複寫final方法** 另外兩個類 Student$EnhancerByCGLIB$92f3e3f6$FastClassByCGLIB$1d02f934 和 Student$FastClassByCGLIB$ec571eb6 都繼承了cglib的抽象類FastClass, 主要是實現了FastClass的一下幾個方法 ```java public abstract int getIndex(String var1, Class[] var2); public abstract int getIndex(Class[] var1); public abstract Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException; public abstract Object newInstance(int var1, Object[] var2) throws InvocationTargetException; public abstract int getIndex(Signature var1); public abstract int getMaxIndex(); ``` 其中的 ### cglib的原理 cglib動態生成一個要代理類的子類,子類重寫要代理的類的所有不是final的方法(**cglib無法對final方法進行代理**)。在子類中採用方法攔截的技術攔截所有父類方法的呼叫,順勢織入橫切邏輯。 CGLIB底層使用位元組碼處理框架ASM,來轉換位元組碼並生成新的類。關於java位元組碼請檢視:[The Java class File Format](https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html) #### Enhancer類原始碼分析 ```java public class Enhancer extends AbstractClassGenerator { //設定目標類作為父類,也就是對應生成的Student$$EnhancerByCGLIB$$92f3e3f6類繼承了Student public void setSuperclass(Class superclass) { if (superclass != null && superclass.isInterface()) { this.setInterfaces(new Class[]{superclass}); } else if (superclass != null && superclass.equals(Object.class)) { this.superclass = null; } else { this.superclass = superclass; } } //通過Enhancer來建立代理類 public Object create() { this.classOnly = false; this.argumentTypes = null; return this.createHelper(); } private Object createHelper() { this.preValidate(); //根據當前設定的父類等信心構造一個唯一的key Object key = KEY_FACTORY.newInstance(this.superclass != null ? this.superclass.getName() : null, ReflectUtils.getNames(this.interfaces), this.filter == ALL_ZERO ? null : new WeakCacheKey(this.filter), this.callbackTypes, this.useFactory, this.interceptDuringConstruction, this.serialVersionUID); this.currentKey = key; Object result = super.create(key); return result; } } protected Object create(Object key) { try { ClassLoader loader = this.getClassLoader(); Map cache = CACHE; //首先從快取中查詢key,如果就生成一個 AbstractClassGenerator.ClassLoaderData data = (AbstractClassGenerator.ClassLoaderData)cache.get(loader); if (data == null) { Class var5 = AbstractClassGenerator.class; synchronized(AbstractClassGenerator.class) { cache = CACHE; data = (AbstractClassGenerator.ClassLoaderData)cache.get(loader); if (data == null) { Map newCache = new WeakHashMap(cache); //核心是呼叫了AbstractClassGenerator的generate來生成位元組碼檔案,並通過ReflectUtils.defineClass返回 data = new AbstractClassGenerator.ClassLoaderData(loader); //加入快取 newCache.put(loader, data); CACHE = newCache; } } } this.key = key; Object obj = data.get(this, this.getUseCache()); //如果是類就通過firstInstance初始化,而firstInstance在AbstractClassGenerator類中是一個抽象方法,具體實現如下 //firstInstance和nextInstance都是通過cglib的ReflectUtils.newInstance來建立例項的 return obj instanceof Class ? this.firstInstance((Class)obj) : this.nextInstance(obj); } catch (RuntimeException var9) { throw var9; } catch (Error var10) { throw var10; } catch (Exception var11) { throw new CodeGenerationException(var11); } } ``` #### MethodProxy 當所生成的代理類被呼叫的時候,MethodProxy會在所設定的CallBack中呼叫intercept方法。而在上面的CglibInterceptor類的intercept方法中就是使用的MethodProxy.invokeSuper方法,原始碼如下: ```java public Object invokeSuper(Object obj, Object[] args) throws Throwable { try { //單例初始化 init(); FastClassInfo fci = fastClassInfo; return fci.f2.invoke(fci.i2, obj, args); } catch (InvocationTargetException e) { throw e.getTargetException(); } } ``` #### init方法: init()方法是一個經典的雙重檢查單例設計模式,初始判斷物件是否已經初始化了,如果沒有就加鎖並再次判空。初始化的內容主要是FastClassInfo物件及其屬性 ```java private final Object initLock = new Object(); private void init() { if (fastClassInfo == null) { synchronized (initLock) { if (fastClassInfo == null) { CreateInfo ci = createInfo; FastClassInfo fci = new FastClassInfo(); fci.f1 = helper(ci, ci.c1); fci.f2 = helper(ci, ci.c2); //通過getIndex來查詢到指定方法的索引 fci.i1 = fci.f1.getIndex(sig1); fci.i2 = fci.f2.getIndex(sig2); fastClassInfo = fci; createInfo = null; } } } } ``` #### FastClass機制 FastClass機制就是對一個類的方法建立索引,通過索引來直接呼叫相應的方法,在上述的其中的invokeSuper中init初始化的主要就是FastClassInfo(內部類,持有兩個FastClass型別的變數)。 ```java private static class FastClassInfo { //目標類的FastClass FastClass f1; //代理類的FastClass FastClass f2; //目標類方法的索引 int i1; //代理類方法的索引 int i2; } ``` 在上一篇[JDK代理實現](http://www.itrensheng.com/archives/spring_transaction_jdk_proxy) 中提到JDK攔截物件是通過InvocationHandler反射的機制來呼叫被攔截方法的,反射的效率比較低。 而cglib是對一個類的方法建立索引,通過索引來直接呼叫相應的方法。 如生成的Student$FastClassByCGLIB$ec571eb6就是繼承了FastClass,通過getIndex(Signature)通過方法簽名來定位一個索引, ```java public int getIndex(Signature var1) { String var10000 = var1.toString(); switch(var10000.hashCode()) { case -1310345955: if (var10000.equals("eat()V")) { return 1; } break; case 1826985398: if (var10000.equals("equals(Ljava/lang/Object;)Z")) { return 2; } break; case 1876544780: if (var10000.equals("study()V")) { return 0; } break; case 1913648695: if (var10000.equals("toString()Ljava/lang/String;")) { return 3; } break; case 1984935277: if (var10000.equals("hashCode()I")) { return 4; } } return -1; } ``` 在根據獲取的的Index位置來呼叫invoke方法,invoke方法在FastClass類中是一個抽象方法,子類(也就是生成的Student$FastClassByCGLIB$ec571eb6繼承FastClass)具體實現如下: ```java public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException { Student var10000 = (Student)var2; int var10001 = var1; try { switch(var10001) { case 0: var10000.study(); return null; case 1: var10000.eat(); return null; case 2: return new Boolean(var10000.equals(var3[0])); case 3: return var10000.toString(); case 4: return new Integer(var10000.hashCode()); } } catch (Throwable var4) { throw new InvocationTargetException(var4); } } ``` 參考: 1. https://github.com/cglib/cglib/wiki 2. https://tech.meituan.com/2019/09/05/java-bytecode-enhancement.html 3. https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html 4. https://www.baeldung.com/cglib 5. https://www.ibm.com/developerworks/cn/java/j-lo-springaopcglib/ind