如何让 AspectJ 的 Aspect 在 Gradle 项目中工作?

How to make an AspectJ Aspect work in a Gradle project?

我正在使用以下示例代码来理解 AspectJ:

public class Account {
    int balance = 20;

    public boolean withdraw(int amount) {
        if (balance < amount) {
            return false;
        }
        balance = balance - amount;
        return true;
    }

    public static void main(String[] args) {
        Account acc = new Account();
        acc.withdraw(5);

        Account acc2 = new Account();
        acc2.withdraw(25);
    }
}

及以下看点:

public aspect AccountAspect {

    pointcut callWithDraw(int amount, Account acc) :
            call(boolean Account.withdraw(int)) && args(amount) && target(acc);

    before(int amount, Account acc) : callWithDraw(amount, acc) {
        System.out.printf("[before] withDraw, current balance %d%n", acc.balance);
    }

    boolean around(int amount, Account acc) : callWithDraw(amount, acc) {
        if (acc.balance < amount) {
            System.out.println("[around] withDraw, check failed");
            return false;
        }
        System.out.println("[around] withDraw, check success");
        return proceed(amount, acc);
    }

    after(int amount, Account acc) : callWithDraw(amount, acc) {
        System.out.printf("[after] withDraw, current balance %d%n", acc.balance);
    }
}

我正在使用以下 Gradle 构建文件:

plugins {
    id 'java'
    id "io.freefair.aspectj.post-compile-weaving" version "4.1.4"
}

configurations {
    ajc
    aspects
    aspectCompile
    compile{
        extendsFrom aspects
    }
}

group 'org.example'
version '1.0-SNAPSHOT'

repositories {
    mavenCentral()
}

dependencies {
    implementation group: 'org.aspectj', name: 'aspectjrt', version: '1.9.4'
    implementation group: 'org.aspectj', name: 'aspectjweaver', version: '1.9.4'
    implementation group: 'org.codehaus.mojo', name: 'aspectj-maven-plugin', version: '1.8'
    testCompile group: 'junit', name: 'junit', version: '4.12'
}
compileJava {
    sourceCompatibility = "1.8"
    targetCompatibility = "1.8"
    //The following two lines are useful if you have queryDSL if not ignore
    dependsOn configurations.ajc.getTaskDependencyFromProjectDependency(true, "compileJava")
}

并且我在 Intellij 中使用 Ajc 编译器来编译 Java 类 和方面。此外,我正在设置以下 VM 选项:

-javaagent:c:\Users\dev\extlibs\aspectjweaver-1.9.6.jar

代码编译和运行没有任何问题,但方面从未触发。我不确定我的配置中缺少什么,但我找不到使用 AspectJ 时可以做的任何其他事情。如果我使用@Aspect 注释并在 META-INF/aop.xml 中注册带注释的方面,该方面运行但带注释的@Aspect Java 类 不允许创建特权方面,我需要catch/intercept一个私有方法。有关如何解决此问题的任何建议?

你在那里混合了很多东西:

  • Freefair post-compile-time weaving plugin: 这不是必需的,因为你有 Java 源代码并且可以使用正常的编译时编织将它与方面一起编译。
  • AspectJ Maven 插件:这是无用的,因为您不能简单地在 Gradle.
  • 中使用 Maven 插件
  • AspectJ 加载时织入 Java 代理:这是不必要的,因为正如我所说,您可以在这里使用正常的编译时织入。你也不需要 aop.xml 如果你不使用 -javaagent:.

我猜你可以使用所有三个选项,编译时、post-编译时和加载时编织,但你应该决定使用哪一个而不是混合使用它们。您还应该决定是否要使用 Gradle 或 Maven,也不要尝试混合使用它们。

这就是编译时编织的工作原理:

  1. 将应用程序和方面代码都放在目录 src/main/aspectj.
  2. 将您的 Gradle 构建文件简化为:
plugins {
  id "java"
  id "io.freefair.aspectj" version "5.1.1"
}

group "org.example"
version "1.0-SNAPSHOT"

repositories {
  mavenCentral()
}

dependencies {
  implementation "org.aspectj:aspectjrt:1.9.6"
}
  1. 现在当 运行 来自我的 IDE 的主要 class 时,我看到类似的东西:
> Task :compileJava NO-SOURCE
> Task :compileAspectj UP-TO-DATE
> Task :processResources NO-SOURCE
> Task :classes UP-TO-DATE

> Task :Account.main()
[before] withDraw, current balance 20
[around] withDraw, check success
[after] withDraw, current balance 15
[before] withDraw, current balance 20
[around] withDraw, check failed
[after] withDraw, current balance 20

当然你也可以编译一个单独的方面库并将其编织到你的应用程序代码中,如果要将方面库重新用于多个应用程序或模块,或者如果应用程序源代码不是用 Java(但例如在 Kotlin 中,Groovy 或 Scala)因此不能由 AspectJ 编译器直接编译。不过,我不会在这里深入探讨,因为我想向您展示最简单的解决方案,因为您似乎是一个初学者。 (顺便说一句,我也不是 Gradle 用户,我通常将 AspectJ 与 Maven 一起使用。)


更新: 插件文档真的很糟糕,但是你可以在维护者的 GitHub 存储库中看到一些 example projects

  • 方面: 编译时编织,即一个模块中的应用程序+方面,如我的回答中所示
  • httpcore-nio: post-编译时编入第 3 方库
  • 测试:如何测试方面
  • 编织: Java、Groovy 和 Lombok 中的混合语言方面以及如何在单个模块中一起使用它们

所有这些也可以使用 Maven 和 AspectJ Maven 插件来完成,但您特别要求 Gradle。

多亏了这个线程中的指针,我能够从 Gradle -

中成功构建 2 种编写方面
  1. 内部定义的方面 src/main/java - https://github.com/vadakr/gradle-aspect-inside-java
  2. 在 src/main/java 之外定义的方面,例如 src/main/aspectj - https://github.com/vadakr/gradle-aspect-outside-java

请注意,我使用了 'io.freefair.aspectj.post-compile-weaving' Gradle 插件 (https://docs.freefair.io/gradle-plugins/6.0.0-m2/reference/#_io_freefair_aspectj_post_compile_weaving),因为它是最简单的,因为它会自动通过 Java 类 (所有可能的连接点)到 'ajc' - 你需要做的就是将它指向方面本身。

调试 Gradle 构建过程时,查看生成的 ajc options proved to be very useful. Cross-checking these against https://www.eclipse.org/aspectj/doc/next/devguide/ajc-ref.html 通常指出问题的根源。它通常与 -inpath 或 -sourceroots 相关。

  • -inpath 是将连接点放入 'ajc'
  • 的方式
  • -sourceroots 是您将方面 (pointcuts/advice) 转化为 'ajc'
  • 的方式

注意截图来自IntelliJ