在 Spring AOP 中,@AfterThrowing 可通过声明具体异常类型参数(如 BizException)并配合其 getter 方法直接提取自定义异常中的上下文参数(如 errorCode、requestId),前提是异常未被目标方法内部捕获且代理调用有效。

在 Spring AOP 中,@AfterThrowing 可以捕获目标方法抛出的异常,但**默认只能拿到 Throwable 对象本身**。若想提取自定义异常中携带的上下文参数(比如错误码、请求 ID、用户信息等),关键在于:你的自定义异常类需提供可访问的 getter 方法,并在通知方法中将参数类型声明为该具体异常类型(而非泛化的 Exception 或 Throwable)。
1. 自定义异常需暴露上下文字段(通过 getter)
确保你的异常类是“有结构”的,不是仅靠 message 拼接信息。例如:
public class BizException extends RuntimeException {
private final String errorCode;
private final String requestId;
private final Long userId;
<pre class="brush:php;toolbar:false;">public BizException(String errorCode, String requestId, Long userId, String message) {
super(message);
this.errorCode = errorCode;
this.requestId = requestId;
this.userId = userId;
}
// 必须提供 public getter,AOP 才能反射读取
public String getErrorCode() { return errorCode; }
public String getRequestId() { return requestId; }
public Long getUserId() { return userId; }}
2. @AfterThrowing 切点需指定 throwing 参数为具体异常类型
这是最关键的一步:**不要写 throwing = "ex" 然后方法参数用 Exception ex**,而要直接使用你自定义的异常类型,并确保切点表达式匹配到抛出该异常的方法(或其子类):
@Aspect
@Component
public class ExceptionContextAspect {
<pre class="brush:php;toolbar:false;">@AfterThrowing(
pointcut = "execution(* com.example.service..*.*(..))",
throwing = "bizEx"
)
public void handleBizException(BizException bizEx) { // ← 类型必须是 BizException
System.out.println("错误码: " + bizEx.getErrorCode());
System.out.println("请求ID: " + bizEx.getRequestId());
System.out.println("用户ID: " + bizEx.getUserId());
// 可继续记录日志、发告警、存审计表等
}}
说明:
- Spring AOP 会自动将匹配到的异常实例传给
handleBizException方法,前提是方法参数类型与实际抛出的异常类型兼容(支持向上转型,如BizException的子类也可被此通知捕获); - 如果切点内可能抛出多种异常,又只想处理某一种,就明确写它的类型;若想统一处理所有异常再做类型判断,可用
Throwable ex+instanceof,但会丢失类型安全和直接调用 getter 的便利性。
3. 配合 @Around 更灵活地注入上下文(进阶场景)
如果上下文参数(如 requestId)并非来自异常本身,而是来自当前请求上下文(如 ThreadLocal、MDC、WebMvcConfigurer 注入的 RequestContext),可在 @AfterThrowing 之外补充获取逻辑:
@AfterThrowing(pointcut = "execution(* com.example.service..*.*(..))", throwing = "bizEx")
public void logWithTraceId(BizException bizEx) {
String traceId = MDC.get("traceId"); // 假设用了 Logback MDC
String requestId = RequestContext.getCurrent().getRequestId(); // 自定义上下文工具类
<pre class="brush:php;toolbar:false;">log.error("[{}] 处理失败,错误码:{},用户:{}",
traceId, bizEx.getErrorCode(), bizEx.getUserId());}
4. 注意事项与常见问题
-
异常未被 catch 就抛出才生效:@AfterThrowing 只对“未被目标方法内部捕获并吞掉”的异常触发;如果 service 方法里 try-catch 了
BizException且没 re-throw,通知不会执行; -
代理方式限制:Spring AOP 默认基于 JDK 动态代理(接口)或 CGLIB(类),确保目标方法是 public 且通过代理调用(非
this.method()内部调用); - 不要依赖 getMessage() 解析上下文:避免把 errorCode、userId 拼在 message 字符串里再正则提取——耦合高、易出错、不可序列化;应始终走结构化字段 + getter;
- 全局异常处理器(@ControllerAdvice)不冲突:@AfterThrowing 是 AOP 层面的日志/增强,@ControllerAdvice 是 MVC 异常响应层,二者可共存,分工不同。
只要异常类设计合理、通知方法签名匹配准确,提取上下文参数就是直接、类型安全、零反射调用的自然过程。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











