Spring Boot 学习第八天:AOP代理机制对性能的影响

时间:2024-07-05 16:33:34

1 概述

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

2 测试案例设计

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

        为了能够基于不同的代理机制来创建对象,需要引入Spring中一个非常有用的注解,即@Scope。这个注解可以用来设置Bean的作用域,还可以用来指定代理模式ScopedProxyMode,在Spring中,ScopedProxyMode是一个枚举,代码如下:

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

    private ScopedProxyMode() {
    }
}

        这里的INTERFACES代表的就是JDK动态代理,而TARGET_CLASS使用的则是CGLIB动态代理。现在,创建两个配置类JDKProxyConfig和CGLIBProxyConfig,分别针对AccountServiceImpl指定两种不同的代理机制。JDKProxyConfig代码如下:

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

        CGLIBProxyConfig代码如下:

@EnableAspectJAutoProxy
@Configuration
public class CGLIBProxyConfig {

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

        借助于这两个配置类,就可以通过AnnotationConfigApplicationContext这个基于注解配置的应用上下文对象来获取添加了不同代理机制的AccountServiceImpl对象。准备工作已经完成,编写一个测试用例来对不同代理机制的性能进行量化。代码如下:

public class ProxyTest {
    private static final Logger LOGGER = Logger.getLogger(ProxyTest.class);

    @Test
    public void testAopProxyPerformance() throws MinimumAccountException {
        int countObjects = 5000;
        AccountServiceImpl[] unproxiedClasses = new AccountServiceImpl[countObjects];
        for (int i = 0; i < countObjects; i++) {
            unproxiedClasses[i] = new AccountServiceImpl();
        }
        AccountService[] cglibProxyClasses = new AccountService[countObjects];
        AccountService accountService = null;
        for (int i = 0; i < countObjects; i++) {
            accountService = new AnnotationConfigApplicationContext(CGLIBProxyConfig.class).getBean(AccountService.class);
            cglibProxyClasses[i] = accountService;
        }
        AccountService[] jdkProxyClasses = new AccountService[countObjects];
        for (int i = 0; i < countObjects; i++) {
            accountService = new AnnotationConfigApplicationContext(JDKProxyConfig.class).getBean(AccountService.class);
            jdkProxyClasses[i] = accountService;
        }

        long noProxy = invokeTargetObjects(countObjects, unproxiedClasses);
        displayResults("NOProxy",noProxy);
        long jdkProxy = invokeTargetObjects(countObjects, jdkProxyClasses);
        displayResults("JDKProxy",jdkProxy);
        long cglibProxy = invokeTargetObjects(countObjects, cglibProxyClasses);
        displayResults("cglibProxy",cglibProxy);

    }
    private void displayResults(String label, long timeTook) {
        LOGGER.info(label + ": " + timeTook + "(ns) " + (timeTook / 1000000) + "(ms)");
    }
    private long invokeTargetObjects(int countObjects,AccountService[] classes) throws MinimumAccountException {
        long start = System.nanoTime();
        Account source = new Account(101,"Account1");
        Account dest = new Account(102,"Account2");
        for (int i = 0; i < countObjects; i++) {
            classes[i].doAccountTransaction(source, dest, 100);
        }
        long end = System.nanoTime();
        return end - start;
    }
}

        运行结果:

        可以看到,分别针对不使用代理、使用JDK代理和CGLIB代理的场景,创建了5000个AccountServiceImpl对象实例,并记录它们的创建时间。以上量化结果取决于不同的机器配置,但不影响得出结论。从结果中不难看出,JDK动态代理在性能上优于CGLIB动态代理,但相差并不大。 

  其他类的代码如下:

public class Account {
    private String accountName;
    private Integer accountNumber;

    public String getAccountName() {
        return accountName;
    }
    public void setAccountName(String accountName) {
        this.accountName = accountName;
    }

    public Integer getAccountNumber() {
        return accountNumber;
    }
    public void setAccountNumber(Integer accountNumber) {
        this.accountNumber = accountNumber;
    }

    public Account(Integer accountNumber, String accountName) {
        this.accountNumber = accountNumber;
        this.accountName = accountName;
    }
}
public interface AccountService {
	
	boolean doAccountTransaction(Account source, Account dest, int amount);
}
public class AccountServiceImpl implements AccountService {
	@Override
	public boolean doAccountTransaction(Account source, Account dest, int amount) {
		
		return true;
	}

}