以“保镖(AOP)”和“大明星(目标方法)”的故事为例。现在这一课,就是要教保镖们“什么时候动手” 、 “好几个保镖怎么排队”以及“怎么精准地拦住该拦的人”。
1. 通知类型 (Advice Types)
这就是在规定保镖在什么时间点干活。
Spring AOP 提供了 5 种时间点,你只需要记住最厉害的那个(@Around),但其他的也要知道意思:
| 注解 | 名字 | 演唱会类比(时间点) |
|---|---|---|
@Before | 前置通知 | 入场前。安检、收手机。 |
@After | 后置通知 | 散场后。不管演唱会是圆满成功还是出了事故,最后都要打扫卫生。 |
@AfterReturning | 返回后通知 | 圆满结束后。只有演出顺利(没报错),才开香槟庆祝。 |
@AfterThrowing | 异常后通知 | 出事故后。只有演出搞砸了(抛异常了),才启动公关预案。 |
@Around | 环绕通知 | 全能大管家。它能决定大明星能不能上台,能改大明星的出场费(返回值),能捕获大明星的失误。 (这个最常用!) |
2. 通知顺序 (Advice Order)
场景:如果大明星请了两个保镖团队(定义了两个 AOP 切面类),一个负责“记日志”,一个负责“检查权限”。
问题:谁先上?
这就好比剥洋葱,或者穿衣服:
- 进去的时候(Before) :数字越小,越先执行。
- 出来的时候(After) :数字越小,越后执行(因为它在最外层)。
怎么控制?
在切面类上加一个注解:@Order(1)。
@Order(1)的保镖站在最外层(先拦截请求,最后放行响应)。@Order(2)的保镖站在里层。
3. 切入点表达式 (Pointcut Expressions)
这就是在教保镖“如何精准识别目标”。
主要有两种写法,一种是“按地址找人”,一种是“按标签找人”。
A. execution(...) —— 按地址找(比较繁琐)
你得把包名、类名、方法名写得清清楚楚。
- 写法:
execution(* com.itheima.service.impl.EmpServiceImpl.*(..)) - 缺点:万一你改了类名,这行代码就废了。而且不够灵活。
B. @annotation(...) —— 按标签找(推荐!好用! )
这是进阶玩法的核心。
- 你自己做一个注解(比如叫
@MyLog)。 - 你想管哪个方法,就在哪个方法头上贴一个
@MyLog。 - AOP 配置里写:
@annotation(com.itheima.anno.MyLog)。 - 效果:保镖只拦截贴了标签的方法。
4. 连接点 (JoinPoint)
这是保镖手里的“情报单”。
当 AOP 拦截到方法时,你肯定想知道:“我是拦住了谁?他带了什么参数?”
JoinPoint 就是干这个的。
在你的通知方法里,加上这个参数,Spring 会自动传给你:
Java
// 在 Advice 方法里
public void recordLog(JoinPoint joinPoint) {
// 1. 获取目标方法的方法名
String methodName = joinPoint.getSignature().getName();
// 2. 获取目标方法收到的参数(比如 id=1)
Object[] args = joinPoint.getArgs();
// 3. 获取目标对象本身
Object target = joinPoint.getTarget();
System.out.println("我拦住了方法:" + methodName + ",参数是:" + Arrays.toString(args));
}
注意:如果是 @Around 环绕通知,参数类型要用功能更强的子接口 ProceedingJoinPoint,因为它多了一个 .proceed() 方法(让目标方法继续执行)。
总结
这四个概念其实就是回答了 AOP 编程的四个问题:
- 类型:保镖在入场前还是散场后动手?
- 顺序:好几个保镖谁说了算?(
@Order) - 表达式:怎么告诉保镖去拦谁?(推荐用
@annotation贴标签法) - 连接点:保镖怎么知道拦下来的人叫什么名字、带了什么东西?(
JoinPoint)