@Around 拦截器
ACE Framework 的 AOP 拦截器基于编译期宏织入实现,无需运行时动态代理。@Around["interceptorName"] 宏在编译期将方法体包裹进 Invocation 对象,运行期由注册的 MethodInterceptor.invoke 驱动整条调用链,对业务代码零侵入。
核心原理
@Around["timing"] 装饰方法
↓ 编译期宏展开
方法体 → Invocation { proceed() 调用原始实现 }
↓ 运行期
MethodInterceptor.invoke(inv) → inv.proceed() → 返回值多个 @Around 叠加时,宏按声明顺序由外到内嵌套包裹,形成类似洋葱的调用链。
定义拦截器
实现 MethodInterceptor 接口,重写 invoke 方法:
cangjie
package ace.demo.interceptor
import ace.framework.aop.*
public class TimingInterceptor <: MethodInterceptor {
public func invoke(inv: Invocation): Any {
let start = DateTime.now().toUnixTimeMilliseconds()
let result = inv.proceed()
let elapsed = DateTime.now().toUnixTimeMilliseconds() - start
println("[Timing] ${inv.className}.${inv.methodName} 耗时 ${elapsed}ms")
return result
}
}Invocation 字段说明
| 字段 / 方法 | 类型 | 说明 |
|---|---|---|
className | String | 被拦截的类名 |
methodName | String | 被拦截的方法名 |
args | Array<Any> | 方法实参列表(按声明顺序) |
proceed() | () -> Any | 调用下一个拦截器或原始方法体,返回原始返回值 |
WARNING
proceed() 必须调用且只调用一次。漏调会导致原始方法不执行;重复调用会使原方法执行多次(对写操作有副作用)。
注册拦截器
在应用启动前(main 函数或配置初始化阶段)调用 registerInterceptor:
cangjie
import ace.framework.aop.*
import ace.demo.interceptor.*
main(): Int64 {
registerInterceptor("timing", TimingInterceptor())
registerInterceptor("auth", AuthInterceptor())
let app = AceApplication.create()
app.listen(8080)
return 0
}registerInterceptor 第一个参数为拦截器名称,与 @Around 的参数对应。同一名称只能注册一个实例;重复注册会覆盖旧实例。
使用 @Around
在 @Service 或 @Controller 的方法上添加 @Around["name"]:
cangjie
import ace.framework.*
import ace.framework.aop.*
@Service
public class OrderService {
@Around["timing"]
public func createOrder(userId: Int64, amount: Float64): String {
// 业务逻辑
return "order-${userId}-${amount}"
}
@Around["auth"]
@Around["timing"]
public func cancelOrder(orderId: String): Bool {
// 先经过 auth 拦截器,再经过 timing 拦截器,最后执行方法体
return true
}
}链式拦截顺序
多个 @Around 按从上到下的顺序由外到内嵌套,执行顺序如下:
auth.invoke 开始
└─ timing.invoke 开始
└─ 原始方法体
timing.invoke 结束
auth.invoke 结束完整示例:计时拦截器
以下示例展示一个完整的计时拦截器,记录方法名、参数和耗时,并在超过阈值时打印警告:
cangjie
package ace.demo.interceptor
import ace.framework.aop.*
public class TimingInterceptor <: MethodInterceptor {
private let warnThresholdMs: Int64
public init(warnThresholdMs!: Int64 = 200) {
this.warnThresholdMs = warnThresholdMs
}
public func invoke(inv: Invocation): Any {
let start = DateTime.now().toUnixTimeMilliseconds()
let result = inv.proceed()
let elapsed = DateTime.now().toUnixTimeMilliseconds() - start
let tag = if (elapsed > warnThresholdMs) { "[SLOW]" } else { "[OK]" }
println("${tag} ${inv.className}.${inv.methodName}(${inv.args}) -> ${elapsed}ms")
return result
}
}注册时传入自定义阈值:
cangjie
registerInterceptor("timing", TimingInterceptor(warnThresholdMs: 100))典型用途
| 场景 | 说明 |
|---|---|
| 耗时统计 | 记录方法执行时间,慢方法告警 |
| 事务管理 | proceed() 前开启事务,成功后提交,异常时回滚 |
| 权限校验 | 读取 RequestContextHolder 中的用户信息,无权限时抛出异常 |
| 日志记录 | 统一记录入参/出参/异常,减少业务层日志代码 |
| 重试 / 熔断 | 捕获异常后重试或降级(也可直接用 @Retry/@CircuitBreaker) |
TIP
@Around 与 @Timed、@Retry、@CircuitBreaker 等内置宏可同时使用。内置宏的拦截器已由框架注册,无需手动 registerInterceptor。
DANGER
拦截器中不要持有请求级别的可变状态。拦截器实例全局单例,并发调用时共享同一实例,请使用 RequestContextHolder 或方法局部变量隔离状态。