Spring AOP中的JDK和CGLib动态代理哪个效率更高?

JAVA 2023-07-05 17:29:38
59阅读

一、情况

  今天有小伙伴们招聘面试的情况下被问起:Spring AOP中JDK 和 CGLib动态代理哪一个高效率高些?

二、基本要素

  最先,我们知道Spring AOP的最底层完成有二种方法:一种是JDK动态代理,另一种是CGLib的方法。

  自Java 1.3之后,Java出示了动态代理技术性,容许开发人员在运作期创建接口的代理案例,之后此项技术性被采用了Spring的许多 地区。

  JDK动态代理关键涉及到java.lang.reflect包下面的2个类:Proxy和InvocationHandler。在其中,InvocationHandler是一个接口,能够根据完成该接口界定横切面逻辑性,并根据反射机制启用总体目标类的编码,动态性地将横切面逻辑性和领域模型掉价在一起。

  JDK动态代理得话,他有一个限定,便是它只有为接口创建代理案例,而针对沒有根据接口界定业务流程方式的类,怎样创建动态代理案例哪?回答便是CGLib。

  CGLib选用最底层的字节码技术性,全名是:Code Generation Library,CGLib能够为一个类创建一个派生类,在派生类中选用方式阻拦的技术性阻拦全部父类方法的启用并趁机织入横切面逻辑性。

三、JDK 和 CGLib动态代理差别

  1、JDK动态代理实际完成基本原理:

  • 根据完成InvocationHandlet接口创建自身的启用CPU;
  • 根据为Proxy类特定ClassLoader对象和一组interface来创建动态代理;
  • 根据反射机制获得动态代理类的构造方法,其唯一主要参数种类便是启用CPU接口种类;
  • 根据构造方法创建动态代理类案例,结构时启用CPU对象做为主要参数参与;

  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动态代理对象的性能获得了非常大的提高,可是他也并并不是适用全部的情景。关键反映在以下的2个指标值中:

  1、CGLib所创建的动态代理对象在具体运作情况下的性能要比JDK动态代理高许多,有研究表明,大约要高10倍;

  2、可是CGLib在创建对象的情况下所花销的時间却比JDK动态代理要多许多 ,有研究表明,大约有8倍的差别;

  3、因而,针对singleton的代理对象或是具备案例池的代理,由于不用经常的创建代理对象,因此 较为合适选用CGLib动态代理,总之,则较为可用JDK动态代理。

  結果是否如上面1、2、3条叙述的那般哪?下面大家做一些小实验剖析一下!

五、性能检测

  1、最先几个Java类

  

  

  2、Target.java

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

  3、TargetImpl.java

  package com.java.proxy.test;public class TargetImpl implements Target { @Override public int test(int i) { return i 1; }}

  4、JdkDynamicProxyTest.java

  package com.java.proxy.test;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class JdkDynamicProxyTest implements InvocationHandler { private Target target; private JdkDynamicProxyTest(Target target) { this.target = target; } public static Target newProxyInstance(Target target) { return (Target) Proxy.newProxyInstance(JdkDynamicProxyTest.class.getClassLoader(), new Class<?>[]{Target.class}, new JdkDynamicProxyTest(target)); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return method.invoke(target, args); }}

  5、CglibProxyTest.java

  package com.java.proxy.test;import org.springframework.cglib.proxy.Enhancer;import org.springframework.cglib.proxy.MethodInterceptor;import org.springframework.cglib.proxy.MethodProxy;import java.lang.reflect.Method;public class CglibProxyTest implements MethodInterceptor { private CglibProxyTest() { } public static <T extends Target> Target newProxyInstance(Class<T> targetInstanceClazz) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(targetInstanceClazz); enhancer.setCallback(new CglibProxyTest()); return (Target) enhancer.create(); } @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { return proxy.invokeSuper(obj, args); }}

  6、ProxyPerformanceTest.java

  package com.java.proxy.test;import java.util.LinkedHashMap;import java.util.Map;public class ProxyPerformanceTest { public static void main(String[] args) { //创建检测对象 Target nativeTest = new TargetImpl(); Target dynamicProxy = JdkDynamicProxyTest.newProxyInstance(nativeTest); Target cglibProxy = CglibProxyTest.newProxyInstance(TargetImpl.class); //加热一下 int preRunCount = runWithoutMonitor(nativeTest, preRunCount); runWithoutMonitor(cglibProxy, preRunCount); runWithoutMonitor(dynamicProxy, preRunCount); //实行检测 Map<String, Target> tests = new LinkedHashMap<String, Target>(); tests.put("Native ", nativeTest); tests.put("Dynamic ", dynamicProxy); tests.put("Cglib ", cglibProxy); int repeatCount = 3; int runCount = runTest(repeatCount, runCount, tests); runCount = runTest(repeatCount, runCount, tests); } private static void runTest(int repeatCount, int runCount, Map<String, Target> tests) { System.out.println( String.format("===== run test : [repeatCount=%s] [runCount=%s] [java.version=%s] =====", repeatCount, runCount, System.getProperty("java.version"))); for (int i = 0; i < repeatCount; i ) { System.out.println(String.format("--------- test : [%s] ---------", (i 1))); for (String key : tests.keySet()) { runWithMonitor(tests.get(key), runCount, key); } } } private static void runWithoutMonitor(Target target, int runCount) { for (int i = 0; i < runCount; i++) { target.test(i); } } private static void runWithMonitor(Target target, int runCount, String tag) { long start = System.currentTimeMillis(); for (int i = 0; i < runCount; i++) { target.test(i); } long end = System.currentTimeMillis(); System.out.println("[" + tag + "] Total Time:" + (end - start) + "ms"); }}

  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://>668/article/details/span> 2、https://rticle/details/span>


  搜索或扫描下述二维码关注微信公众号:Java后端技术(ID: JavaITWork),和20万人一起学Java!

  Java后端技术专注Java相关技术:SSM、Spring全家桶、微服务、MySQL、MyCat、集群、分布式、中间件、Linux、网络、多线程,偶尔讲点运维Jenkins、Nexus、Docker、ELK,偶尔分享些技术干货,致力于Java全栈开发!

  

the end
免责声明:本文不代表本站的观点和立场,如有侵权请联系本站删除!本站仅提供信息存储空间服务。