Spring AOP中的JDK和CGLib * 哪个效率更高?

作者:徐刘根 时间:2023-07-02 18:42:50 

一、背景

今天有小伙伴面试的时候被问到: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类

Spring AOP中的JDK和CGLib * 哪个效率更高?

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 = 10000;
   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 = 1000000;
   runTest(repeatCount, runCount, tests);
   runCount = 50000000;
   runTest(repeatCount, runCount, tests);
 }
 private static void runTest(int repeatCount, int runCount, Map<String, Target> tests) {
   System.out.println(
       String.format("\n===== 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("\n--------- 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

Spring AOP中的JDK和CGLib * 哪个效率更高?

Spring AOP中的JDK和CGLib * 哪个效率更高?

(2)JDK 1.7

Spring AOP中的JDK和CGLib * 哪个效率更高?

Spring AOP中的JDK和CGLib * 哪个效率更高?

(3)JDK 1.8

Spring AOP中的JDK和CGLib * 哪个效率更高?

Spring AOP中的JDK和CGLib * 哪个效率更高?

经过多次试验,可以看出平均情况下的话,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这两个了,是时候的可以抛出来对两者之间区别的理解,是有加分的哦!

来源:https://blog.csdn.net/xlgen157387/article/details/82497594

标签:spring,aop,jdk,cglib, , ,效率
0
投稿

猜你喜欢

  • java实现短信验证码5分钟有效时间

    2023-08-30 18:42:26
  • Android Camera开发实现可复用的相机组件

    2023-04-08 20:34:56
  • 实现activity管理器一次退出所有activity

    2022-11-20 10:15:23
  • MVPXlistView展示上拉下拉效果

    2022-10-30 10:45:38
  • C#使用GDI+创建缩略图实例

    2023-02-21 01:57:16
  • Spring Boot Admin 进行项目监控管理的方法

    2021-09-01 23:39:19
  • Java数据结构及算法实例:朴素字符匹配 Brute Force

    2022-01-10 15:03:40
  • 浅析Android中build.gradle的实用技巧

    2022-05-03 15:15:40
  • Java thread.isInterrupted() 返回值不确定结果分析解决

    2023-11-09 19:27:09
  • c#图像截取实例

    2022-02-21 07:49:58
  • SpringMVC 域对象共享数据的实现示例

    2021-07-18 15:45:57
  • Java如何基于ProcessBuilder类调用外部程序

    2023-11-27 20:19:57
  • 使用VS2010 C#开发ActiveX控件(下),完整代码打包下载

    2022-08-20 18:05:29
  • Eclipse最新版使用过程中遇到的问题总结

    2023-12-03 04:14:14
  • Java编程调用微信分享功能示例

    2022-10-16 06:39:49
  • Kotlin封装RecyclerView Adapter实例教程

    2023-11-06 01:54:23
  • c# 实现MD5,SHA1,SHA256,SHA512等常用加密算法源代码

    2021-11-22 22:18:24
  • 浅谈让@Value更方便的Spring自定义转换类

    2023-10-06 14:01:02
  • 一文带你认识Java中的Object类和深浅拷贝

    2023-02-05 16:52:08
  • 详解java Collections.sort的两种用法

    2023-11-28 09:30:31
  • asp之家 软件编程 m.aspxhome.com