Springboot 없이 Servlet에서 Spring을 연동하는 경우, 톰캣에 등록한 웹 어플리케이션에 DispatcherServlet을 등록해 사용한다. 반면 Springboot에서는 내장 톰캣을 만들고 그 안에 DispatcherServlet을 자동으로 등록해준다.
DispatcherServlet은 HTTP 프로토콜로 들어오는 모든 요청을 가장 먼저 받아 적합한 컨트롤러에 위임해주는 Front Controller이다. HTTP Request가 들어왔을 때 공통적으로 처리되어야 할 부분들을 매번 구현하지 않고 DispatcherServlet에서 처리 후 적합한 Controller로 요청을 넘기는 방식으로 동작한다. Springboot에서 이 DispatcherServlet이 공통적으로 처리해야 할 부분들을 어떻게 처리하고 다른 Controller에게 위임하는지 알아보고자 한다.
위의 소스 코드는 Spring Web Framework의 base servlet인 FrameworkServlet의 코드이다. HTTP Request의 메소드에 따라 doGet, doPost, doPut, doDelete 메소드가 각각 실행된다. 이 메소드들은 전부 processRequest를 호출하고 있다.
processRequest 메소드의 내부에서는 클라이언트로부터 날아온 Request의 Locale 정보, attribute 정보들을 얻어 저장한다. 그리고 doService 메소드를 호출한다. doService는 내부에서 DispatcherServlet의 doDispatch를 호출한다.
doDispatch 메소드에서는 가장 먼저 request를 처리할 handler를 찾는다.
handlerMappings로는 RequestMappingHandlerMapping, BeanNameUrlHandlerMapping, RouterFunctionMapping, SimpleUrlHandlerMapping, WelcomePageHandlerMapping이 존재한다.
이 handlerMappings는 initHandlerMappings 메소드를 통해 초기화 될 때 할당되는 값들로, detectAllHandlerMappings가 true일 경우에는 5가지(default strategy) 혹은 그 이상의 handlerMapping 구현체를 전부 등록하고 false인 경우에는 "handlerMapping"이라는 이름을 갖는 bean만 등록된다.
Request에 맞는 handler maaping 구현체를 찾아 mappedHandler 변수에 할당한 뒤에는 mappedHandler를 실행시켜줄 handler adapter를 찾는다.
handlerAdapters의 종류로는 RequestMappingHandlerAdapter, HandlerFunctionAdapter, HttpRequestHandlerAdapter, SimpleControllerHandlerAdapter가 존재한다.
handlerMappings와 마찬가지로 handlerAdapters는 initHandlerAdapters 메소드를 통해 초기화 될 때 할당되는 값들로, detectAllHandlerAdapters가 true일 경우에는 4가지(default strategy) 혹은 그 이상의 handlerAdapter 구현체를 전부 등록하고 false인 경우에는 "handlerAdapter"라는 이름을 갖는 bean만 등록된다.
mappedHandler에 알맞은 handlerAdapter를 찾아 ha 변수에 할당한 뒤에는 ha.handle()을 통해 HTTP 요청에 맞는 메소드를 실행시킨다. 그 전에 GET 요청일 경우 캐싱 관련하여 lastModified 값을 확인하는 부분, Interceptor를 체크해 먼저 처리하는 applyPreHandle 메소드 부분을 코드를 통해 살펴볼 수 있다.
@RequestMapping 어노테이션을 사용하는 경우 HandlerAdapter로 RequestMappingHandlerAdapter가 사용되는데 이 RequestMappingHandlerAdapter는 AbstractHandlerMethodAdapter를 상속받고 있다.
ha.handle(processedRequest, response, mappedHandler.getHandler());
정리하면, Controller에서 @RequestMapping을 사용할 경우 위의 코드에서 HandlerAdapter를 구현한 AbstractHandlerMethodAdapter의 handle 메소드를 통해 RequestMappingHandlerAdapter의 handleInternal 메소드가 실행된다.
handleInternal 메소드 내부에서 invokeHandlerMethod의 invokeAndHandle을 통해 handlerMapping 구현체를 통해 찾았던 메소드를 실행시킨다.
메소드 실행 후 리턴한 결과가 null이 아닌 경우 메소드가 리턴한 object는 returnValueHandler에 의해 처리된다.
returnValueHandler로는 15가지가 존재한다. 그 종류는 아래와 같다.
@ResponseBody 어노테이션이 있는 경우 선택되는 returnValueHandler는 12번의 RequestResponseBodyMethodProcessor이다. 이 Handler는 writeWithMessageConverters 메소드를 통해, 리턴된 object에 맞는 converter로 응답 객체를 변환한다. converter의 종류로는 총 8가지가 존재한다.
이 중 8번인 MappingJackson2HttpMessageConverter 통해 응답객체를 변환해 반환한다.
Controller 내의 메소드에서 @ResponseBody 어노테이션을 사용한 경우 ReturnValueHandler로 RequestResponseBodyMethodProcessor가 선택되어 ModelAndView 객체를 리턴하지 않지만 @ResponseBody를 사용하지 않고 view의 이름을 리턴하는 메소드인 경우 다른 ReturnValueHandler가 선택되어 ModelAndView 객체를 리턴한다.
리턴된 ModelAndView 객체로 applyDefaultViewName 메소드에서 view 이름을 세팅하고 applyPostHandle을 통해 postHandle 메소드를 구현한 Interceptor들을 찾아 실행하여 후처리를 진행한다.
마지막으로 processDispatchResult 메소드에서 render 메소드를 통해 렌더링이 일어나고 응답이 클라이언트에 반환된다.
[참조] https://mangkyu.tistory.com/18
[참조] https://nanoson.tistory.com/33
[참조] https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=todoskr&logNo=220845006916
[참조] https://it-mesung.tistory.com/142
[참조] https://yeonyeon.tistory.com/112
[참조] https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyMethodProcessor.html