购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

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

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

3.3.1 测试案例设计

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

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

代码清单3-15 ScopedProxyMode枚举类代码

public enum ScopedProxyMode {
    DEFAULT,
    NO,
    INTERFACES,
    TARGET_CLASS;
}

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

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

代码清单3-16 JDKProxyConfig类代码

@Configuration
@EnableAspectJAutoProxy
public class JDKProxyConfig {
    @Bean
    @Scope(proxyMode=ScopedProxyMode.INTERFACES)
    public AccountService accountService(){
        return new AccountServiceImpl();
    }
}

而CGLIBProxyConfig则如代码清单3-17所示。

代码清单3-17 CGLIBProxyConfig类代码

@Configuration
@EnableAspectJAutoProxy
public class CGLIBProxyAppConfig {
    @Bean
    @Scope(proxyMode=ScopedProxyMode.TARGET_CLASS)
    public AccountService accountService(){
        return new AccountServiceImpl();
    }
}

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

代码清单3-18 基于动态代理获取AccountService实现类代码

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

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

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

代码清单3-19 动态代理性能测试用例代码

@Test
public 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/spring-boot-examples/tree/main/SpringAopProxyExample。

3.3.2 案例结果分析

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

代码清单3-20 测试用例结果

NOProxy: 562900(ns) 0(ms)
JDKProxy: 39113600(ns) 39(ms)
CGLIBProxy: 46222000(ns) 46(ms)

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

点击中间区域
呼出菜单
上一章
目录
下一章
×

打开