首页 科技正文

阳江市:Spring事务Transactional和动态代理(二)-cglib动态代理

admin 科技 2020-06-02 67 1

系列文章索引:

  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
,

诚信在线

诚信在线(www.cx11yj.cn)现已开放诚信在线手机版下载。游戏公平、公开、公正,用实力赢取信誉。

版权声明

本文仅代表作者观点,
不代表本站Allbet的立场。
本文系作者授权发表,未经许可,不得转载。

评论

精彩评论
  • 2020-06-02 02:29:09

    Allbetwww.szqygww.com欢迎进入欧博开户平台(Allbet Gaming),欧博开户平台开放欧博(Allbet)开户、欧博(Allbet)代理开户、欧博(Allbet)电脑客户端、欧博(Allbet)APP下载等业务额,我认为比较优秀