在 spring aop 中,@annotation 和 execution 是两种常用的切点表达式(pointcut expression),用于定义哪些方法需要被切面(aspect)拦截。它们的设计目的、匹配规则和适用场景有显著区别,以下从核心机制、语法、应用场景和示例进行对比分析:
🔍 一、核心概念与机制
1. **@annotation表达式**
- 作用:匹配带有特定注解的方法。
- 原理:基于方法上的注解标记进行拦截,与方法的签名(类名、方法名等)无关。
- 适用场景:
- 需要为特定功能(如日志、权限校验)标记方法时。
- 需通过注解传递元数据(例如
@permission(role="admin"))。
- 优点:灵活性高,与业务逻辑解耦,适合自定义横切关注点。
- 缺点:需显式在方法上添加注解,无法批量匹配无注解的方法。
2. **execution表达式**
- 作用:通过方法签名(返回值、包名、类名、方法名、参数类型)匹配方法。
- 原理:基于方法的结构特征拦截,无需方法添加额外注解。
- 适用场景:
- 批量拦截特定包/类下的所有方法(如
com.example.service.*.*(..))。 - 按命名规范匹配方法(如所有
update*开头的方法)。
- 批量拦截特定包/类下的所有方法(如
- 优点:无需修改代码,适合通用功能(如接口耗时统计)。
- 缺点:表达式复杂,对无规则的方法难以精确匹配。
⚙️ 二、语法详解
1. **@annotation语法**
@before("@annotation(com.example.loggable)")
public void logbefore(joinpoint joinpoint) {
// 通知逻辑
}- 参数:注解的全限定类名(如
com.example.loggable)。 - 扩展:可与自定义注解结合,实现动态控制(例如
@loggable(level="debug"))。
2. **execution语法**
@around("execution(* com.example.service.*.*(..))")
public object monitor(proceedingjoinpoint pjp) throws throwable {
// 环绕通知逻辑
}- 结构:
execution(修饰符? 返回值 包.类.?方法名(参数) 异常?)*:通配任意元素(如返回值、单层包名)。..:通配多层包或任意参数(如com..*.serviceimpl匹配com下所有子包的serviceimpl类)。
- 示例:
- 匹配
userservice的所有方法:execution(* com.service.userservice.*(..)) - 匹配所有
save开头的方法:execution(* save*(..))
- 匹配
🧩 三、典型场景对比
| 维度 | **@annotation** | **execution** |
|---|---|---|
| 匹配依据 | 方法上的注解标记 | 方法签名(包、类、方法名、参数等) |
| 代码侵入性 | 需在方法上添加注解 | 无侵入,直接匹配方法结构 |
| 灵活性 | 高(可通过注解参数定制逻辑) | 中(依赖方法命名和包结构) |
| 适用案例 | - 权限校验(@requireauth)- 日志分级( @loglevel("debug")) | - 接口耗时统计 - 全局事务管理 - 包级别异常处理 |
| 复杂匹配能力 | 弱(仅依赖注解是否存在) | 强(支持通配符、逻辑运算符组合) |
💻 四、代码示例
1. **@annotation实现自定义日志**
// 自定义注解
@retention(retentionpolicy.runtime)
@target(elementtype.method)
public @interface loggable {}
// 切面类
@aspect
@component
public class logaspect {
@before("@annotation(com.example.loggable)")
public void logmethodcall(joinpoint jp) {
system.out.println("log: " + jp.getsignature().getname());
}
}
// 业务方法使用
@service
public class userservice {
@loggable
public void createuser() { ... }
}2. **execution实现方法耗时统计**
@aspect
@component
public class timeaspect {
@around("execution(* com.example.service.*.*(..))")
public object recordtime(proceedingjoinpoint pjp) throws throwable {
long start = system.currenttimemillis();
object result = pjp.proceed();
long duration = system.currenttimemillis() - start;
system.out.println(pjp.getsignature() + " executed in " + duration + "ms");
return result;
}
}✅ 五、如何选择?
- 优先
@annotation:
当需要精细控制且逻辑与业务无关时(如权限、审计日志),通过注解实现解耦。 - 优先
execution:
需批量拦截(如全局性能监控)或无法修改源码时,通过方法签名覆盖。 - 混合使用:
复杂场景可组合表达式,例如:
@before("@annotation(com.example.auth) && execution(* com.service.*.*(..))")💎 总结
- **
@annotation:注解驱动,精准标记方法,适合高定制化横切逻辑**。 - **
execution:结构驱动,无侵入匹配,适合通用功能增强**。
两者本质是互补工具,实际开发中需根据侵入性容忍度、匹配精度和维护成本综合选择。
到此这篇关于springaop中@annotation与execution的深度对比的文章就介绍到这了,更多相关springaop @annotation与execution内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论