Spring AOP(AspectJ)能否与Kotlin属性配合使用?问题排查
Spring AOP(AspectJ)与Kotlin属性配合的问题解析
问题背景
能否将Spring AOP(AspectJ)与Kotlin属性配合使用?Kotlin属性编译为Java代码时遵循以下规则:
- 生成前缀为
get的getter方法; - 可变属性(
var)生成前缀为set的setter方法; - 有幕后字段的属性生成与属性同名的私有字段。
最小复现代码
注解定义
@Retention(AnnotationRetention.RUNTIME) @Target(AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION) annotation class TestAnnotation
切面类
@Aspect class TestAspect { @Around("@annotation(annotation)") fun throwingAround(joinPoint: ProceedingJoinPoint, annotation: TestAnnotation): Any? { throw RuntimeException() } }
测试类
internal class MinimalReproducibleExample { open class TestProperties { @TestAnnotation val sampleProperty: String = "sample property" @TestAnnotation fun sampleFunction(): String = "sample function" } private lateinit var testProperties: TestProperties @BeforeEach fun setUp() { val aspectJProxyFactory = AspectJProxyFactory(TestProperties()) aspectJProxyFactory.addAspect(TestAspect()) val aopProxyFactory = DefaultAopProxyFactory() val aopProxy = aopProxyFactory.createAopProxy(aspectJProxyFactory) testProperties = aopProxy.proxy as TestProperties } @Test fun test() { println(testProperties.sampleProperty) println(testProperties.sampleFunction()) } }
运行输出
null sample function
疑问
调试发现生成的是CGLIB代理,理论上可代理具体类,但切面未被调用。请问是@Around定义有误,还是Kotlin属性或代理具体类存在限制?
问题原因与解决方案
核心问题在于Kotlin属性注解的默认作用目标:当你把@TestAnnotation直接标注在Kotlin的val属性上时,注解默认会被编译到私有幕后字段上,而非生成的getSampleProperty()方法上。而Spring AOP的@annotation(annotation)切点表达式仅匹配方法上的注解,因此无法拦截属性的getter调用。
解决方法
显式指定注解作用于getter方法
在标注属性时,使用@get:TestAnnotation语法强制将注解编译到生成的getter方法上:open class TestProperties { @get:TestAnnotation val sampleProperty: String = "sample property" @TestAnnotation fun sampleFunction(): String = "sample function" }这样Spring AOP的切点就能匹配到getter方法,切面逻辑会被正常触发,调用
sampleProperty时会抛出预期的RuntimeException。关于字段级拦截的说明
原生AspectJ编译时织入支持字段级注解匹配,但Spring AOP基于动态代理的实现仅支持方法级拦截,因此该方式不适用于当前场景。
另外,测试输出中的null是因为CGLIB代理子类会将父类非final属性初始化为类型默认值,由于切面未触发,直接返回了代理类的属性默认值(String类型默认值为null)。
内容的提问来源于stack exchange,提问作者Bennett Lynch
相关产品推荐
相关产品推荐

