静态代理与JDK动态代理与CGLIB动态代理(下)

简介: 静态代理与JDK动态代理与CGLIB动态代理(下)

我们看看MethodProxy的内部

public Object invoke(Object obj, Object[] args) throws Throwable {
        try {
            this.init();
            MethodProxy.FastClassInfo fci = this.fastClassInfo;
            return fci.f1.invoke(fci.i1, obj, args);
        } catch (InvocationTargetException var4) {
            throw var4.getTargetException();
        } catch (IllegalArgumentException var5) {
            if(this.fastClassInfo.i1 < 0) {
                throw new IllegalArgumentException("Protected method: " + this.sig1);
            } else {
                throw var5;
            }
        }
    }
    public Object invokeSuper(Object obj, Object[] args) throws Throwable {
        try {
            this.init();
            MethodProxy.FastClassInfo fci = this.fastClassInfo;
            return fci.f2.invoke(fci.i2, obj, args);
        } catch (InvocationTargetException var4) {
            throw var4.getTargetException();
        }
    }
    ///fastclass
  private static class FastClassInfo {
        FastClass f1;
        FastClass f2;
        int i1;
        int i2;
        private FastClassInfo() {
        }
    }

这里有invoke()与invokeSuper()。为啥上面要调用invokeSuper()不调用invoke()呢?


这里就要涉及到CGLB的FastClass机制。 FastClass.机制:为代理类和被代理类各生成一个Class。这个Class会为代理类或被代理类的方法分配一个index(int类型)索引。通过这个索引可以直接定位到要调用的方法,省去了反射。 具体推荐阅读参考1参考2

这里我们只需知道结果:MethodProxy.FastClassInfo 属性的最终值会是

private static class FastClassInfo {
        FastClass f1;//被代理类UserNoInterface的FastClass
        FastClass f2;//代理类UserNoInterface?EnhancerByCGLIB?b3361405的FastClass
        int i1;//被代理类的doSomething()的索引
        int i2;//代理类CGLIB$doSomething$0()(内部调用被代理对象的方法)的索引。
        private FastClassInfo() {
        }
    }


三个文件分别为:

  • UserNoInterface?EnhancerByCGLIB?b3361405?FastClassByCGLIB?fc90c93c《代理类的FastClass》
  • UserNoInterface?EnhancerByCGLIB?b3361405《 cglb生成的代理类》
  • UserNoInterface?FastClassByCGLIB?29e52466《被代理类的FastClass》

我们在回头看看invoke(),invokeSuper()方法

  • invoke() 调用的是fci.f1.invoke(fci.i1, obj, args);也就是调用的是被代理类UserNoInterface的FastClass的invoke方法。
  • invokeSuper()调用的是fci.f2.invoke(fci.i2, obj, args);也就是调用的是代理类UserNoInterface?EnhancerByCGLIB?b3361405的FastClass的invoke方法.
//=================被代理对象的fastclass的invoke方法
public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
        UserNoInterface var10000 = (UserNoInterface)var2;
        int var10001 = var1;
        try {
            switch(var10001) {
            case 0:
                var10000.doSomething();
                return null;
            }
        } catch (Throwable var4) {
            throw new InvocationTargetException(var4);
        }
        throw new IllegalArgumentException("Cannot find matching method/constructor");
    }
//=================代理对象的fastclass的invoke方法
public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
        b3361405 var10000 = (b3361405)var2;
        int var10001 = var1;
        try {
            switch(var10001) {
            case 7:
                var10000.doSomething();
                return null;
            case 15:
                var10000.CGLIB$doSomething$0();
                return null;
            }
        } catch (Throwable var4) {
            throw new InvocationTargetException(var4);
        }
        throw new IllegalArgumentException("Cannot find matching method/constructor");
    }

我们看到如果调用了MethodProxy.invoke() 会调用被代理对象的fastclass的invoke方法,会调用var10000.doSomething()。而var10000其实就是代理对象,这样就出现了死循环。 这也是网上为啥说的不能在此处使用MethodProxy.invoke()的原因。

看看最终的调用链:

代理对象.doSomething()--->拦截器.intercept()--->MethodProxy.invokeSuper(sub, objects)--->代理类的FastClass类对象的.invoke()--->代理对象的CGLIB$doSomething$0()方法--->目标对象的方法doSomething()

小结:

  • CGLB动态代理可以代理没有实现接口的类
  • CGLB动态代理通过Enhancer 实现代理功能
  • CGLB动态代理生成一个目标对象的子类。


总结:


  • 静态代理从源头解决代理问题
  • 动态代理从运行使用时解决代理问题。
  • JDK动态代理通过InvocationHandler实现拦截。
  • GCLB通过MethodInterceptor实现拦截。

重点

1.JDK动态是通过反射来实现的。两个要素是Proxy+InvocationHandler

  • Proxy 用于创建代理,并且内存中创建的类也继承Proxy
  • InvocationHandler增强器,实现对目标方法的增强。

2.CGLB动态代理:两要素Enhancer + MethodInterceptor(CallBack)

  • Enhancer创建代理
  • MethodInterceptor 增强器, 实现对目标方法增强。

3.JDK代理与CGLB动态代理都在内存中生成新的字节码,最终还是落在Class对象上,

要素是手段,字节码才是目的。JVM并不关心你java代码,他关心的是在内存中可被使用的字节码。

欢迎大家关注我的公众号【源码行动】,最新个人理解及时奉送。


相关文章
|
1月前
|
Java API 开发者
Jdk动态代理为啥不能代理Class?
该文章主要介绍了JDK动态代理的原理以及为何JDK动态代理不能代理Class。
Jdk动态代理为啥不能代理Class?
|
1月前
|
Java
Spring5入门到实战------9、AOP基本概念、底层原理、JDK动态代理实现
这篇文章是Spring5框架的实战教程,深入讲解了AOP的基本概念、如何利用动态代理实现AOP,特别是通过JDK动态代理机制在不修改源代码的情况下为业务逻辑添加新功能,降低代码耦合度,并通过具体代码示例演示了JDK动态代理的实现过程。
Spring5入门到实战------9、AOP基本概念、底层原理、JDK动态代理实现
|
17天前
|
开发者 C# 容器
【独家揭秘】当WPF邂逅DirectX:看这两个技术如何联手打造令人惊艳的高性能图形渲染体验,从环境搭建到代码实践,一步步教你成为图形编程高手
【8月更文挑战第31天】本文通过代码示例详细介绍了如何在WPF应用中集成DirectX以实现高性能图形渲染。首先创建WPF项目并使用SharpDX作为桥梁,然后在XAML中定义承载DirectX内容的容器。接着,通过C#代码初始化DirectX环境,设置渲染逻辑,并在WPF窗口中绘制图形。此方法适用于从简单2D到复杂3D场景的各种图形处理需求,为WPF开发者提供了高性能图形渲染的技术支持和实践指导。
43 0
|
24天前
|
设计模式 Java C++
揭秘!JDK动态代理VS CGLIB:一场关于Java代理界的‘宫心计’,你站哪队?
【8月更文挑战第24天】Java 动态代理是一种设计模式,允许在不改动原类的基础上通过代理类扩展功能。主要实现方式包括 JDK 动态代理和 CGLIB。前者基于接口,利用反射机制在运行时创建代理类;后者采用继承方式并通过字节码技术生成子类实现类的代理。两者在实现机制、性能及适用场景上有明显差异。JDK 动态代理适用于有接口的场景,而 CGLIB 更适合代理未实现接口的类,尽管性能更优但存在一些限制。开发者可根据需求选择合适的代理方式。
61 0
|
3月前
|
设计模式 Java 程序员
java动态代理(JDK和cglib)
java动态代理(JDK和cglib)
23 0
|
2月前
|
Oracle Java 关系型数据库
入职必会-开发环境搭建41-Linux软件安装-安装JDK
本文介绍了在Linux系统中下载和安装JDK
入职必会-开发环境搭建41-Linux软件安装-安装JDK
|
30天前
|
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应用打下基础。
38 1
|
1月前
|
Oracle Java 关系型数据库
Mac安装JDK1.8
Mac安装JDK1.8
267 4
|
2月前
|
Java Linux
Linux复制安装 jdk 环境
Linux复制安装 jdk 环境
47 3
|
27天前
|
Java 开发工具
开发工具系列 之 同一个电脑上安装多个版本的JDK
这篇文章介绍了如何在一台电脑上安装和配置多个版本的JDK,包括从官网下载所需JDK、安装过程、配置环境变量以及如何查看和切换当前使用的JDK版本,并提到了如果IDEA和JDK版本不兼容时的解决方法。
开发工具系列 之 同一个电脑上安装多个版本的JDK