Java与Kotlin混合使用时AspectJ织入失效问题求助
问题场景
项目采用AspectJ、Kotlin与Java混合开发,切面需拦截Java类Class1的sayHello()方法,但AspectJ在compileKotlin阶段执行织入时,compileJava任务尚未执行,导致AspectJ无法找到目标Java类,出现如下警告:
Task :compileKotlin
2 warnings
cc\nostaldaisuki\aspectjtest\AspectJTest.kt [warning] no match for this type name: cc.nostaldaisuki.aspectjtest.Class1 [Xlint:invalidAbsoluteTypeName]G:\Codes\JVM\aspectj-test\build\classes\kotlin\main!cc\nostaldaisuki\aspectjtest\AspectJTest.class [warning] advice defined in cc.nostaldaisuki.aspectjtest.AspectJTest has not been applied [Xlint:adviceDidNotMatch]
Task :compileJava
将Class1改为Kotlin类后,织入可正常执行,现需解决Java与Kotlin混合场景下的该问题。
项目配置与代码
build.gradle.kts配置
plugins { kotlin("jvm") version "1.9.0" kotlin("kapt") version "1.9.22" id("io.freefair.aspectj.post-compile-weaving") version "8.4" id("java") } group = "org.example" version = "1.0-SNAPSHOT" repositories { mavenCentral() } dependencies { implementation("org.aspectj:aspectjrt:1.9.8.RC3") implementation("org.aspectj:aspectjweaver:1.9.8.RC3") } kotlin { jvmToolchain(17) }
应用入口代码
package cc.nostaldaisuki.aspectjtest fun main() { val class1Instance = Class1("Hello!") class1Instance.sayHello() }
切面类代码
package cc.nostaldaisuki.aspectjtest import org.aspectj.lang.ProceedingJoinPoint import org.aspectj.lang.annotation.Around import org.aspectj.lang.annotation.Aspect @Aspect class AspectJTest { @Around("execution(* cc.nostaldaisuki.aspectjtest.Class1.sayHello())") fun replaceSayHello(joinPoint: ProceedingJoinPoint) { val targetObject = joinPoint.target as Class1Kt val field = targetObject.javaClass.getDeclaredField("content") field.isAccessible = true val fieldContent = field.get(targetObject) as String Class2.replacement(fieldContent) } }
解决方案
方案1:调整编译任务顺序,让Java先编译
Gradle默认先执行compileKotlin再执行compileJava,修改任务依赖,让Java类先编译完成,确保Kotlin切面能访问到编译后的Java类:
tasks.compileKotlin { // 让Kotlin编译依赖Java编译完成 dependsOn(tasks.compileJava) // 给Kotlin编译器添加Java编译输出目录到类路径 classpath += files(tasks.compileJava.get().destinationDirectory) } tasks.compileJava { // 解除Java编译对Kotlin编译的默认依赖,避免循环依赖 dependsOn.remove(tasks.compileKotlin) }
注意:此方案要求Kotlin代码不依赖未编译的Java代码,若Kotlin存在对Java类的编译期依赖,需确保依赖关系合理。
方案2:将切面改为Java类
将Kotlin切面改为Java类,这样切面会在compileJava阶段编译,AspectJ在后续织入时可直接访问已编译的Java目标类:
package cc.nostaldaisuki.aspectjtest; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import java.lang.reflect.Field; @Aspect public class AspectJTest { @Around("execution(* cc.nostaldaisuki.aspectjtest.Class1.sayHello())") public void replaceSayHello(ProceedingJoinPoint joinPoint) throws Throwable { Object targetObject = joinPoint.getTarget(); Field field = targetObject.getClass().getDeclaredField("content"); field.setAccessible(true); String fieldContent = (String) field.get(targetObject); Class2.replacement(fieldContent); } }
此方案无需调整任务顺序,兼容性更强,适合大多数混合开发场景。
方案3:调整AspectJ织入时机
配置Freefair的AspectJ插件,让织入操作在所有编译任务(Kotlin+Java)完成后执行:
tasks.named<io.freefair.gradle.plugins.aspectj.tasks.AspectJWeave>("weaveMain") { // 确保织入任务在Kotlin和Java编译完成后执行 dependsOn(tasks.compileKotlin, tasks.compileJava) // 添加Kotlin和Java的编译输出目录到织入类路径 classpath += files(tasks.compileKotlin.get().destinationDirectory, tasks.compileJava.get().destinationDirectory) }
此方案通过延迟织入时机,确保所有目标类都已编译完成,适合需要保留Kotlin切面的场景。
内容的提问来源于stack exchange,提问作者Nostal Yuu

