分析cglib动态代理的实现(下)

简介: 分析cglib动态代理的实现(下)

如何执行代理方法

当我们拿到生成的代理类对象的时候,执行sayHello()方法时,实际执行的是下面的代码

public final void sayHello() {
    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$sayHello$0$Method, CGLIB$emptyArgs, CGLIB$sayHello$0$Proxy);
    } else {
        super.sayHello();
    }
}
复制代码

MethodInterceptor类型的变量(例子中就是HelloInterceptor类)不为空时,将会执行该类中的intercept()方法

我们分析一下这个方法的参数:

  1. this: 也就是动态代理类的对象
  2. CGLIB$sayHello$0$Method:它是类型为Method的静态属性,,在CGLIB$STATICHOOK1()方法中赋值,而该方法在最下面的静态代码块中被调用。被赋值为Hello类里面的sayHello方法
  3. CGLIB$emptyArgs:传递参数,这个表示没有参数,有参数的话也只是直接传递参数数组,很简单
  4. CGLIB$sayHello$0$Proxy:是cglib自己为这个方法创建的一个代理对象,内部是通过FastClass来执行原始方法的,它比通过反射来执行要快一些(以前),不过目前JDK版本的迭代,通过反射执行效率反而更加高。

栈溢出问题

当我们调用methodProxy.invoke(o, objects)方法时,就会发生栈溢出。我们看一下这个方法的内部是如何实现的

该方法接收两个参数:

  1. 执行方法的对象
  2. 方法的参数
public Object invoke(Object obj, Object[] args) throws Throwable {
    try {
        // 会创建一个Hello类的FastClass类,该类用于较快调用指定的方法
        // 主要是通过index下标来确定需要执行的方法。有兴趣可以自行反编译查看
        init();
        FastClassInfo fci = fastClassInfo;
        // 1
        return fci.f1.invoke(fci.i1, obj, args);
    } catch (InvocationTargetException e) {
        throw e.getTargetException();
    } catch (IllegalArgumentException e) {
        if (fastClassInfo.i1 < 0)
            throw new IllegalArgumentException("Protected method: " + sig1);
        throw e;
    }
}
复制代码

1中的那行代码,就是真正执行方法的语句,我们可以debug看内部的属性是什么


image.png

通过debug我们可以看到f1是一个Hello?FastClassByCGLIB?7572df32类的对象,我们可以在生成的动态代理类下面找到这个类,我们打开这个类,并找到它的invoke方法

我们执行这个方法传递的参数,可以看到i1是0,黄色框部分。

我们传递三个参数分别是:

  1. 0
  2. 动态代理类的对象
  3. 执行方法所需要的参数
public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
    Hello var10000 = (Hello)var2;
    int var10001 = var1;
    try {
        switch(var10001) {
        case 0:
            var10000.sayHello();
            return null;
        case 1:
            return new Boolean(var10000.equals(var3[0]));
        case 2:
            return var10000.toString();
        case 3:
            return new Integer(var10000.hashCode());
        }
    } catch (Throwable var4) {
        throw new InvocationTargetException(var4);
    }
    throw new IllegalArgumentException("Cannot find matching method/constructor");
}
复制代码

我们通过这个源码可以看出实际上执行的是var10000.sayHello(),而var10000是我们传递进来的动态代理对象,也就等于又执行了一次jack.sayHello();,而jack.sayHello()又会执行var10000.sayHello(),所以一直互相调用,直到发生栈溢出。

不发生栈溢出的方法

我们分析完上一个会发生栈溢出的方法调用后,我们来分析一下不会发生栈溢出的方法调用的原理。

当我们调用的时methodProxy.invokeSuper(o, objects);时,就不会发生栈溢出的问题,我们看一下这个方法的源码

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();
    }
}
复制代码

image.png

通过debug,我们可以看到f2Hello?EnhancerByCGLIB?35d006ca?FastClassByCGLIB?518e6b66_3,它是为生成的动态代理类生成的FastClass类。

我们执行invoke方法传递的参数分别是:

  1. 17
  2. 动态代理类的对象
  3. 执行方法所需要的参数
public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
    35d006ca var10000 = (35d006ca)var2;
    int var10001 = var1;
    try {
        switch(var10001) {
        case 0:
            return new Boolean(var10000.equals(var3[0]));
        case 1:
            return var10000.toString();
        case 2:
            return new Integer(var10000.hashCode());
        case 3:
            return var10000.clone();
        case 4:
            return var10000.newInstance((Callback[])var3[0]);
        case 5:
            return var10000.newInstance((Callback)var3[0]);
        case 6:
            return var10000.newInstance((Class[])var3[0], (Object[])var3[1], (Callback[])var3[2]);
        case 7:
            var10000.setCallback(((Number)var3[0]).intValue(), (Callback)var3[1]);
            return null;
        case 8:
            var10000.sayHello();
            return null;
        case 9:
            35d006ca.CGLIB$SET_STATIC_CALLBACKS((Callback[])var3[0]);
            return null;
        case 10:
            35d006ca.CGLIB$SET_THREAD_CALLBACKS((Callback[])var3[0]);
            return null;
        case 11:
            var10000.setCallbacks((Callback[])var3[0]);
            return null;
        case 12:
            return var10000.getCallbacks();
        case 13:
            return var10000.getCallback(((Number)var3[0]).intValue());
        case 14:
            return var10000.CGLIB$toString$2();
        case 15:
            return new Integer(var10000.CGLIB$hashCode$3());
        case 16:
            return var10000.CGLIB$clone$4();
        case 17:
            var10000.CGLIB$sayHello$0();
            return null;
        case 18:
            return new Boolean(var10000.CGLIB$equals$1(var3[0]));
        case 19:
            return 35d006ca.CGLIB$findMethodProxy((Signature)var3[0]);
        case 20:
            35d006ca.CGLIB$STATICHOOK1();
            return null;
        }
    } catch (Throwable var4) {
        throw new InvocationTargetException(var4);
    }
    throw new IllegalArgumentException("Cannot find matching method/constructor");
}
复制代码


实际上就是直接调用父类的sayHello方法,也就是最原始的那个方法。所以这里并不会出现互相调用的局面。

还剩下一个method.invoke(o, objects)方法没有讲解。大家可以自己去debug一下看看会不会报错,原理跟methodProxy.invoke()类似。

总结

cglib动态代理和JDK动态代理本质的区别就是:

  • cglib对于类的代理是基于继承的。cglib通过继承来实现动态代理(也可以通过接口)
  • JDK只能基于接口,因为它自身需要继承Proxy类,而java不支持多继承。而JDK只能通过实现接口来实现动态代理。

cglib动态代理的过程中会生成三个Class文件(实际是五个,另外两个不需要理解),分别是:

  1. 原始类的动态代理类
  2. 原始类的FastClass类
  3. 动态代理类的FastClass类

cglib调用原始方法是通过FastClass的下标进行调用的。而JDK动态代理是通过反射进行调用的。

尾声

至此,cglib动态代理就讲完了,当然,ASM操作字节码生成动态代理文件部分因为不熟悉所以没有去讲解。但是不影响理解cglib动态代理的原理。写了两天,一共花了12个小时,加上debug,找资料,阅读源码,终于完成了。希望大家看到错误的地方多多包涵,我会虚心请教。

这几天会总结出cglib动态代理和JDK动态代理的区别!

目录
相关文章
|
11月前
|
缓存 Java 数据库连接
分析JDK动态代理的实现
分析JDK动态代理的实现
68 0
|
Java Maven
JDK动态代理和CGLIB动态代理的区别及实例
JDK动态代理和CGLIB动态代理的区别及实例
202 0
|
11月前
|
缓存 Java
分析cglib动态代理的实现(上)
分析cglib动态代理的实现(上)
62 0
|
Java 索引 Spring
静态代理?动态代理?JDK动态代理和CGLIB包实现动态代理的区别
什么是静态代理?什么是动态代理?JDK动态代理和CGLIB包实现动态代理的区别
118 1
静态代理?动态代理?JDK动态代理和CGLIB包实现动态代理的区别
|
Java 测试技术 Spring
动态代理:Cglib原理讲解
动态代理:Cglib原理讲解
144 1
|
Java Spring
jdk动态代理和cglib动态代理
只有聪明人才能看见的简介~( ̄▽ ̄~)~
87 0
jdk动态代理和cglib动态代理
|
Java 索引
jdk动态代理和cglib动态代理的原理分析(下)
jdk动态代理和cglib动态代理的原理分析(下)
223 0
jdk动态代理和cglib动态代理的原理分析(下)
|
设计模式 Java 索引
jdk动态代理和cglib动态代理的原理分析(上)
jdk动态代理和cglib动态代理的原理分析(上)
jdk动态代理和cglib动态代理的原理分析(上)
|
设计模式 Java
【动态代理】—— JDK和cglib的动态代理
【动态代理】—— JDK和cglib的动态代理
142 0
【动态代理】—— JDK和cglib的动态代理
|
设计模式 Java API
静态代理、动态代理(JDK动态代理,Cglib动态代理)(1)
静态代理、动态代理(JDK动态代理,Cglib动态代理)(1)
129 0
静态代理、动态代理(JDK动态代理,Cglib动态代理)(1)