리뷰하고 있는 프로젝트에서 AOP를 적용하다가 ControllerAdvice로 안가고 UndeclaredThrowableException
가 발생한다고 질문받았다. 처음보는 예외여서 어떻게 동작하는지 확인을 해보았다.
문제 됬던 프로젝트에서 Aspect를 이용하는데, 조건에 맞지 않으면 AuthenticationException
를 던지는데, AuthenticationException
가 아닌 UndeclaredThrowableException
를 던졌다.
// AOP를 사용하는 곳
@AopAnnotation
@PostMapping
public ApiResponse test() {
}
// Aspect 로직
@Override
public String intercept(HttpServletRequest request) throws ExceptionX {
if(조건) throw new ExceptionX(); // 여기서 예외가 터졌음
return pass;
}
// 예외
public class ExceptionX extends Exception {
...
}
AspectJ를 이용하여 AOP를 구현하면 CglibAopProxy.proceed()
를 호출하는데, 해당 메서드는 RuntimeExcpetion, Exception에 따라 로직이 다르게 동작한다.
public Object proceed() throws Throwable {
try {
return super.proceed();
} catch (RuntimeException var2) {
RuntimeException ex = var2;
throw ex;
} catch (Exception var3) {
Exception ex = var3;
// 여기가 중요
if (!ReflectionUtils.declaresException(this.getMethod(), ex.getClass()) && !KotlinDetector.isKotlinType(this.getMethod().getDeclaringClass())) {
throw new UndeclaredThrowableException(ex);
} else {
throw ex;
}
}
}
문제 됬던 상황에서는 Exception을 상속받았기 때문에 예외가 터졌을때 2번째 catch문으로 이동을 하게 되었고, if절에 있는 조건이 true가 되서 UndeclaredThrowableException
가 발생했다.
ReflectionUtils.declaresException
의 상세 구현은 아래와 같다.
AuthenticationException
예외를 던지고, false를 반환하면 위에서 throws UndeclaredThrowableException
를 던진다.public static boolean declaresException(Method method, Class<?> exceptionType) {
Assert.notNull(method, "Method must not be null");
Class<?>[] declaredExceptions = method.getExceptionTypes(); // 1번 여기가 중요!!!
for (Class<?> declaredException : declaredExceptions) { // 2번
if (declaredException.isAssignableFrom(exceptionType)) {
return true;
}
}
return false;
}
throws ExceptionX
를 던진다.// 2안 해결방안
@AopAnnotat안on
@PostMapping
public ApiResponse test() throws ExceptionX {
}