Skip to content

AOP(面向切面编程)

AOP 的作用:把日志、缓存、计时、安全等通用逻辑从业务代码中分离出来。

在 Behemiron 中,AOP 是基于注解的:

  • 给方法打标记注解
  • 在切面中声明通知(Before/After/Around)

1. 最小示例

定义一个业务注解:

kotlin
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class Logged

业务方法使用注解:

kotlin
@Service
class ShopService {
    @Logged
    fun buy(playerId: String, itemId: String) {
        // ...
    }
}

切面:

kotlin
@Aspect(order = 1)
class LogAspect {
    @Before(Logged::class)
    fun before(jp: JoinPoint) {
        println("进入方法: ${jp.signature}")
    }

    @After(Logged::class)
    fun after(jp: JoinPoint) {
        println("离开方法: ${jp.signature}")
    }
}

2. 通知类型

  • @Before:方法执行前
  • @After:方法执行后(无论是否异常)
  • @AfterReturning:方法正常返回后,可拿到返回值
  • @AfterThrowing:方法抛异常后,可拿到异常
  • @Around:最强,完全控制调用

@Around 示例:

kotlin
@Around(Logged::class)
fun around(pjp: ProceedingJoinPoint): Any? {
    val start = System.currentTimeMillis()
    try {
        return pjp.proceed()
    } finally {
        println("耗时: ${System.currentTimeMillis() - start}ms")
    }
}

3. 重要规则

  • 只有 IOC 创建的 Bean 才会被代理
  • 业务类内部自调用(this.xxx())不会触发 AOP。
  • Kotlin object 代理取决于运行时实现(可通过 AopAPI.supportsKotlinObjectProxy() 判断)。

4. 缓存 AOP

Behemiron 内置了缓存切面:

  • @Cacheable
  • @CacheEvict
  • @CachePut

示例:

kotlin
@Cacheable("playerData", key = "#p0")
fun getPlayerData(uuid: UUID): PlayerData? { ... }

SpEL(混淆安全语法)

仅支持 混淆安全 的表达式,避免二开时因为混淆导致失效:

  • 位置参数:#p0#p1#a0#a1
  • 返回值:#result(仅用于 unless / condition
  • 字面量:'text' / "text"true / falsenull、数字
  • 拼接:+
  • 比较:== != > < >= <=
  • 逻辑:&& || !

不支持(混淆不安全):

  • #paramName(参数名引用)
  • #p0.xxx / #a0.xxx(属性访问)

示例:

kotlin
@Cacheable("players", key = "#p0 + ':' + #p1")
@Cacheable("players", unless = "#result == null")