总结摘要
Spring AOP Aspect 异常UndeclaredThrowableException
问题标题
AOP 代码抛出 UndeclaredThrowableException 异常
问题描述
AOP 代码抛出 UndeclaredThrowableException 异常。
AOP 内的业务代码抛出了受检异常,该异常未在方法上声明。
问题分析
因使用 lombok 注解 @SneakyThrows,业务方法上未声明受检异常。
业务方法使用 AOP 功能,当业务方法内抛出受检异常时,AOP 会进行处理:
- 如果该受检异常已在方法中声明,则直接抛出。
- 否则,包装原始异常后抛出 UndeclaredThrowableException。
参见源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| // org.springframework.aop.framework.CglibAopProxy.CglibMethodInvocation#proceed
@Override
@Nullable
public Object proceed() throws Throwable {
try {
return super.proceed();
}
catch (RuntimeException ex) {
throw ex;
}
catch (Exception ex) {
if (ReflectionUtils.declaresException(getMethod(), ex.getClass()) ||
KotlinDetector.isKotlinType(getMethod().getDeclaringClass())) {
// Propagate original exception if declared on the target method
// (with callers expecting it). Always propagate it for Kotlin code
// since checked exceptions do not have to be explicitly declared there.
throw ex;
}
else {
// Checked exception thrown in the interceptor but not declared on the
// target method signature -> apply an UndeclaredThrowableException,
// aligned with standard JDK dynamic proxy behavior.
throw new UndeclaredThrowableException(ex);
}
}
}
|
相关知识
注解@SneakyThrows
@SneakyThrows可以用来偷偷地抛出受检异常,而实际上不在你的方法的 throws 子句中声明。这种有点争议的能力当然要谨慎使用。lombok 生成的代码不会忽略、包装、替换或以其他方式修改抛出的受检异常;它只是欺骗了编译器。在 JVM(类文件)级别,所有异常,无论是否受检,都可以抛出,而不管你的方法的 throws 子句是什么,这就是为什么它有效的原因。
因此,AOP 内的逻辑可能抛出未声明的受检异常,AOP 将抛出UndeclaredThrowableException异常。
另外,非受检异常 RuntimeException 会被 AOP 捕获并直接抛出,不会导致抛出 UndeclaredThrowableException 异常,。
参考资料
AOP跨模块捕获异常遭CGLIB拦截而继续向上抛出异常
https://cloud.tencent.com/developer/article/2381157
END