利用AspectJ + Gradle 来实现非 Spring 下的 AOP 注解

有些时候我们有一个需求,就是能够统计增加了某个注解方法的执行时间。很自然的,我们能够想到用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

复制代码

参考文档

  1. arendd/AspectjGradlePlugin: AspectJ Plugin for Gradle 5+
  2. Javing: AspectJ + Custom Annotation + Gradle (Without Spring)

猜你喜欢

转载自juejin.im/post/7035251072050397215