jdk动态代理和cglib动态代理的原理分析(下)

简介: jdk动态代理和cglib动态代理的原理分析(下)

在intercept()方法中执行MethodProxy的invokeSuper方法:

1dc618a0ed9580ce8bfa6facb208c08f.png


public Object invokeSuper(Object obj, Object[] args) throws Throwable {
    try {
      // 调用init方法,获取下标,且要生成FastClass类实例
        init();
        FastClassInfo fci = fastClassInfo;
        // f2是被代理类的FastClass实例,下标在init方法中计算好了
        // 假设我们调用的上面的toString()方法,其实也就是调用的代理类的CGLIB$toString$0方法,这样就实现了原始方法的调用,所以我们才需要执行invokeSuper,如果调用invoke,就会形成死循环
        return fci.f2.invoke(fci.i2, obj, args);
    } catch (InvocationTargetException e) {
        throw e.getTargetException();
    }
}
/**
 * invoke执行的是原始类的方法,因为f1是被代理类的FastClass
 * 
 */
public Object invoke(Object obj, Object[] args) throws Throwable {
    try {
        init();
        FastClassInfo fci = fastClassInfo;
        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;
    }
}


看下被代理类的fastClass的invoke方法():


public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
        558ee7c6 var10000 = (558ee7c6)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((Class[])var3[0], (Object[])var3[1], (Callback[])var3[2]);
            case 6:
                return var10000.newInstance((Callback)var3[0]);
            case 7:
                var10000.setCallbacks((Callback[])var3[0]);
                return null;
            case 8:
                var10000.findLove();
                return null;
            case 9:
                var10000.setCallback(((Number)var3[0]).intValue(), (Callback)var3[1]);
                return null;
            case 10:
                return var10000.getCallback(((Number)var3[0]).intValue());
            case 11:
                return var10000.getCallbacks();
            case 12:
                558ee7c6.CGLIB$SET_STATIC_CALLBACKS((Callback[])var3[0]);
                return null;
            case 13:
                558ee7c6.CGLIB$SET_THREAD_CALLBACKS((Callback[])var3[0]);
                return null;
            case 14:
                return 558ee7c6.CGLIB$findMethodProxy((Signature)var3[0]);
            case 15:
                return new Boolean(var10000.CGLIB$equals$1(var3[0]));
            case 16:
                return var10000.CGLIB$toString$2();
            case 17:
                var10000.CGLIB$findLove$0();
                return null;
            case 18:
                return var10000.CGLIB$clone$4();
            case 19:
                return new Integer(var10000.CGLIB$hashCode$3());
            case 20:
                558ee7c6.CGLIB$STATICHOOK1();
                return null;
            }
        } catch (Throwable var4) {
            throw new InvocationTargetException(var4);
        }
        throw new IllegalArgumentException("Cannot find matching method/constructor");
    }

5d4c6812c8535adbb050f4ddf2e1bce8.png

最终调到了 ,被代理类的findLove()方法。

cglib这里有点复杂,重新梳理下:


CglibTest

1dc618a0ed9580ce8bfa6facb208c08f.png


进入到代理类中调用findLove()方法:


Customer$$EnhancerByCGLIB$$558ee7c6

5d4c6812c8535adbb050f4ddf2e1bce8.png


调用的时候会,如果媒婆类CGlibMeipo,写了拦截方法,所以会跳到intercept()


CGlibMeipo


46a9d80a6e05e4e3b19d57a0ee70bcdf.png


执行完before,开始调动methodProxy的invokeSuper方法,


MethodProxy

66ba272a0bfc97be54a5fa679e3d5482.png

//f1;com.example.proxy.staticproxy.Son
//f2.com.example.proxy.staticproxy.Son$$EnhancerByCGLIB$$d760ccc


继续走代理类的代理类的


fastClasscom.example.proxy.staticproxy.Son$$EnhancerByCGLIB$$d760ccc的invoke


方法:


com.example.proxy.staticproxy.Son$$EnhancerByCGLIB$$d760ccc

1dc618a0ed9580ce8bfa6facb208c08f.png

5d4c6812c8535adbb050f4ddf2e1bce8.png


到了这里又返回到了代理类Son$$EnhancerByCGLIB$$d760ccc中,开始执行 CGLIB$findLove$0()方法:


代理类: Son$$EnhancerByCGLIB$$d760ccc


46a9d80a6e05e4e3b19d57a0ee70bcdf.png

66ba272a0bfc97be54a5fa679e3d5482.png



该类继承了Son,所以走Son里面的finidLove方法:


Son


1dc618a0ed9580ce8bfa6facb208c08f.png


执行完返回到媒婆动态代理类的after()方法:


CGlibMeipo


5d4c6812c8535adbb050f4ddf2e1bce8.png


到此就执行完了,返回到了测试类中:

CglibTest

46a9d80a6e05e4e3b19d57a0ee70bcdf.png

66ba272a0bfc97be54a5fa679e3d5482.png


自此,cglib动态创建代理对象就调试完了。

下面开始总结下:


五.jdk和cglib各自怎么调用被代理对象的方法?


1.JDK 动态代理是实现了被代理对象的接口,CGLib 是继承了被代理对象。


2.JDK 和CGLib 都是在运行期生成字节码,JDK 是直接写Class 字节码,CGLib 使用ASM

框架写Class 字节码,Cglib 代理实现更复杂,生成代理类比JDK 效率低。


3.JDK 调用代理方法,是通过反射机制调用,CGLib 是通过FastClass 机制直接调用方法,CGLib 执行效率更高。


六.cglib动态生成的代理对象的时候为什么是3个class?


首先cglib需要创建一个代理对象,这是必然的,多出来的2个class文件,是因为cglib没有使用反射,而是使用fastclass维护每个方法的索引。一个类保存代理对象的方法索引,一个保存被代理对象的索引。这样当客户端调用的时候,fastclass机制就提供给客户端相应的方法区执行。所以是3个class。


相关文章
|
10天前
|
Java 编译器
Java健壮性 Java可移植性 JDK, JRE, JVM三者关系 Java的加载与执行原理 javac编译与JAVA_HOME环境变量介绍 Java中的注释与缩进 main方法的args参数
Java健壮性 Java可移植性 JDK, JRE, JVM三者关系 Java的加载与执行原理 javac编译与JAVA_HOME环境变量介绍 Java中的注释与缩进 main方法的args参数
11 1
|
11天前
|
设计模式 Java 程序员
java动态代理(JDK和cglib)
java动态代理(JDK和cglib)
|
13天前
|
设计模式 Java 程序员
java动态代理(JDK和cglib)
java动态代理(JDK和cglib)
11 0
|
14天前
|
缓存 Java Maven
JDK 动态代理
JDK 动态代理
6 0
|
19天前
|
Java Spring
深入解析Spring源码,揭示JDK动态代理的工作原理。
深入解析Spring源码,揭示JDK动态代理的工作原理。
18 0
|
4天前
|
IDE Oracle Java
day4:JDK、IntelliJ IDEA的安装和环境变量配置
【7月更文挑战第4天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
11 0
|
12天前
|
Linux 测试技术 开发工具
CentOS Linux 8使用阿里源(安装jdk11、git测试)
CentOS Linux 8使用阿里源(安装jdk11、git测试)
54 1
|
18天前
|
Java 关系型数据库 MySQL
杨校老师课堂之Java项目部署到云端服务器之安装MySQL、Jdk、Tomcat
杨校老师课堂之Java项目部署到云端服务器之安装MySQL、Jdk、Tomcat
24 0
杨校老师课堂之Java项目部署到云端服务器之安装MySQL、Jdk、Tomcat
|
1月前
|
Oracle Java 关系型数据库
玩客云安装Armbian和部署jdk环境
该文介绍了在玩客云设备上安装Armbian系统和Java SDK的步骤。首先,需要准备玩客云设备、Armbian镜像文件和USB工具。然后,通过短接点刷入Armbian系统,并通过SSH访问。接着,从可信源下载Java SDK,将其解压并移动到合适目录,编辑环境变量使其生效。最后验证Java安装成功。注意选择兼容版本并备份数据。内容涵盖了ROM开发相关技术。
|
1月前
|
Oracle Java 关系型数据库
Java入门——开发环境、入门程序(搭建Java开发环境、安装JDK 验证、JDK、编写代码、编译代码、运行代码)
Java入门——开发环境、入门程序(搭建Java开发环境、安装JDK 验证、JDK、编写代码、编译代码、运行代码)
35 3