[Spring] @ControllerAdvice는 어떻게 동작하는 것일까?
스프링에서 @ControllerAdvice가 어떻게 동작하는지에 대해 공부해 본 결과, Spring MVC가 어떻게 돌아가는지에 대해 공부해야했다. 구글에 존재하는 많은 이미지 중, 아래의 Spring MVC Life Cycle에 대한 이미지가 DispatcherServlet 소스를 참고하기 전 이해를 도와주는데 많은 도움이 되었다.

1: User가 요청을 보낸다.
2: Filter가 이를 받아 DispatcherServlet에게 넘긴다.
3: Locale, Multipart와 같은 것들에 대한 처리.
4: Request에 대한 Controller Mapping 처리.
5: 실제 처리를 하는 Controller로 가기 전 Interceptor preHandle 처리
6~7 : Controller 내부 프로세스 처리
8: Exception이 발생하면 HandlerExceptionResolver가 해당 Exception을 처리
9. Interceptor의 postHandle 처리
10. ViewResolver에서 View 매핑
11. 실세 View Display
12. User에게 Response
위 프로세스에서 8번에 해당하는 구간에 대해 확인해볼 예정이다.
Exception이 RequestMapping 중 발생하거나 @Controller와 같은 RequestHandler에 의해 던져졌다면 DispatcherServlet은 HandlerExceptionResolver Bean들에게 처리를 위임하거나 미리 정의해놓은 Error Response와 같은(Web.xml에 <error-page>로 설정한) alternative handling을 제공한다.
아래는 DispatcherServlet의 일부이다.
public class DispatcherServlet extends FrameworkServlet {
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
....
....
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
....
....
}
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
....
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
...
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
....
}
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
@Nullable Object handler, Exception ex) throws Exception {
...
// Check registered HandlerExceptionResolvers...
ModelAndView exMv = null;
if (this.handlerExceptionResolvers != null) {
for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
exMv = resolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
}
....
....
throw ex;
}
}
1. Dispatcher Servlet에 doDispatch에서 동작을 하다가 processDispatchResult로 들어간다.
2. exception이 null이면 정상처리 되고 에러가 발생(exception != null) 하면,
3. processHandlerExcpetion 으로 들어가서,
4. HandlerExceptionResolver를 받아와 resolveException을 하게 되는데
5. HandlerExceptionResolver에는 @ControllerAdvice 빈이 등록되어 주입됨
(@ControllerAdvice 어노테이션을 들어가서 한번 보면 @Component가 선언되어 있는 것을 볼 수 있다.)
(참고로 스프링 MVC 라이프사이클에서도 짐작할 수 있겠지만 Interceptor에서의 Exception은 @ControllerAdvice에서 처리되지 않는다.)
이전 글(https://blog.naver.com/lastindus/222644516492)과 이번 글을 통해서 @ControllerAdvice가 어떻게 동작하고 어떻게 사용하는지 알아보았다. 다음 포스팅에서는 @ControllerAdvice 안의 @ExceptionHandler를 사용하면서 어떻게 Exception에 대해 분류할지 더 알아보려한다.