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


    系列文章索引:

    1. Spring事务Transactional和动态代理(一)-JDK代理实现
    2. Spring事务Transactional和动态代理(二)-cglib动态代理
    3. Spring事务Transactional和动态代理(三)-事务失效的场景

    什么是cglib

    Cglib是一个强大的、高性能的代码生成包,它广泛被许多AOP框架使用,为他们提供方法的拦截。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。JDK必须强制基于interface接口类型:Spring事务Transactional和动态代理(上)-JDK代理实现

    cglib的应用

    cglib应用很广泛,根据cglib在Github上的描述(cglib),存在以下应用:

    1. Byte Code Engineering Library
      也就是JavaClass字节码文件,这个库可以很方便的分析,创建和操作字节码文件
    2. XORM
      是一个可扩展的ORM框架,使用cglib来生成持久化对象,为RDBMS提供了映射到接口的持久Entity,让开发人员专注于业务对象模型
    3. Hibernate
      Hibernate是一个又一个强大的、超高性能的Java对象/关系持久性框架。可以开发持久对象,包括关联、继承、多态性、组合和Java集合框架
    4. The Java Class File Editor
      Java类文件编辑器,允许用户在磁盘上或在运行时加载类时读取/修改Class文件,也它可以动态地创建新类
    5. Nanning Aspects
      是一个基于java的简介AOP框架
    6. Spring
    7. iBatis/Mybatis
    8. ASM
    9. Proxool
      基于java的连接池
    10. Guice
    11. ModelMapper

    cglib的使用

    使用cglib需要先引入jar包,在maven中添加依赖:

            <dependency>
                <groupId>cglib</groupId>
                <artifactId>cglib</artifactId>
                <version>3.3.0</version>
            </dependency>
    

    新建一个目标类,其中一个为final方法,一个为非final方法,用于对比cglib对于两种方法的织入结果:

    public class Student {
    
        public void study(){
            System.out.println("study");
        }
    
        public final void eat(){
            System.out.println("eat");
        }
    
    }
    

    Interceptor 代理类如下:

    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方法生成代理类
      代码如下:
    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是没有的:

    before study
    study
    after study
    
    eat
    

    cglib生成的代理class文件分析

    通过在测试类中加入了

    System.getProperties().put(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, ".");

    代码之后,本地就多出来了一些.class文件如下:

    首先看一下Student$EnhancerByCGLIB$92f3e3f6,继承了Student并且实现了Factory接口(接口方法主要是newInstance,setCallback和getCallbacks),该类中的代码太多,以下代码是节选:

    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的一下几个方法

        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

    Enhancer类源码分析

    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<ClassLoader, AbstractClassGenerator.ClassLoaderData> 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<ClassLoader, AbstractClassGenerator.ClassLoaderData> 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方法,源码如下:

    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对象及其属性

    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类型的变量)。

     private static class FastClassInfo
        {
            //目标类的FastClass
            FastClass f1;
            //代理类的FastClass
            FastClass f2;
            //目标类方法的索引
            int i1;
            //代理类方法的索引
            int i2;
        }
    

    在上一篇JDK代理实现 中提到JDK拦截对象是通过InvocationHandler反射的机制来调用被拦截方法的,反射的效率比较低。
    而cglib是对一个类的方法建立索引,通过索引来直接调用相应的方法。
    如生成的Student(FastClassByCGLIB)ec571eb6就是继承了FastClass,通过getIndex(Signature)通过方法签名来定位一个索引,

        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)具体实现如下:

     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/index.html
  • 相关阅读:
    个人收藏的flex特效网址【经典中的极品】
    JavaWEB开发国际化
    Java实现寻找和为定值的多个数
    Java实现寻找和为定值的多个数
    Java实现寻找和为定值的多个数
    Java实现寻找和为定值的多个数
    Java实现二进制幂
    Java实现二进制幂
    Java实现二进制幂
    Java实现二进制幂
  • 原文地址:https://www.cnblogs.com/qizhelongdeyang/p/12412049.html
Copyright © 2020-2023  润新知