You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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调用。

解决方法

  1. 显式指定注解作用于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。

  2. 关于字段级拦截的说明
    原生AspectJ编译时织入支持字段级注解匹配,但Spring AOP基于动态代理的实现仅支持方法级拦截,因此该方式不适用于当前场景。

另外,测试输出中的null是因为CGLIB代理子类会将父类非final属性初始化为类型默认值,由于切面未触发,直接返回了代理类的属性默认值(String类型默认值为null)。

内容的提问来源于stack exchange,提问作者Bennett Lynch

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 03:20:14