一、背景

今天有小伙伴面试的时候被问到:Spring AOP中JDK 和 CGLib动态代理哪个效率更高?

二、基本概念

首先,我们知道Spring AOP的底层实现有两种方式:一种是JDK动态代理,另一种是CGLib的方式。

自Java 1.3以后,Java提供了动态代理技术,允许开发者在运行期创建接口的代理实例,后来这项技术被用到了Spring的很多地方。

JDK动态代理主要涉及java.lang.reflect包下边的两个类:Proxy和InvocationHandler。其中,InvocationHandler是一个接口,可以通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态地将横切逻辑和业务逻辑贬值在一起。

JDK动态代理的话,他有一个限制,就是它只能为接口创建代理实例,而对于没有通过接口定义业务方法的类,如何创建动态代理实例哪?答案就是CGLib。

CGLib采用底层的字节码技术,全称是:Code Generation Library,CGLib可以为一个类创建一个子类,在子类中采用方法拦截的技术拦截所有父类方法的调用并顺势织入横切逻辑。

三、JDK 和 CGLib动态代理区别

1、JDK动态代理具体实现原理:

  • 通过实现InvocationHandlet接口创建自己的调用处理器;

  • 通过为Proxy类指定ClassLoader对象和一组interface来创建动态代理;

  • 通过反射机制获取动态代理类的构造函数,其唯一参数类型就是调用处理器接口类型;

  • 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数参入;

JDK动态代理是面向接口的代理模式,如果被代理目标没有接口那么Spring也无能为力,Spring通过Java的反射机制生产被代理接口的新的匿名实现类,重写了其中AOP的增强方法。

2、CGLib动态代理:

CGLib是一个强大、高性能的Code生产类库,可以实现运行期动态扩展java类,Spring在运行期间通过 CGlib继承要被动态代理的类,重写父类的方法,实现AOP面向切面编程呢。

3、两者对比:

  • JDK动态代理是面向接口的。

  • CGLib动态代理是通过字节码底层继承要代理类来实现(如果被代理类被final关键字所修饰,那么抱歉会失败)。

4、使用注意:

  • 如果要被代理的对象是个实现类,那么Spring会使用JDK动态代理来完成操作(Spirng默认采用JDK动态代理实现机制);

  • 如果要被代理的对象不是个实现类那么,Spring会强制使用CGLib来实现动态代理。

四、JDK 和 CGLib动态代理性能对比-教科书上的描述

我们不管是看书还是看文章亦或是我那个上搜索参考答案,可能很多时候,都可以找到如下的回答:

关于两者之间的性能的话,JDK动态代理所创建的代理对象,在以前的JDK版本中,性能并不是很高,虽然在高版本中JDK动态代理对象的性能得到了很大的提升,但是他也并不是适用于所有的场景。主要体现在如下的两个指标中:

1、CGLib所创建的动态代理对象在实际运行时候的性能要比JDK动态代理高不少,有研究表明,大概要高10倍;

2、但是CGLib在创建对象的时候所花费的时间却比JDK动态代理要多很多,有研究表明,大概有8倍的差距;

3、因此,对于singleton的代理对象或者具有实例池的代理,因为无需频繁的创建代理对象,所以比较适合采用CGLib动态代理,反正,则比较适用JDK动态代理。

结果是不是如上边1、2、3条描述的那样哪?下边我们做一些小实验分析一下!

五、性能测试

1、首先有几个Java类

2、Target.java

  1. package com.java.proxy.test;
  2. public interface Target {
  3. int test(int i);
  4. }

3、TargetImpl.java

  1. package com.java.proxy.test;
  2. public class TargetImpl implements Target {
  3. @Override
  4. public int test(int i) {
  5. return i + 1;
  6. }
  7. }

4、JdkDynamicProxyTest.java

  1. package com.java.proxy.test;
  2. import java.lang.reflect.InvocationHandler;
  3. import java.lang.reflect.Method;
  4. import java.lang.reflect.Proxy;
  5. public class JdkDynamicProxyTest implements InvocationHandler {
  6. private Target target;
  7. private JdkDynamicProxyTest(Target target) {
  8. this.target = target;
  9. }
  10. public static Target newProxyInstance(Target target) {
  11. return (Target) Proxy.newProxyInstance(JdkDynamicProxyTest.class.getClassLoader(),
  12. new Class<?>[]{Target.class},
  13. new JdkDynamicProxyTest(target));
  14. }
  15. @Override
  16. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  17. return method.invoke(target, args);
  18. }
  19. }

5、CglibProxyTest.java

  1. package com.java.proxy.test;
  2. import org.springframework.cglib.proxy.Enhancer;
  3. import org.springframework.cglib.proxy.MethodInterceptor;
  4. import org.springframework.cglib.proxy.MethodProxy;
  5. import java.lang.reflect.Method;
  6. public class CglibProxyTest implements MethodInterceptor {
  7. private CglibProxyTest() {
  8. }
  9. public static <T extends Target> Target newProxyInstance(Class<T> targetInstanceClazz) {
  10. Enhancer enhancer = new Enhancer();
  11. enhancer.setSuperclass(targetInstanceClazz);
  12. enhancer.setCallback(new CglibProxyTest());
  13. return (Target) enhancer.create();
  14. }
  15. @Override
  16. public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
  17. return proxy.invokeSuper(obj, args);
  18. }
  19. }

6、ProxyPerformanceTest.java

  1. package com.java.proxy.test;
  2. import java.util.LinkedHashMap;
  3. import java.util.Map;
  4. public class ProxyPerformanceTest {
  5. public static void main(String[] args) {
  6. //创建测试对象
  7. Target nativeTest = new TargetImpl();
  8. Target dynamicProxy = JdkDynamicProxyTest.newProxyInstance(nativeTest);
  9. Target cglibProxy = CglibProxyTest.newProxyInstance(TargetImpl.class);
  10. //预热一下
  11. int preRunCount = 10000;
  12. runWithoutMonitor(nativeTest, preRunCount);
  13. runWithoutMonitor(cglibProxy, preRunCount);
  14. runWithoutMonitor(dynamicProxy, preRunCount);
  15. //执行测试
  16. Map<String, Target> tests = new LinkedHashMap<String, Target>();
  17. tests.put("Native ", nativeTest);
  18. tests.put("Dynamic ", dynamicProxy);
  19. tests.put("Cglib ", cglibProxy);
  20. int repeatCount = 3;
  21. int runCount = 1000000;
  22. runTest(repeatCount, runCount, tests);
  23. runCount = 50000000;
  24. runTest(repeatCount, runCount, tests);
  25. }
  26. private static void runTest(int repeatCount, int runCount, Map<String, Target> tests) {
  27. System.out.println(
  28. String.format("\n===== run test : [repeatCount=%s] [runCount=%s] [java.version=%s] =====",
  29. repeatCount, runCount, System.getProperty("java.version")));
  30. for (int i = 0; i < repeatCount; i++) {
  31. System.out.println(String.format("\n--------- test : [%s] ---------", (i + 1)));
  32. for (String key : tests.keySet()) {
  33. runWithMonitor(tests.get(key), runCount, key);
  34. }
  35. }
  36. }
  37. private static void runWithoutMonitor(Target target, int runCount) {
  38. for (int i = 0; i < runCount; i++) {
  39. target.test(i);
  40. }
  41. }
  42. private static void runWithMonitor(Target target, int runCount, String tag) {
  43. long start = System.currentTimeMillis();
  44. for (int i = 0; i < runCount; i++) {
  45. target.test(i);
  46. }
  47. long end = System.currentTimeMillis();
  48. System.out.println("[" + tag + "] Total Time:" + (end - start) + "ms");
  49. }
  50. }

7、测试结果

(1)JDK 1.6



(2)JDK 1.7

(3)JDK 1.8

经过多次试验,可以看出平均情况下的话,JDK动态代理的运行速度已经逐渐提高了,在低版本的时候,运行的性能可能不如CGLib,但是在1.8版本中运行多次,基本都可以得到一致的测试结果,那就是JDK动态代理已经比CGLib动态代理快了!

但是JDK动态代理和CGLib动态代理的适用场景还是不一样的哈!

六、总结

最终的测试结果大致是这样的,在1.6和1.7的时候,JDK动态代理的速度要比CGLib动态代理的速度要慢,但是并没有教科书上的10倍差距,在JDK1.8的时候,JDK动态代理的速度已经比CGLib动态代理的速度快很多了,希望小伙伴在遇到这个问题的时候能够有的放矢!

Spring AOP中的JDK和CGLib动态代理关于这个知识点很重要,关于两者之间性能的对比经过测试实验已经有了一个初步的结果,以后再有人问你Spring AOP,不要简单的说JDK动态代理和CGLib这两个了,是时候的可以抛出来对两者之间区别的理解,是有加分的哦!

参考文章:

1、https://blog.csdn.net/qq1723205668/article/details/56481476

2、https://blog.csdn.net/xiangbq/article/details/49794335

Spring AOP中的JDK和CGLib动态代理哪个效率更高?的更多相关文章

  1. Spring AOP中的JDK和CGLIB动态代理

    Spring在将Advice织入目标对象的Joinpoint是在运行时动态进行的.它采用的方式可能有两种,即JDK动态代理与CGLIB代理.Spring会根据具体的情况在两者之间切换. 实际情况如下: ...

  2. Spring框架中的JDK与CGLib动态代理

    JDK和CGLib动态代理区别 JDK动态代理:利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生成一个实现代理接口的匿名类, 在调用具体方法前调用InvokeHandler ...

  3. spring框架中JDK和CGLIB动态代理区别

    转载:https://blog.csdn.net/yhl_jxy/article/details/80635012 前言JDK动态代理实现原理(jdk8):https://blog.csdn.net/ ...

  4. 动态代理的两种方式,以及区别(静态代理、JDK与CGLIB动态代理、AOP+IoC)

    Spring学习总结(二)——静态代理.JDK与CGLIB动态代理.AOP+IoC   目录 一.为什么需要代理模式 二.静态代理 三.动态代理,使用JDK内置的Proxy实现 四.动态代理,使用cg ...

  5. 【java高级编程】JDK和CGLIB动态代理区别

    转载:https://blog.csdn.net/yhl_jxy/article/details/80635012 前言 JDK动态代理实现原理(jdk8):https://blog.csdn.net ...

  6. JDK、CGlib动态代理详解

    Java动态代理之JDK实现和CGlib实现(简单易懂)      一 JDK和CGLIB动态代理原理 1.JDK动态代理 利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生 ...

  7. JDK和CGLIB动态代理区别

    背景:虽然自己了解这两种代理的区别,但是面试时候还是答的很模糊,需要好好总结. 前言JDK动态代理实现原理(jdk8):https://blog.csdn.net/yhl_jxy/article/de ...

  8. JDK和CGLIB动态代理原理

    1.JDK动态代理利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生成一个实现代理接口的匿名类, 在调用具体方法前调用InvokeHandler来处理. 2.CGLiB动态代 ...

  9. JDK和CGLIB动态代理原理区别

    JDK和CGLIB动态代理原理区别 https://blog.csdn.net/yhl_jxy/article/details/80635012 2018年06月09日 18:34:17 阅读数:65 ...

随机推荐

  1. NOIP-螺旋矩阵

    题目描述 一个 n 行 n 列的螺旋矩阵可由如下方法生成: 从矩阵的左上角(第 1 行第 1 列)出发,初始时向右移动:如果前方是未曾经过的格子,则继续前进,否则右转:重复上述操作直至经过矩阵中所有格 ...

  2. 用挂载,使用NTFS移动硬盘,拷贝iPhone里的照片,拷到MAC

    2. 写权限挂载移动硬盘 1) mount查看 2) diskutil umount /dev/disk2 3) sudo mount_ntfs -o rw,nobrowse /dev/disk2s1 ...

  3. Openstack的视频学习

    0.安装环境准备 部署架构: 网络模式(红色Net0为管理网络,Net1接外网,Net2是接虚拟机网络流量的): 虚拟化平台为VirtualBox,虚拟网络Host-Only网络的配置: Net0:管 ...

  4. 【高并发架构】Redis特点及构件模型

    数据结构 redis 相比 memcached 来说,拥有更多的数据结构,能支持更丰富的数据操作.如果需要缓存能够支持更复杂的结构和操作, redis 会是不错的选择. redis 主要有以下几种数据 ...

  5. QEMU KVM Libvirt手册(10):Managing Virtual Machines with libvirt

    libvirt is a library that provides a common API for managing popular virtualization solutions, among ...

  6. NET Core微服务之路:基于Ocelot的API网关Relay实现--RPC篇

    前言 我们都知道,API网关是工作在应用层上网关程序,为何要这样设计呢,而不是将网关程序直接工作在传输层.或者网络层等等更底层的环境呢?让我们先来简单的了解一下TCP/IP的五层模型.     (图片 ...

  7. 体验一把做黑客的感觉-IPC$入侵之远程控制

    前言 一看你就是看标题进来的,我可不是标题党啊,大家往下看吧,本文章主要介绍了利用IPC共享漏洞上传并执行木马. 基础知识 一.什么是IPC 进程间通信(IPC,Inter-Process Commu ...

  8. [Swift]LeetCode40. 组合总和 II | Combination Sum II

    Given a collection of candidate numbers (candidates) and a target number (target), find all unique c ...

  9. [Swift]LeetCode68. 文本左右对齐 | Text Justification

    Given an array of words and a width maxWidth, format the text such that each line has exactly maxWid ...

  10. [Swift]LeetCode290. 单词模式 | Word Pattern

    Given a pattern and a string str, find if str follows the same pattern. Here follow means a full mat ...