🌟 总览:什么是 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,可以是:

  1. Per-class(类级别共享):所有被代理的对象共用同一个 Advice 实例。
  2. 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)。
🧩 涉及组件:
  1. IntroductionInterceptor: 实现新接口的方法逻辑。
  2. 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

✅ 最佳实践建议

  1. 优先使用更具体的 Advice 类型(如 Before/After),代码更清晰。
  2. 如果要跨框架兼容,使用 MethodInterceptor(Around)
  3. Introduction 是高级功能,谨慎使用,注意状态管理(per-instance)
  4. Avoid 在 Advice 中抛出不必要的检查异常,以免破坏签名兼容性。
  5. 结合 Pointcut 使用 Advisor 来精确控制织入位置

如果你正在学习 Spring AOP 或准备面试,这一部分内容非常关键。掌握这些 Advice 的区别和使用场景,就能写出更优雅、更可控的切面逻辑。

需要我根据这个内容画一张 思维导图结构图 或者给出一个 完整的 Spring AOP 配置示例(XML + 注解) 吗?欢迎继续提问!

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐