13

Spring事务Transactional和动态代理(二)-cglib动态代理 | IT人生

 3 years ago
source link: http://www.itrensheng.com/archives/cglib
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

系列文章索引: 系列文章索引:

什么是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. Proxool 基于java的连接池
  9. Guice
  10. 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文件如下:

image.png
首先看一下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);
        }
}

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK