Cglib 动态代理

867 阅读5分钟

参考

1、介绍了 Cglib的生成的3个动态代理类源码结构, FastClass机制
https://www.cnblogs.com/monkey0307/p/8328821.html

2、通过从 MethodProxy的invokeSuper方法执行的过程来了解Cglib动态代理执行过程
https://www.cnblogs.com/yangming1996/p/6824249.html

概述

  • JDK 动态代理类的生成的机制有个鲜明的特点是: 某个类必须有实现的接口, 而生成的代理类也只能代理某个类接口定义的方法, 如果某个类没有实现接口,那么这个类就不能用JDK产生动态代理了!

  • Cglib 可以通过继承的方式实现动态代理,Cglib通过扫描该类以及其父类中所有的 public 非 final修饰的方法,通过 asm 定义该类的子类字节码,其中该子类重写了父类所有的方法,然后返回该子类的实例作为代理类。也就是说我们的 Cglib是用该类的子类作为代理类来实现代理操作的。当然 Cglib的缺点也是呼之欲出,对于被代理类中的非 public或者 final修饰的方法,不能实现代理。

1、使用方式

MethodInterceptor#intercept方法参数说明: Object obj:被代理的原对象 Method method:被调用的当前方法 Object[] args:该方法的参数集合 MethodProxy proxy:被调用方法的代理,它可以和 method完成同样的事情,但是它使用 FastClass机制非反射执行方法,效率高

public class MyMethodInterceptor implements MethodInterceptor {

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("这里是对目标类进行增强!!!" + method.getName());
        //注意这里的方法调用,不是用反射哦!!!
        Object object = proxy.invokeSuper(obj, args);
        return object;
    }

    public static void main(String[] args) {

        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "java_workapace");

        //创建Enhancer对象,类似于JDK动态代理的Proxy类,下一步就是设置几个参数
        Enhancer enhancer = new Enhancer();
        //设置目标类的字节码文件
        enhancer.setSuperclass(OrderServiceImpl.class);
        //设置回调函数
        enhancer.setCallback(new MyMethodInterceptor());

        //这里的creat方法就是正式创建代理类
        OrderServiceImpl proxyDog = (OrderServiceImpl)enhancer.create();
        //调用代理类的eat方法

        proxyDog.test1("xoiaoming");
    }
}

2、原理介绍

2.1、获取 Cglib 生成的代理类对象 & FastClass

通过设置系统变量可以保存生成代理类的字节码 System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "java_workapace");

Cglib 动态代理过程生成了三个字节码文件

  1. OrderServiceImpl?EnhancerByCGLIB?e4b0590d?FastClassByCGLIB?cf8aae5d.class
  2. OrderServiceImpl?EnhancerByCGLIB?e4b0590d.class
  3. OrderServiceImpl?FastClassByCGLIB?97d766be.class

OrderServiceImpl?EnhancerByCGLIB?e4b0590d.class 是增加代理类对象,继承了被代理类OrderServiceImpl。类中含有被代理了的方法 & 父类中方法的调用,被重写了方法名字。 test1()、CGLIB$test1$2

OrderServiceImpl?EnhancerByCGLIB?e4b0590d?FastClassByCGLIB?cf8aae5d.class、OrderServiceImpl?FastClassByCGLIB?97d766be.class都是对代理类OrderServiceImpl?EnhancerByCGLIB?e4b0590d.class 中的方法索引

OrderServiceImpl?EnhancerByCGLIB?e4b0590d?FastClassByCGLIB?cf8aae5d.class FastClass类: 含有被代理方法&非代理方法的方法索引 test1()、CGLIB$test12
OrderServiceImplFastClassByCGLIB$97d766be.class FastClass类: 只还有被代理方法的索引 test1()

2.2、FastClass机制

Cglib 动态代理执行代理方法效率之所以比JDK的高是因为Cglib采用了FastClass机制,它的原理简单来说就是: 为代理类和被代理类各生成一个Class,这个Class会为代理类或被代理类的方法分配一个index(int类型)。 这个index当做一个入参,FastClass就可以直接定位要调用的方法直接进行调用,这样省去了反射调用,所以调用效率比JDK动态代理通过反射调用高。

1、通过反编译 OrderServiceImpl?FastClassByCGLIB?97d766be.class、OrderServiceImpl?EnhancerByCGLIB?e4b0590d?FastClassByCGLIB?cf8aae5d.class FastClassl类查看类结构

 public int getIndex(Signature var1) {
        String var10000 = var1.toString();
        switch(var10000.hashCode()) {
        case -1147862635:
            if (var10000.equals("test2()V")) {
                return 0;
            }
            break;
        case 992023936:
            if (var10000.equals("test1(Ljava/lang/String;)V")) {
                return 1;
            }
            break;
        case 1826985398:
            if (var10000.equals("equals(Ljava/lang/Object;)Z")) {
                return 2;
            }
            break;
        case 1913648695:
            if (var10000.equals("toString()Ljava/lang/String;")) {
                return 3;
            }
            break;
        case 1984935277:
            if (var10000.equals("hashCode()I")) {
                return 4;
            }
        }

        return -1;
    }

   public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
        OrderServiceImpl var10000 = (OrderServiceImpl)var2;
        int var10001 = var1;

        try {
            switch(var10001) {
            case 0:
                var10000.test2();
                return null;
            case 1:
                var10000.test1((String)var3[0]);
                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);
        }

        throw new IllegalArgumentException("Cannot find matching method/constructor");
    }

2、FastClass并不是跟代理类一块生成的,而是在第一次执行MethodProxy invoke/invokeSuper时生成的并放在了缓存中。

//MethodProxy invoke/invokeSuper都调用了init()
private void init() {
        if(this.fastClassInfo == null) {
            Object var1 = this.initLock;
            synchronized(this.initLock) {
                if(this.fastClassInfo == null) {
                    MethodProxy.CreateInfo ci = this.createInfo;
                    MethodProxy.FastClassInfo fci = new MethodProxy.FastClassInfo();
                    fci.f1 = helper(ci, ci.c1);//如果缓存中就取出,没有就生成新的FastClass
                    fci.f2 = helper(ci, ci.c2);
                    fci.i1 = fci.f1.getIndex(this.sig1);//获取方法的index
                    fci.i2 = fci.f2.getIndex(this.sig2);
                    this.fastClassInfo = fci;
                    this.createInfo = null;
                }
            }
        }

    }

2.3、Cglib 代理类分析

1、通过反编译 代理类 OrderServiceImpl?EnhancerByCGLIB?e4b0590d.class 分析。 在代理类中 继承被代理的方法例:test3(), 是通过回调方法来执行 ##在enhancer.setCallback(new MyMethodInterceptor())设置。 父类中的方法生成了一个 CGLIB$test3$1()方法去调用。

  final void CGLIB$test3$1() {
        super.test3();
    }

    public final void test3() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if (var10000 != null) {
            var10000.intercept(this, CGLIB$test3$1$Method, CGLIB$emptyArgs, CGLIB$test3$1$Proxy);
        } else {
            super.test3();
        }
    }

2、进入 org.springframework.cglib.proxy.MethodInterceptor#intercept 方法后, 通过 org.springframework.cglib.proxy.MethodProxy#invokeSuper 执行被代理类方法。

3、MethodProxy 持有代理类、被代理类的信息FastClassInfo, 通过FastClass执行方法

private static class FastClassInfo {
    FastClass f1;// FastClass: OrderServiceImpl$$FastClassByCGLIB$$97d766be.class, 只含有代理中被增强方法的索引
    FastClass f2;// FastClass: OrderServiceImpl$$EnhancerByCGLIB$$e4b0590d$$FastClassByCGLIB$$cf8aae5d.class 
    int i1; // 被代理类的方法签名 test1
    int i2;// 未被代理类的方法签名 CGLIB$test1$2

    private FastClassInfo() {
    }
}

2.4、invokeSuper()、invoke() 区别

MethodProxy#invokeSuper 方法执行代理类中的被重写名字的调用父类方法(未被增强的方法), MethodProxy.FastClassInfo#f2 执行 CGLIB$test2$0()方法 MethodProxy#invoke 方法执行代理的类中 重写父类的方法(被增强的方法), MethodProxy.FastClassInfo#f1 执行 test2()方法

    final void CGLIB$test2$0() {
        super.test2();
    }

    public final void test2() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if (var10000 != null) {
        } else {
            super.test2();
        }
    }

在重写 com.wl.springbootdemo.demo.dynamic.cglib.MyMethodInterceptor#intercept 方法时, 需要使用 invokeSuper()方法执行, 否则会一直死循环