CGLib 和 JDK 动态代理对比
1.JDK 动态代理是实现了被代理对象的接口,CGLib 是继承了被代理对象。
2.JDK 和 CGLib 都是在运行期生成字节码,JDK 是直接写 Class 字节码,CGLib 使用 ASM
框架写 Class 字节码,Cglib 代理实现更复杂,生成代理类比 JDK 效率低。
3.JDK 调用代理方法,是通过反射机制调用,CGLib 是通过 FastClass 机制直接调用方法,
CGLib 执行效率更高
4.cglib实现是实现MethodInterceptor接口的intercept方法 jdk是实现InvocationHandler
接口的invoke方法
JDK Proxy
我们都知道 JDK Proxy 采用字节重组,重新生的对象来替代原始的对象以达到动态代理
的目的。JDK Proxy 生成对象的步骤如下:
1、拿到被代理对象的引用,并且获取到它的所有的接口,反射获取。
2、JDK Proxy 类重新生成一个新的类、同时新的类要实现被代理类所有实现的所有的接口。
3、动态生成 Java 代码,把新加的业务逻辑方法由一定的逻辑代码去调用(在代码中体现)。
4、编译新生成的 Java 代码.class 生成$Proxy0.class
5、再重新加载到 JVM 中运行。
我们发现$Proxy0 继承了 Proxy 类,同时还实现了我们的 Person(代理对象的接口) 接口,而且重写了
findLove()(接口中的方法)等方法。而且在静态块中用反射查找到了目标对象的所有方法,而且保存了所
有方法的引用,在重写的方法用反射调用目标对象的方法。小伙伴们此时一定在好奇,
这些代码是哪里来的呢?其实是 JDK 帮我们自动生成的。现在,我们不依赖 JDK 自己来
动态生成源代码、动态完成编译,然后,替代目标对象并执行
CGLib 动态代理执行代理方法效率之所以比 JDK 的高
因为 Cglib 采用了 FastClass 机
制,它的原理简单来说就是:为代理类和被代理类各生成一个 Class,这个 Class 会为代
理类或被代理类的方法分配一个 index(int 类型)。这个 index 当做一个入参,FastClass
就可以直接定位要调用的方法直接进行调用,这样省去了反射调用,所以调用效率比 JDK
动态代理通过反射调用高
FastClass 并不是跟代理类一块生成的,而是在第一次执行 MethodProxy
重新执行代码,我们会发现在 E://cglib_proxy_class 目录下多了三个 class 文件,如图:
通过调试跟踪,我们发现 Customer$$EnhancerByCGLIB$$3feeb52a.class 就是 CGLib
生成的代理类,继承了 Customer 类。反编译后代码是这样的
代理类重写了 Customer 类的所有方法。我们通过代理类的源码可以看到,代理类会获得所有
在 父 类 继 承 来 的 方 法 , 并 且 会 有 MethodProxy 与 之 对 应 , 比 如 Method
CGLIB$findLove$0$Method、MethodProxy CGLIB$findLove$0$Proxy;这些方法在代
理类的 findLove()中都有调用
调 用 过 程 : 代 理 对 象 调 用 this.findLove() 方 法 -> 调 用 拦 截 器
->methodProxy.invokeSuper->CGLIB$findLove$0->被代理对象 findLove()方法。
此时,我们发现拦截器 MethodInterceptor 中就是由 MethodProxy 的 invokeSuper
方法调用代理方法的,MethodProxy 非常关键,我们分析一下它具体做了什么
上面代码调用过程就是获取到代理类对应的 FastClass,并执行了代理方法。还记得之前
生成三个 class 文件吗?
Customer$$EnhancerByCGLIB$$3feeb52a$$FastClassByCGLIB$$6aad62f1.class就
是代理类的 FastClass,
Customer$$FastClassByCGLIB$$2669574a.class 就是被代理类的 FastClass