有些时候我们有一个需求,就是能够统计增加了某个注解方法的执行时间。很自然的,我们能够想到用AOP的方法。
由于我们熟悉编写的代码是在 Spring 下面的编写的 AOP 注解,但是很多时候,我们可能并不一定需要去一个庞大的Spring 环境,才能够实现 AOP 的功能。因为我仅仅想做的就是一个在多线程下的方法性能测试,我只是想启动最少量的代码,来实现我需要的切面功能。
搜索了一番之后终于找到在 Gradle 项目中,不启动 Spring 环境,来使用 AOP 的方案。
下面的代码将实现以@ExecutionTime
注解来修饰方法,获得方法的统计时间。
Gradle的配置
目前暂时没有找到一个官方支持的AspectJ Gradle 插件。如果你用的是 Gradle5 以上的版本,建议你跟我一样使用 arendd/AspectjGradlePlugin: AspectJ Plugin for Gradle 5+ 插件。
project.ext {
aspectjVersion = '1.9.7'
}
buildscript {
repositories {
maven {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
classpath "gradle.plugin.de:AspectjGradlePlugin:0.0.6"
}
}
apply plugin: "aspectj.AspectjGradlePlugin"
group 'top.ilovestudy.tools'
version 'unspecified'
dependencies {
implementation "org.aspectj:aspectjrt:${aspectjVersion}"
implementation "org.aspectj:aspectjweaver:${aspectjVersion}"
implementation "org.aspectj:aspectjtools:${aspectjVersion}"
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
}
java {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
test {
useJUnitPlatform()
}
复制代码
增加一个 ExecutionTime 注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ExecutionTime {
ChronoUnit unit() default ChronoUnit.NANOS;
}
复制代码
增加一个 aspect 实现类 ExecutionTimeAspect
@Aspect
public class ExecutionTimeAspect {
@Pointcut("@annotation(executionTime)")
public void callAt(ExecutionTime executionTime) {
}
@Around("callAt(executionTime)")
public Object around(ProceedingJoinPoint pjp, ExecutionTime executionTime) throws Throwable {
long start = System.nanoTime();
Object proceed = pjp.proceed();
long end = System.nanoTime();
System.out.println(String.format("[%s] method [%s] execution time: %s %s",
Thread.currentThread().getName(),
pjp.getSignature().getName(),
Duration.of(end - start, ChronoUnit.NANOS).get(executionTime.unit()),
executionTime.unit().name()));
return proceed;
}
}
复制代码
使用
@ExecutionTime
void add10K() {
int idx = 0;
while (idx++ < maxCountNum) {
addOne();
}
}
复制代码
然后,不管我们在 Junit5 测试框架中,还是实现的代码中,调用 add10K 的方法,都会获得一个日志的记录(线程,方法,执行时间),表示运行 add10K 耗费了多少时间。例如:
[calc7] method [add10K] execution time: 3841505 NANOS
[calc0] method [add10K] execution time: 48612486 NANOS
[calc1] method [add10K] execution time: 53342401 NANOS
[calc3] method [add10K] execution time: 55629095 NANOS
[calc5] method [add10K] execution time: 56005109 NANOS
[calc7] method [add10K] execution time: 56238950 NANOS
复制代码