Spring AOP核心:Advice类型与实战解析
🌟 总览:什么是 Spring AOP 的 Advice?
在 AOP 中:
- Advice(通知/增强) 是“在某个连接点(方法调用)上要执行的代码”。
- 比如:在方法执行前打印日志、在方法抛出异常后记录错误、在方法返回后统计调用次数等。
Spring AOP 支持多种类型的 Advice,并通过代理机制实现。我们来看你提供的内容是如何组织的:
6.2 Advice API in Spring
这一节讲的是:Spring AOP 是如何处理各种 Advice 的?
6.2.1 Advice 的生命周期(Lifecycles)
核心观点:
每个 Advice 都是一个 Spring Bean,可以是:
- Per-class(类级别共享):所有被代理的对象共用同一个 Advice 实例。
- Per-instance(实例级别独享):每个被代理对象都有自己独立的 Advice 实例。
举个例子:
| 类型 | 适用场景 | 举例 |
|---|---|---|
| Per-class | 不依赖目标对象状态的通用逻辑 | 事务管理、日志记录 |
| Per-instance | 要为每个对象维护额外状态 | “锁”功能(如 Lockable 接口),每个对象有自己的 locked 状态 |
✅ 重点:你可以混合使用两种模式,比如一个 AOP 代理中既有共享的事务 advice,也有每个实例独有的“锁定”功能。
6.2.2 Spring 中的 Advice 类型
Spring 提供了多种标准 Advice 类型,并支持扩展。以下是五种主要类型:
1️⃣ Around Advice(环绕通知)——最强大的类型
✅ 特点:
- 最灵活,能控制方法调用前后的行为。
- 必须实现
MethodInterceptor接口。 - 可以决定是否继续执行原方法(
proceed())。 - 可以修改返回值或抛出异常。
📌 接口定义(Java):
public interface MethodInterceptor extends Interceptor {Object invoke(MethodInvocation invocation) throws Throwable;
}
🔍 参数说明:
invocation: 包含目标方法、参数、目标对象、代理对象等信息。invocation.proceed(): 继续执行下一个拦截器或目标方法。
💡 示例:打印方法调用前后日志
public class DebugInterceptor implements MethodInterceptor {public Object invoke(MethodInvocation invocation) throws Throwable {System.out.println("Before: " + invocation.getMethod());Object result = invocation.proceed(); // 执行目标方法System.out.println("After: method returned " + result);return result;}
}
✅ 注意:
- 如果你不调用
proceed(),目标方法就不会执行。 - 少数情况下可以返回替代值或抛异常,但一般建议调用
proceed()。
📌 优势:与 AOP Alliance 兼容,可在其他 AOP 框架中复用。
2️⃣ Before Advice(前置通知)
✅ 特点:
- 在方法执行前运行。
- 不能改变返回值。
- 若抛异常,则中断流程,不再执行后续拦截器和目标方法。
📌 接口定义:
public interface MethodBeforeAdvice extends BeforeAdvice {void before(Method m, Object[] args, Object target) throws Throwable;
}
💡 示例:统计方法调用次数
public class CountingBeforeAdvice implements MethodBeforeAdvice {private int count = 0;public void before(Method m, Object[] args, Object target) {count++;}public int getCount() {return count;}
}
✅ 优点:简单安全,无需处理 proceed(),不会忘记调用。
📌 可配合任意 Pointcut 使用。
3️⃣ Throws Advice(异常通知)
✅ 特点:
- 方法抛出异常后触发。
- 是无接口标记型接口(tag interface),不需要实现具体方法。
- 通过反射查找名为
afterThrowing(...)的方法来绑定逻辑。
📌 方法签名格式:
void afterThrowing([Method, args, target], 异常类型)
- 参数可选,但最后一个必须是异常类型。
- 可以有 1 个或 4 个参数。
💡 示例1:捕获 RemoteException
public class RemoteThrowsAdvice implements ThrowsAdvice {public void afterThrowing(RemoteException ex) {System.out.println("Remote error occurred: " + ex.getMessage());}
}
💡 示例2:获取更多上下文
public class ServletThrowsAdviceWithArguments implements ThrowsAdvice {public void afterThrowing(Method m, Object[] args, Object target, ServletException ex) {System.out.println("Failed in method: " + m.getName());}
}
💡 合并多个异常处理
public class CombinedThrowsAdvice implements ThrowsAdvice {public void afterThrowing(RemoteException ex) { ... }public void afterThrowing(Method m, Object[], Object, ServletException ex) { ... }
}
⚠️ 重要警告:
- 如果
afterThrowing方法自身抛出异常,会覆盖原始异常。 - 抛出检查异常(checked exception)时,必须与目标方法声明一致,否则会包装成运行时异常。
📌 可配合任意 Pointcut 使用。
4️⃣ After Returning Advice(后置返回通知)
✅ 特点:
- 方法成功执行并返回后执行。
- 不能修改返回值。
- 若自身抛异常,会中断流程。
📌 接口定义:
public interface AfterReturningAdvice extends Advice {void afterReturning(Object returnValue, Method m, Object[] args, Object target) throws Throwable;
}
💡 示例:统计成功调用次数
public class CountingAfterReturningAdvice implements AfterReturningAdvice {private int count = 0;public void afterReturning(Object returnValue, Method m, Object[] args, Object target) {count++;}public int getCount() {return count;}
}
📌 与 Before Advice 类似,但只在成功返回时触发。
📌 可配合任意 Pointcut 使用。
5️⃣ Introduction Advice(引入通知)——特殊类型
✅ 特点:
- 为一个类动态添加新的接口和行为(即“混入 mixin”)。
- 属于“类级别”的增强,不是方法级别。
- 例如:让任何对象都能变成“可锁定”的(Lockable)。
🧩 涉及组件:
IntroductionInterceptor: 实现新接口的方法逻辑。IntroductionAdvisor: 将 IntroductionInterceptor 和目标类关联起来。
📌 接口定义:
public interface IntroductionInterceptor extends MethodInterceptor {boolean implementsInterface(Class intf);
}
💡 实战示例:实现 Lockable 接口
目标:让任意对象支持 .lock() 和 .unlock(),锁定后禁止调用 setter 方法。
Step 1: 定义接口
public interface Lockable {void lock();void unlock();boolean locked();
}
Step 2: 创建 IntroductionInterceptor(使用 DelegatingIntroductionInterceptor)
public class LockMixin extends DelegatingIntroductionInterceptor implements Lockable {private boolean locked = false;public void lock() { this.locked = true; }public void unlock() { this.locked = false; }public boolean locked() { return this.locked; }// 拦截所有方法调用public Object invoke(MethodInvocation invocation) throws Throwable {if (locked && invocation.getMethod().getName().startsWith("set")) {throw new IllegalStateException("Object is locked!");}return super.invoke(invocation); // 调用父类逻辑(自动分发到 introduced 方法)}
}
📌 关键点:
DelegatingIntroductionInterceptor会自动把Lockable接口的方法调用转发给当前对象。- 自定义
invoke()是为了拦截 setter 方法。
Step 3: 创建 Advisor
public class LockMixinAdvisor extends DefaultIntroductionAdvisor {public LockMixinAdvisor() {super(new LockMixin(), Lockable.class);}
}
✅ 这样就完成了“为任意对象添加 Lockable 功能”。
📌 注意:
- Introduction 不能用普通 Pointcut,因为它作用于类而非方法。
- 必须使用
IntroductionAdvisor。 - 通常是 per-instance 的,因为每个对象可能有自己的状态(如
locked)。
6.3 Advisor API in Spring
Advisor = 一个 Pointcut + 一个 Advice
换句话说:
- Advice 告诉你“做什么”(what to do)。
- Pointcut 告诉你“在哪里做”(where to do it)。
- Advisor 把两者结合起来。
常见实现类:
org.springframework.aop.support.DefaultPointcutAdvisor
它可以组合:
- 任意 Pointcut(如
NameMatchMethodPointcut) - 任意 Advice(Before、After、Throws、Around)
✅ 关键能力:
你可以在同一个 AOP 代理中混合使用多种 Advice!
例如:
<!-- XML 配置示例 -->
<aop:config><aop:advisor advice-ref="txAdvice" pointcut="execution(* save*(..))"/><aop:advisor advice-ref="loggingBefore" pointcut="execution(* *.*(..))"/><aop:advisor advice-ref="countingAfter" pointcut="execution(* find*(..))"/><aop:advisor advice-ref="lockAdvisor" /> <!-- 引入 advisor -->
</aop:config>
Spring 会自动构建拦截器链(Interceptor Chain),按顺序执行。
🎯 总结:一张表帮你理清所有 Advice 类型
| Advice 类型 | 触发时机 | 是否能修改流程 | 是否能访问返回值/异常 | 是否需要 proceed() | 典型用途 |
|---|---|---|---|---|---|
| Around | 方法前后 | ✅ 能(可跳过、改返回值) | ✅ 能 | ✅ 必须手动调用 | 权限控制、缓存、性能监控 |
| Before | 方法前 | ❌ 不能改返回值 ✅ 可中断(抛异常) | ❌ 不能 | ❌ 不需要 | 日志、参数校验 |
| After Returning | 方法成功返回后 | ❌ 不能改返回值 ✅ 可中断(抛异常) | ✅ 能读返回值 | ❌ 不需要 | 统计成功次数、清理资源 |
| Throws | 方法抛异常后 | ✅ 能覆盖异常 | ✅ 能访问异常对象 | ❌ 不需要 | 错误日志、重试、降级 |
| Introduction | 类级别增强 | ✅ 能添加新接口和状态 | ✅ 能 | ✅ 需要(但通常用父类实现) | Mixin(混入)、动态增强对象能力 |
🧠 如何选择合适的 Advice?
| 需求 | 推荐 Advice |
|---|---|
| 记录方法开始 | Before Advice |
| 记录方法结束(无论成败) | Around Advice(finally 块) |
| 记录方法成功返回 | After Returning |
| 捕获并处理异常 | Throws Advice |
| 包装整个方法(如事务、缓存) | Around Advice |
| 给对象加新功能(如 Lockable) | Introduction Advice |
✅ 最佳实践建议
- 优先使用更具体的 Advice 类型(如 Before/After),代码更清晰。
- 如果要跨框架兼容,使用
MethodInterceptor(Around)。 - Introduction 是高级功能,谨慎使用,注意状态管理(per-instance)。
- Avoid 在 Advice 中抛出不必要的检查异常,以免破坏签名兼容性。
- 结合 Pointcut 使用 Advisor 来精确控制织入位置。
如果你正在学习 Spring AOP 或准备面试,这一部分内容非常关键。掌握这些 Advice 的区别和使用场景,就能写出更优雅、更可控的切面逻辑。
需要我根据这个内容画一张 思维导图结构图 或者给出一个 完整的 Spring AOP 配置示例(XML + 注解) 吗?欢迎继续提问!
