Spring使用-AOP-异常UndeclaredThrowableException

总结摘要
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