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。


相关文章
|
13天前
|
设计模式 Java API
[Java]静态代理与动态代理(基于JDK1.8)
本文介绍了代理模式及其分类,包括静态代理和动态代理。静态代理分为面向接口和面向继承两种形式,分别通过手动创建代理类实现;动态代理则利用反射技术,在运行时动态创建代理对象,分为JDK动态代理和Cglib动态代理。文中通过具体代码示例详细讲解了各种代理模式的实现方式和应用场景。
14 0
[Java]静态代理与动态代理(基于JDK1.8)
|
24天前
|
Java
【编程进阶知识】静态代理、JDK动态代理及Cglib动态代理各自存在的缺点及代码示例
本文介绍了三种Java代理模式:静态代理、JDK动态代理和Cglib动态代理。静态代理针对特定接口或对象,需手动编码实现;JDK动态代理通过反射机制实现,适用于所有接口;Cglib动态代理则基于字节码技术,无需接口支持,但需引入外部库。每种方法各有优缺点,选择时应根据具体需求考虑。
17 1
|
3月前
|
开发者 C# 容器
【独家揭秘】当WPF邂逅DirectX:看这两个技术如何联手打造令人惊艳的高性能图形渲染体验,从环境搭建到代码实践,一步步教你成为图形编程高手
【8月更文挑战第31天】本文通过代码示例详细介绍了如何在WPF应用中集成DirectX以实现高性能图形渲染。首先创建WPF项目并使用SharpDX作为桥梁,然后在XAML中定义承载DirectX内容的容器。接着,通过C#代码初始化DirectX环境,设置渲染逻辑,并在WPF窗口中绘制图形。此方法适用于从简单2D到复杂3D场景的各种图形处理需求,为WPF开发者提供了高性能图形渲染的技术支持和实践指导。
189 0
|
3月前
|
设计模式 Java C++
揭秘!JDK动态代理VS CGLIB:一场关于Java代理界的‘宫心计’,你站哪队?
【8月更文挑战第24天】Java 动态代理是一种设计模式,允许在不改动原类的基础上通过代理类扩展功能。主要实现方式包括 JDK 动态代理和 CGLIB。前者基于接口,利用反射机制在运行时创建代理类;后者采用继承方式并通过字节码技术生成子类实现类的代理。两者在实现机制、性能及适用场景上有明显差异。JDK 动态代理适用于有接口的场景,而 CGLIB 更适合代理未实现接口的类,尽管性能更优但存在一些限制。开发者可根据需求选择合适的代理方式。
162 0
|
2月前
|
Java
安装JDK18没有JRE环境的解决办法
安装JDK18没有JRE环境的解决办法
266 3
|
3月前
|
Java 关系型数据库 MySQL
"解锁Java Web传奇之旅:从JDK1.8到Tomcat,再到MariaDB,一场跨越数据库的冒险安装盛宴,挑战你的技术极限!"
【8月更文挑战第19天】在Linux上搭建Java Web应用环境,需安装JDK 1.8、Tomcat及MariaDB。本指南详述了使用apt-get安装OpenJDK 1.8的方法,并验证其版本。接着下载与解压Tomcat至`/usr/local/`目录,并启动服务。最后,通过apt-get安装MariaDB,设置基本安全配置。完成这些步骤后,即可验证各组件的状态,为部署Java Web应用打下基础。
56 1
|
3月前
|
Oracle Java 关系型数据库
Mac安装JDK1.8
Mac安装JDK1.8
663 4
|
4月前
|
Java Linux
Linux复制安装 jdk 环境
Linux复制安装 jdk 环境
103 3
|
1月前
|
Oracle Java 关系型数据库
jdk17安装全方位手把手安装教程 / 已有jdk8了,安装JDK17后如何配置环境变量 / 多个不同版本的JDK,如何配置环境变量?
本文提供了详细的JDK 17安装教程,包括下载、安装、配置环境变量的步骤,并解释了在已有其他版本JDK的情况下如何管理多个JDK环境。
330 0
|
3月前
|
Java 开发工具
开发工具系列 之 同一个电脑上安装多个版本的JDK
这篇文章介绍了如何在一台电脑上安装和配置多个版本的JDK,包括从官网下载所需JDK、安装过程、配置环境变量以及如何查看和切换当前使用的JDK版本,并提到了如果IDEA和JDK版本不兼容时的解决方法。
开发工具系列 之 同一个电脑上安装多个版本的JDK