在@Around通知中,应声明throws Throwable并直接throw e以原样重抛异常,避免包装或吞掉异常,确保Spring异常处理机制和调用方异常契约完整。

在 Spring AOP 的 @Around 通知中,ProceedingJoinPoint.proceed() 执行目标方法时若抛出异常,该异常默认会向上传递。若你在环绕通知里捕获了它,又想原样重新抛出(尤其是检查型异常),需注意:不能直接 throw e 后不管,否则可能破坏 Spring 的异常处理机制或导致类型擦除问题。
确保异常原样向上抛出
proceed() 方法声明抛出 Throwable,所以任何异常(包括 checked exception)都能被接住。但如果你用 try-catch(Throwable e) 捕获后直接 throw e,JVM 允许;Spring 也能继续处理。关键在于:不要用 throws Exception 这类宽泛声明掩盖原始异常类型,也不要包装成新异常(除非有意为之)。
- 捕获后直接
throw e是最安全的原样重抛方式 - 避免
throw new RuntimeException(e)—— 这会把 checked 异常转成 unchecked,调用方可能无法 catch - 若方法签名声明 throws 某 checked 异常,而你重抛的是其子类,编译器通常不报错;但若重抛的是无关类型,可能触发编译失败(取决于 catch 块位置和方法声明)
正确写法示例
以下是一个标准、安全的 @Around 写法,支持原异常透传:
@Around("execution(* com.example.service.*.*(..))")
public Object logAndPropagate(ProceedingJoinPoint joinPoint) throws Throwable {
try {
System.out.println("Before method: " + joinPoint.getSignature());
Object result = joinPoint.proceed(); // 可能抛出任意 Throwable
System.out.println("After method");
return result;
} catch (Throwable e) {
System.err.println("Exception occurred: " + e.getMessage());
throw e; // ✅ 直接 re-throw,保留原始类型和堆栈
}
}注意方法签名必须声明 throws Throwable,否则编译不通过(因为 proceed() 声明如此)。
立即学习“Java免费学习笔记(深入)”;
特别注意 checked 异常的声明一致性
如果目标方法声明 throws IOException,而你的环绕通知里 proceed() 触发了 IOException,你 catch (IOException e) 后 throw e,那通知方法也得声明 throws IOException 或更宽泛的 throws Exception;否则编译报错。最稳妥是统一用 throws Throwable。
- 推荐环绕通知方法统一声明
throws Throwable - 不要只 catch
Exception而忽略Error(虽然一般不建议处理 Error,但proceed()确实可能抛出) - 若业务逻辑需要对特定异常做处理(如记录、转换),处理完仍应
throw e或throw new XxxException(e)并确保上层能捕获
不要用 return null 或吞掉异常
常见错误是捕获异常后只打印日志却不抛出,导致调用方收不到异常,逻辑静默失败:
- ❌
catch (Exception e) { log.error(...); return null; }—— 调用方以为成功,实际出错了 - ❌
catch (Exception e) { throw new RuntimeException(e); }—— 破坏原有异常契约,调用方无法按原声明捕获 - ✅ 正确做法:记录日志 +
throw e,保持异常传播链完整


















