写点什么

Spring 核心容器:AOP 代理机制对性能的影响案例分析

  • 2023-06-17
    湖南
  • 本文字数:2055 字

    阅读完需:约 7 分钟

AOP 代理机制对性能的影响案例分析

在讨论动态代理机制时,一个不可避免的话题是性能。无论采用 JDK 动态代理还是 CGLIB 动态代理,本质上都是在原有目标对象上进行了封装和转换,这个过程需要消耗资源和性能。而 JDK 动态代理和 CGLIB 动态代理的内部实现过程本身也存在很大的差异性。本节将讨论这两种动态代理机制对系统运行性能所带来的影响。

测试案例设计

为了量化不同动态代理机制对性能的影响程度,我们将设计一个案例,在该案例中同样使用前面介绍的 AccountService 接口以及它的实现类 AccountServiceImpl。我们将通过创建一定数量的 AccountServiceImpl 实例并调用它的 doAccountTransaction()方法,触发动态代理机制。


为了能够基于不同的代理机制来创建代理对象,需要引入 Spring 中一个非常有用的注解,即 @Scope 注解。我们已经在第 2 章中了解到该注解可以用来设置 Bean 的作用域。其实,@Scope 注解还可以用来指定代理模式 ScopedProxyMode。在 Spring 中,Scoped-ProxyMode 是一个枚举,代码如下所示:

public enum ScopedProxyMode {	DEFAULT,NO,INTERFACES,TARGET_CLASS;}
复制代码

请注意,ScopedProxyMode 中的 INTERFACES 代表的就是 JDK 动态代理,而 TARGET_CLASS 使用的则是 CGLIB 动态代理。


现在,让我们创建两个配置类 JDKProxyConfig 和 CGLIBProxyConfig,分别针对 AccountServiceImpl 指定两种不同的代理机制。其中,JDKProxyConfig 代码如下所示:

@Configuration@EnableAspectJAutoProxypublic class JDKProxyConfig {	@Bean	@Scope(proxyMode=ScopedProxyMode.INTERFACES)	public AccountService accountService(){		return new AccountServiceImpl();	}}
复制代码

而 CGLIBProxyConfig 则如下所示:

@Configuration@EnableAspectJAutoProxypublic class CGLIBProxyAppConfig {	@Bean	@Scope(proxyMode=ScopedProxyMode.TARGET_CLASS)	public AccountService accountService(){		return new AccountServiceImpl();	}}
复制代码

借助于这两个配置文件,我们就可以通过 AnnotationConfigApplicationContext 这个基于注解配置的应用上下文对象来获取添加了不同代理机制的 AccountServiceImpl 对象,实现方式如下所示:

//基于JDKProxyConfig获取AccountServiceImpl对象AccountService accountService = new AnnotationConfigApplicationContext(JDKProxyConfig.class).getBean(AccountService.class);//基于CGLIBProxyConfig获取AccountServiceImpl对象AccountService accountService = new AnnotationConfigApplicationContext(CGLIBProxyConfig.class).getBean(AccountService.class);
复制代码

现在,准备工作已经完成,让我们编写一个测试用例来对不同代理机制的性能进行量化。测试用例如下所示:

@Testpublic void testAopProxyPerformance() {	int countofObjects = 5000;	AccountServiceImpl[] unproxiedClasses = new AccountServiceImpl[countofObjects];	for (int i = 0; i < countofObjects; i++) {		unproxiedClasses[i] = new AccountServiceImpl();	}	AccountService[] cglibProxyClasses = new AccountService[countofObjects];	AccountService accountService = null;	for (int i = 0; i < countofObjects; i++) {		accountService = new AnnotationConfigApplicationContext(CGLIBProxyAppConfig.class).getBean(AccountService.class);		cglibProxyClasses[i] = accountService;	}	AccountService[] jdkProxyClasses = new AccountService[countofObjects];	for (int i = 0; i < countofObjects; i++) {		accountService = new AnnotationConfigApplicationContext(JDKProxyAppConfig.class).getBean(AccountService.class);		jdkProxyClasses[i] = accountService;	}	long timeTookForUnproxiedObjects = invokeTargetObjects(countofObjects, unproxiedClasses);	displayResults("NOProxy", timeTookForUnproxiedObjects);	long timeTookForJdkProxiedObjects = invokeTargetObjects(countofObjects, jdkProxyClasses);	displayResults("JDKProxy", timeTookForJdkProxiedObjects);	long timeTookForCglibProxiedObjects = invokeTargetObjects(countofObjects, cglibProxyClasses);	displayResults("CGLIBProxy", timeTookForCglibProxiedObjects);}
复制代码

可以看到,我们分别针对不使用代理、使用 JDK 代理以及使用 CGLIB 代理的场景,创建了 5000 个 AccountServiceImpl 对象实例,并记录它们的创建时间。

完整的代码可以参考:https://github.com/tianminzheng/springboot-examples/tree/main/SpringAopProxyExample

案例结果分析

现在,让我们执行这个测试用例,得到的结果如下所示:

NOProxy: 562900(ns) 0(ms)JDKProxy: 39113600(ns) 39(ms)CGLIBProxy: 46222000(ns) 46(ms)
复制代码

以上量化结果取决于不同的机器配置,但不影响我们得出结论。从结果中不难看出,JDK 动态代理在性能上优于 CGLIB 动态代理,但相差并不大。事实上,通常情况下,我们不需要对上述结果有太多的担忧,因为相比代理机制带来的优势,添加代理的时间往往可以忽略不计。

用户头像

加VX:bjmsb02 凭截图即可获取 2020-06-14 加入

公众号:程序员高级码农

评论

发布
暂无评论
Spring核心容器:AOP代理机制对性能的影响案例分析_互联网架构师小马_InfoQ写作社区