静态代理与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代码,他关心的是在内存中可被使用的字节码。

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


相关文章
|
7月前
|
Java Spring
JDK动态代理和CGLIB动态代理的区别
Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理: ● JDK动态代理只提供接口的代理,不支持类的代理Proxy.newProxyInstance(类加载器, 代理对象实现的所有接口, 代理执行器) ● CGLIB是通过继承的方式做的动态代理 , 如果某个类被标记为final,那么它是无法使用 CGLIB做动态代理的。Enhancer.create(父类的字节码对象, 代理执行器)
|
7月前
|
监控 Java API
JDK动态代理和CGLIB动态代理
Java动态代理允许在运行时创建代理对象,增强或拦截目标类方法的执行。主要通过两种方式实现:JDK动态代理和CGLIB动态代理。JDK动态代理基于接口,利用`java.lang.reflect.Proxy`类和`InvocationHandler`接口;CGLIB则通过字节码技术生成目标类的子类作为代理,适用于未实现接口的类。两者均用于在方法执行前后添加额外逻辑,如日志记录、权限控制等,广泛应用于AOP框架中。
215 2
|
8月前
|
监控 Java API
JDK动态代理和CGLIB动态代理
Java动态代理允许在运行时创建代理对象,增强或拦截目标类的方法调用,无需修改原代码。它有两种主要实现方式:JDK动态代理和CGLIB动态代理。 - **JDK动态代理**:通过`java.lang.reflect.Proxy`类和`InvocationHandler`接口实现,适用于实现了接口的类。它在方法调用前后插入额外逻辑,如日志记录、权限控制等。 - **CGLIB动态代理**:基于字节码技术,为未实现接口的类生成子类作为代理,重写父类方法以添加增强逻辑。适用于没有接口的类,但要求目标类不能是`final`类或方法。
100 1
|
3月前
|
存储 Ubuntu 安全
在Ubuntu 16.04上安装openjdk-6/7/8-jdk的步骤
在整个安装过程中,你可能需要管理员权限,因此你可能要使用 `sudo` 来获取必要的权限。记得做完每一个步骤后,都要检查输出,以确保没有发生错误,并且每项操作都成功完成。如果在安装过程中遇到问题,查看 `/var/log/` 下的日志文件对于问题的解决可能是有帮助的。
164 21
|
3月前
|
IDE Ubuntu Java
在Ubuntu18.04安装兼容JDK 8的Eclipse集成开发环境的指南。
完成以上步骤后,您将在Ubuntu 18.04系统上成功安装并配置了Eclipse IDE,它将与JDK 8兼容,可以开始进行Java开发工作。如果遇到任何问题,请确保每一步骤都正确执行,并检查是否所有路径都与您的具体情况相匹配。
122 11
|
2月前
|
Ubuntu Java Android开发
在Ubuntu 18.04上安装与JDK 8兼容的Eclipse版本的步骤。
安装过程结束后,您就可以开始使用Eclipse来开发您的Java项目了,并且确保它与JDK 8兼容无误。这个过程涉及的是一个基本的安装流程,针对使用Java 8的用户,Eclipse的其他配置和插件安装根据个人开发环境和需求来定制。
164 0
|
5月前
|
Java 关系型数据库 MySQL
在Linux平台上进行JDK、Tomcat、MySQL的安装并部署后端项目
现在,你可以通过访问http://Your_IP:Tomcat_Port/Your_Project访问你的项目了。如果一切顺利,你将看到那绚烂的胜利之光照耀在你的项目之上!
302 41
|
6月前
|
Oracle Java 关系型数据库
Tomcat和JDK的详细安装、下载和环境配置指南
以上就是JDK和Tomcat的下载、安装和环境配置的详细步骤。希望这个指南能帮助你顺利完成设置。
397 32
安装JDK18没有JRE环境的解决办法
安装JDK18没有JRE环境的解决办法
1037 61
|
5月前
|
开发框架 Java 关系型数据库
在Linux系统中安装JDK、Tomcat、MySQL以及部署J2EE后端接口
校验时,浏览器输入:http://[your_server_IP]:8080/myapp。如果你看到你的应用的欢迎页面,恭喜你,一切都已就绪。
399 17