Hyeonuk_.log

[Spring] @ControllerAdvice는 어떻게 동작하는 것일까? 본문

Dev_.log/Spring

[Spring] @ControllerAdvice는 어떻게 동작하는 것일까?

Hyeonuk_. 2022. 2. 18. 23:03

 

 

스프링에서 @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에 대해 분류할지 더 알아보려한다.


 

Comments