主题
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/false、null、数字 - 拼接:
+ - 比较:
==!=><>=<= - 逻辑:
&&||!
不支持(混淆不安全):
#paramName(参数名引用)#p0.xxx/#a0.xxx(属性访问)
示例:
kotlin
@Cacheable("players", key = "#p0 + ':' + #p1")
@Cacheable("players", unless = "#result == null")