使用 Jcabi Maven 插件的 AspectJ 二进制编织不适用于 Kotlin 代码
AspectJ binary weaving with Jcabi Maven plugin not working for Kotlin code
我正在尝试 运行 对函数进行一些注释,该函数将在方法执行前后进行记录。
我做了什么:(所有classes都在src/main/kotlin
下)
注释class
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class LogMe
看点class
import org.aspectj.lang.JoinPoint
import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.Around
import org.aspectj.lang.annotation.Aspect
@Aspect
abstract class Aspect {
@Around("@annotation(LogMe) && execution(* *(..))")
fun logMe(joinPoint: ProceedingJoinPoint): Any {
beforeExecution(joinPoint)
afterExecution(joinPoint)
return joinPoint.proceed()
}
private fun beforeExecution(joinPoint: JoinPoint) {
println("[${joinPoint.signature.name} has started its execution]")
}
private fun afterExecution(joinPoint: JoinPoint) {
println("[${joinPoint.signature.name} has ended its execution]")
}
}
Foo class 带注释的方法
class Foo {
@LogMe
fun yourMethodAround() {
println("Executing foo.yourMethodAround()")
}
}
主文件
fun main(args: Array<String>) {
val foo = Foo()
foo.yourMethodAround()
}
我的POM.xml(剪辑版)
...
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
<version>1.3.40</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
<version>1.3.40</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.4</version>
</dependency>
<!-- TEST -->
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test</artifactId>
<version>1.3.40</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test-junit</artifactId>
<version>1.3.40</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<sourceDirectory>src/main/kotlin</sourceDirectory>
<testSourceDirectory>src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<artifactId>kotlin-maven-plugin</artifactId>
<configuration>
<jvmTarget>1.8</jvmTarget>
</configuration>
<groupId>org.jetbrains.kotlin</groupId>
<version>1.3.40</version>
<executions>
<execution>
<id>kapt</id>
<goals>
<goal>kapt</goal>
</goals>
</execution>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals> <goal>compile</goal> </goals>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals> <goal>test-compile</goal> </goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.jcabi</groupId>
<artifactId>jcabi-maven-plugin</artifactId>
<version>0.14.1</version>
<executions>
<execution>
<goals>
<goal>ajc</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>MainKt</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
当我基本上 运行 这个 main 时,我得到的是它进入我的 Foo class 方法的 println:
Executing foo.yourMethodAround()
但是我没有得到我期望从 Aspect class.
执行前后的原理
你们中有人遇到过这个问题吗?这让我很挣扎,因为我不明白这里发生了什么。
免责声明:
- 我从来没有用过Jcabi插件,一般都是用AspectJ Maven插件,二进制编织也是如此。
- 没用过Kotlin语言,一般用Java或Groovy。
现在你的方面有些不对:
- 不能是
abstract
,否则无法创建实例
- 对于
void
方法它必须能够 return null
,所以 Kotlin return 类型应该是 Any?
- 你应该
proceed()
在前后日志消息之间,否则日志输出是错误的。
- 假设您的 classes,尤其是注解 class,不在默认包中但有一个实际的包名,您需要使用完全限定的 class切入点中的名称,例如
@annotation(de.scrum_master.app.LogMe)
- 使用方面 class 名称
Aspect
,即与 @Aspect
注释相同的名称,只是在另一个包中,有点难看。你应该重命名它。
对我来说这很好用:
package de.scrum_master.aspect
import org.aspectj.lang.JoinPoint
import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.Around
import org.aspectj.lang.annotation.Aspect
@Aspect
class LogAspect {
@Around("@annotation(de.scrum_master.app.LogMe) && execution(* *(..))")
fun logMe(joinPoint: ProceedingJoinPoint): Any? {
beforeExecution(joinPoint)
val result = joinPoint.proceed()
afterExecution(joinPoint)
return result
}
private fun beforeExecution(joinPoint: JoinPoint) {
println("[${joinPoint.signature.name} has started its execution]")
}
private fun afterExecution(joinPoint: JoinPoint) {
println("[${joinPoint.signature.name} has ended its execution]")
}
}
此外,也许您还应该将 Jcabi 插件配置为语言级别 Java 8。这里没有它也可以工作,但也许它会更好,具体取决于您使用的语言功能:
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
mvn clean verify
后我的控制台如下所示:
$ java -jar target/so-aj-kotlin-56890630-1.0-SNAPSHOT.jar
[yourMethodAround has started its execution]
Executing foo.yourMethodAround()
[yourMethodAround has ended its execution]
我的 IDE IntelliJ IDEA 不太了解二进制编织的东西,因为它不知道 Jcabi,只知道 AspectJ Maven。所以我只是将项目配置为将编译委托给 Maven:
然后运行直接从IDEA申请时日志输出是一样的。
我正在尝试 运行 对函数进行一些注释,该函数将在方法执行前后进行记录。
我做了什么:(所有classes都在src/main/kotlin
下)
注释class
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class LogMe
看点class
import org.aspectj.lang.JoinPoint
import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.Around
import org.aspectj.lang.annotation.Aspect
@Aspect
abstract class Aspect {
@Around("@annotation(LogMe) && execution(* *(..))")
fun logMe(joinPoint: ProceedingJoinPoint): Any {
beforeExecution(joinPoint)
afterExecution(joinPoint)
return joinPoint.proceed()
}
private fun beforeExecution(joinPoint: JoinPoint) {
println("[${joinPoint.signature.name} has started its execution]")
}
private fun afterExecution(joinPoint: JoinPoint) {
println("[${joinPoint.signature.name} has ended its execution]")
}
}
Foo class 带注释的方法
class Foo {
@LogMe
fun yourMethodAround() {
println("Executing foo.yourMethodAround()")
}
}
主文件
fun main(args: Array<String>) {
val foo = Foo()
foo.yourMethodAround()
}
我的POM.xml(剪辑版)
...
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
<version>1.3.40</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
<version>1.3.40</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.4</version>
</dependency>
<!-- TEST -->
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test</artifactId>
<version>1.3.40</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test-junit</artifactId>
<version>1.3.40</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<sourceDirectory>src/main/kotlin</sourceDirectory>
<testSourceDirectory>src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<artifactId>kotlin-maven-plugin</artifactId>
<configuration>
<jvmTarget>1.8</jvmTarget>
</configuration>
<groupId>org.jetbrains.kotlin</groupId>
<version>1.3.40</version>
<executions>
<execution>
<id>kapt</id>
<goals>
<goal>kapt</goal>
</goals>
</execution>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals> <goal>compile</goal> </goals>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals> <goal>test-compile</goal> </goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.jcabi</groupId>
<artifactId>jcabi-maven-plugin</artifactId>
<version>0.14.1</version>
<executions>
<execution>
<goals>
<goal>ajc</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>MainKt</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
当我基本上 运行 这个 main 时,我得到的是它进入我的 Foo class 方法的 println:
Executing foo.yourMethodAround()
但是我没有得到我期望从 Aspect class.
执行前后的原理你们中有人遇到过这个问题吗?这让我很挣扎,因为我不明白这里发生了什么。
免责声明:
- 我从来没有用过Jcabi插件,一般都是用AspectJ Maven插件,二进制编织也是如此。
- 没用过Kotlin语言,一般用Java或Groovy。
现在你的方面有些不对:
- 不能是
abstract
,否则无法创建实例 - 对于
void
方法它必须能够 returnnull
,所以 Kotlin return 类型应该是Any?
- 你应该
proceed()
在前后日志消息之间,否则日志输出是错误的。 - 假设您的 classes,尤其是注解 class,不在默认包中但有一个实际的包名,您需要使用完全限定的 class切入点中的名称,例如
@annotation(de.scrum_master.app.LogMe)
- 使用方面 class 名称
Aspect
,即与@Aspect
注释相同的名称,只是在另一个包中,有点难看。你应该重命名它。
对我来说这很好用:
package de.scrum_master.aspect
import org.aspectj.lang.JoinPoint
import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.Around
import org.aspectj.lang.annotation.Aspect
@Aspect
class LogAspect {
@Around("@annotation(de.scrum_master.app.LogMe) && execution(* *(..))")
fun logMe(joinPoint: ProceedingJoinPoint): Any? {
beforeExecution(joinPoint)
val result = joinPoint.proceed()
afterExecution(joinPoint)
return result
}
private fun beforeExecution(joinPoint: JoinPoint) {
println("[${joinPoint.signature.name} has started its execution]")
}
private fun afterExecution(joinPoint: JoinPoint) {
println("[${joinPoint.signature.name} has ended its execution]")
}
}
此外,也许您还应该将 Jcabi 插件配置为语言级别 Java 8。这里没有它也可以工作,但也许它会更好,具体取决于您使用的语言功能:
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
mvn clean verify
后我的控制台如下所示:
$ java -jar target/so-aj-kotlin-56890630-1.0-SNAPSHOT.jar
[yourMethodAround has started its execution]
Executing foo.yourMethodAround()
[yourMethodAround has ended its execution]
我的 IDE IntelliJ IDEA 不太了解二进制编织的东西,因为它不知道 Jcabi,只知道 AspectJ Maven。所以我只是将项目配置为将编译委托给 Maven:
然后运行直接从IDEA申请时日志输出是一样的。