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。


相关文章
|
1月前
|
安全 Java 开发者
AOP中的JDK动态代理与CGLIB动态代理:深度解析与实战模拟
【11月更文挑战第21天】面向切面编程(AOP,Aspect-Oriented Programming)是一种编程范式,它通过将横切关注点(cross-cutting concerns)与业务逻辑分离,以提高代码的可维护性和可重用性。在Java开发中,AOP的实现离不开动态代理技术,其中JDK动态代理和CGLIB动态代理是两种常用的方式。本文将从背景、历史、功能点、业务场景、底层逻辑等多个维度,深度解析这两种代理方式的区别,并通过Java示例进行模拟和比较。
71 4
|
2月前
|
设计模式 Java API
[Java]静态代理与动态代理(基于JDK1.8)
本文介绍了代理模式及其分类,包括静态代理和动态代理。静态代理分为面向接口和面向继承两种形式,分别通过手动创建代理类实现;动态代理则利用反射技术,在运行时动态创建代理对象,分为JDK动态代理和Cglib动态代理。文中通过具体代码示例详细讲解了各种代理模式的实现方式和应用场景。
38 0
[Java]静态代理与动态代理(基于JDK1.8)
|
2月前
|
Java
【编程进阶知识】静态代理、JDK动态代理及Cglib动态代理各自存在的缺点及代码示例
本文介绍了三种Java代理模式:静态代理、JDK动态代理和Cglib动态代理。静态代理针对特定接口或对象,需手动编码实现;JDK动态代理通过反射机制实现,适用于所有接口;Cglib动态代理则基于字节码技术,无需接口支持,但需引入外部库。每种方法各有优缺点,选择时应根据具体需求考虑。
27 1
|
4月前
|
开发者 C# 容器
【独家揭秘】当WPF邂逅DirectX:看这两个技术如何联手打造令人惊艳的高性能图形渲染体验,从环境搭建到代码实践,一步步教你成为图形编程高手
【8月更文挑战第31天】本文通过代码示例详细介绍了如何在WPF应用中集成DirectX以实现高性能图形渲染。首先创建WPF项目并使用SharpDX作为桥梁,然后在XAML中定义承载DirectX内容的容器。接着,通过C#代码初始化DirectX环境,设置渲染逻辑,并在WPF窗口中绘制图形。此方法适用于从简单2D到复杂3D场景的各种图形处理需求,为WPF开发者提供了高性能图形渲染的技术支持和实践指导。
315 0
|
3月前
|
Java
安装JDK18没有JRE环境的解决办法
安装JDK18没有JRE环境的解决办法
382 3
|
5天前
|
NoSQL 关系型数据库 MySQL
Linux安装jdk、mysql、redis
Linux安装jdk、mysql、redis
76 7
|
4月前
|
Oracle Java 关系型数据库
Mac安装JDK1.8
Mac安装JDK1.8
788 4
|
4月前
|
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应用打下基础。
66 1
|
1月前
|
Oracle Java 关系型数据库
安装 JDK 时应该注意哪些问题
选择合适的JDK版本需考虑项目需求与兼容性,推荐使用LTS版本如JDK 17或21。安装时注意操作系统适配,配置环境变量PATH和JAVA_HOME,确保合法使用许可证,并进行安装后测试以验证JDK功能正常。
55 1
|
1月前
|
IDE Java 编译器
开发 Java 程序一定要安装 JDK 吗
开发Java程序通常需要安装JDK(Java Development Kit),因为它包含了编译、运行和调试Java程序所需的各种工具和环境。不过,某些集成开发环境(IDE)可能内置了JDK,或可使用在线Java编辑器,无需单独安装。
77 1