Spring事务Transactional和动态代理(二)-cglib动态代理

软件发布|下载排行|最新软件

当前位置:首页IT学院IT技术

Spring事务Transactional和动态代理(二)-cglib动态代理

骑着龙的羊   2020-03-05 我要评论
系列文章索引: 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:/https://img.qb5200.com/download-x/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:/https://img.qb5200.com/download-x/docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html 4. https://www.baeldung.com/cglib 5. https://www.ibm.comhttps://img.qb5200.com/download-x/developerworks/cn/java/j-lo-springaopcglib/index.html

Copyright 2022 版权所有 软件发布 访问手机版

声明:所有软件和文章来自软件开发商或者作者 如有异议 请与本站联系 联系我们