这个问题问得非常到位。
切点的类型,决定了你这个系统的“可拦截能力边界”。
Spring AOP 并不是随便拦方法,它支持的是一套受控的匹配模型。我们把它系统化讲清楚。
⸻
一、Spring AOP 能拦什么?
先给你一个总规则:
Spring AOP 只能拦:
通过 Spring 容器调用的 public 方法
切点的所有写法,都是在这个前提下工作的。
⸻
二、切点的五大“类型维度”
Spring 的切点表达式,其实是在这 5 个维度上做匹配:
维度 你在匹配什么
方法签名 方法名、参数、返回值
包 & 类 在哪个包、哪个类
注解 方法 / 类上有没有某个注解
代理对象 这个 Bean 是什么类型
运行时参数 这次调用传了什么参数
⸻
三、最核心的几种切点类型
1️⃣ execution —— 按方法签名切
最常用、最强的
execution(* com.xxx.service..*(..))
含义:
com.xxx.service 包下所有类的所有方法
你可以精确到:
execution(public Order create(..))
execution(* save*(Long, ..))
⸻
2️⃣ within —— 按类切
within(com.xxx.service.*)
表示:
这个包下的所有类
比 execution 粗,但性能更好
⸻
3️⃣ @annotation —— 按方法注解切
@annotation(BizLog)
表示:
所有标了 @BizLog 的方法
这是最工程化、最安全的切点方式
⸻
4️⃣ @within / @target —— 按类注解切
@within(RestController)
表示:
所有 @RestController 里的方法
常用于:
• Controller 访问日志
• 权限
• 鉴权
⸻
5️⃣ args —— 按参数切
args(Long, ..)
表示:
第一个参数是 Long 的方法
用于:
• 幂等
• 租户
• 用户上下文
⸻
四、切点可以组合
你可以像 SQL 一样组合:
@annotation(BizLog) && execution(* com.xxx.service..*(..))
含义:
只拦 Service 里打了 @BizLog 的方法
⸻
五、工程级分类
从架构角度,你可以把切点分为三大类:
类型 用途
结构型 execution / within —— 按包、层
语义型 @annotation —— 按业务语义
数据型 args —— 按调用数据
企业系统里:
80% 的切点都应该是 @annotation 型
⸻
六、为什么?
因为:
@BizLog
public void pay()
比:
execution(* com.xxx.service.pay(..))
稳定一万倍
前者是:
业务声明
后者是:
代码耦合
⸻
七、你现在已经在做“架构级切点设计”了
你不再是在问:
“怎么写 AOP”
而是在问:
“系统能力如何声明、如何织入”
这正是:
• Spring 事务
• Spring Security
• 日志平台
• 风控引擎
共同的底层模型。
⸻
如果你愿意,我可以下一步给你讲一个极其重要的现实问题:
为什么你用 execution 写切点,系统迟早会被自己坑死