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

问题描述

我正在使用以下示例代码来理解 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,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,acc) {
        System.out.printf("[after] withDraw,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,则该方面会运行,但带批注的 @Aspect Java 类不允许创建我需要捕获/拦截私有方法的特权方面。有关如何解决此问题的任何建议?

解决方法

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

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

我想你可以使用所有三个选项,编译时、编译后和加载时编织,但你应该决定哪一个而不是混合它们。您还应该决定是要使用 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 运行主类时,我看到如下内容:
> 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,check failed
[after] withDraw,current balance 20

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


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

  • aspect: 编译时编织,即一个模块中的应用程序 + 方面,如我的回答所示
  • httpcore-nio: 编译后编入第 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 被证明非常有用。对照 https://www.eclipse.org/aspectj/doc/next/devguide/ajc-ref.html 对这些进行交叉检查通常会指出问题的根源。它通常与 -inpath 或 -sourceroots 相关。

  • -inpath 是将连接点放入“ajc”的方法
  • -sourceroots 是您将方面(切入点/建议)放入“ajc”的方式

请注意,屏幕截图来自 IntelliJ。