如何使用 gradle 每次都使用 aspect weave kotlin 代码

How to make aspect weave kotlin code every time with gradle

我正在研究 AOP 并使用 gradle + aspectJ 来编写我的代码。所以我按照一些演示所说的那样,将 aspectJtools 添加到顶部 build.gradle.

dependencies {
    ...
    classpath 'org.aspectj:aspectjtools:1.9.5'
}

并在app/build.gradle中,添加任务编织代码:

variants.all { variant ->
    JavaCompile javaCompile
    if (variant.hasProperty('javaCompileProvider')) {
        //android gradle 3.3.0 +
        javaCompile = variant.javaCompileProvider.get()
    } else {
        javaCompile = variant.javaCompile
    }
    def buildType = variant.buildType.name

    javaCompile.doLast {

        MessageHandler handler = new MessageHandler(true)

        String[] javaArgs = [
                "-showWeaveInfo",
                "-1.8",
                "-inpath", javaCompile.destinationDir.toString(),
                "-aspectpath", javaCompile.classpath.asPath,
                "-d", javaCompile.destinationDir.toString(),
                "-classpath", javaCompile.classpath.asPath,
                "-bootclasspath", project.android.bootClasspath.join(File.pathSeparator)
        ]
        new Main().run(javaArgs, handler)

        String[] kotlinArgs = [
                "-showWeaveInfo",
                "-1.8",
                "-inpath", project.buildDir.path + "/tmp/kotlin-classes/" + buildType,
                "-aspectpath", javaCompile.classpath.asPath,
                "-d", project.buildDir.path + "/tmp/kotlin-classes/" + buildType,
                "-classpath", javaCompile.classpath.asPath,
                "-bootclasspath", project.android.bootClasspath.join(File.pathSeparator)
        ]
        new Main().run(kotlinArgs, handler)

        for (IMessage message : handler.getMessages(null, true)) {
            switch (message.getKind()) {
                case IMessage.ABORT:
                case IMessage.ERROR:
                case IMessage.FAIL:
                    log.error message.message, message.thrown
                    break
                case IMessage.WARNING:
                case IMessage.INFO:
                    log.info message.message, message.thrown
                    break
                case IMessage.DEBUG:
                    log.debug message.message, message.thrown
                    break
            }
        }
    }
}

然后我添加我的方面:

@Aspect
public class AspectInCommon extends BaseAspect {

    @Pointcut("execution(* com.example.cheng.test.MainActivityKt.on**(..))")
    public void kotlinMainOn() {
    }

    @After("kotlinMainOn()")
    public void hookKotlinMain(JoinPoint joinPoint) throws Throwable {
        log(joinPoint.getSignature().toLongString());
    }

    ...
}

我的 MainActivity 看起来像:

    class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        ...
    }
}

通过这种方式,我确实将方面融入了我的代码。这是一些输出: 它第一次工作正常,直到我再次单击调试底部,然后所有输出都不再触发。然后经过几次测试,我发现如果我的文件之一在 kotlin 中,它只会在干净构建后编织。所以我尝试将 mainActivity 和 aspect 文件更改为 java 或 kotlin。这是测试结果:

MainActivity language       Aspect language        Weave results
Java                        Java                   Weave every time
Java                        Kotlin                 Weave after clean build *
Kotlin                      Java                   Weave after clean build
Kotlin                      Kotlin                 Weave after clean build

*:我将方面文件放在子模块中,它在干净构建后的第二次构建中崩溃了。错误信息是:

java.lang.NoSuchMethodError: No static method aspectOf()

我想知道如何每次都对 kotlin 代码进行方面编织,而不是在干净构建后只编织一次,因为我实际项目的大部分代码都是基于 kotlin 的。不简单地使用插件是完美的。非常感谢。

我既不使用 Gradle(而是 Maven)也不使用 Kotlin,但我使用多种 JVM 语言或多个编译步骤的经验表明,在单独的模块中执行每个编译步骤是有意义的。

  1. 所以你可以有一个 Java + Kotlin 应用程序 class 模块,用普通的 Java/Kotlin 编译器编译它。
  2. 然后你有一个用 AspectJ 编译器编译的方面模块。
  3. 最后,有一个模块使用 AspectJ 进行二进制编织,在 inpath 上使用第一个模块,在 aspect 路径上使用第二个模块。

这使得构建更简单、更稳定。其他优势是您拥有一个原始的、未编织的应用程序模块和一个方面增强的应用程序模块。根据情况,您可以在应用程序中使用前者或后者,例如如果你编织不应该总是使用的调试或跟踪方面。最后但同样重要的是,一个单独的方面模块使方面库有可能被重用。