[F-Lab 모각코 챌린지] 50일차 - DispatcherServlet 분석
F-Lab 모각코 챌린지 50일차 - 디스패쳐 서블릿 코드를 분석해보았습니다. 스크린샷과 코드 해석을 첨부하였습니다
DispatcherServlet 내부 구현
DispatcherServlet의 호출 스택을 따라가서 결국 개발자가 작성한 코드가 어디서 실행되는지 알아보자
먼저 해당 흐름을 알아보기 위한 방법으로 디버깅 모드를 활용한다
doService
FrontController 의 역할을 수행하는 DispatcherServlet 이 사용자의 요청을 수신하는 부분
하지만 실제 Dispatch 는 이곳에서 수행되지 않으며 이곳에서는 기본적인 request 의 정보를 로깅하고 Dispatch 를 담당하는 메서드를 호출한다
HandlerMapping
요청을 수행하기 위한 컨트롤러는 @GetMapping, @PostMapping 등의 애노테이션으로 선언된 메서드가 그 대상이다.
HandlerMapping 은 핸들러를 찾아주는 인터페이스로서 전략 패턴이 적용되어 있다
doDispatch
실제 Dispatch 를 수행하는 부분이다.
요청을 분석하여 어떤 요청인지 구분하고 그 요청을 처리할 수 있는 핸들러로 dispatch 한다.
- 멀티파트인지, Locale 은 어떤지 체크
이 메서드에서 가장 중요한 부분은 1016 번째 라인의 mappedHandler = getHandler(processedRequest) 이다.
해당 요청을 처리하는 Handler 를 찾는데, 해당 메서드를 따라가보면 아래와 같이 나오게 된다.
이곳에서 중요한 코드는 this.handlerMappings
1233 라인의 this.handlerMappings 를 살펴보면 다음과 같은 매핑을 확인할 수 있다
RequestMappingHandlerMapping 과 BeanNameUrlHandlerMapping 은 DispatcherServlet 이 자동으로 생성 해 준다
- RequestMappingHandlerMapping
우리가 애노테이션을 통해 등록한 RequestMapping들(GetMapping, PostMapping 등) 을 찾아주는 매핑
계속 실행해 보면 mapping 이 저장된 List 를 루프 돌며 handler 를 찾으면 return 해 주게 된다.
위 그림에서는 첫번째로 탐색할 mapping 이 RequestMappingHandlerMapping 이니, 위에서 기술했듯 우리가 등록한 매핑을 찾을 수 있다
매핑을 잘 찾은 모습을 볼 수 있다.
그럼 다시 doDispatch 로 돌아와서 계속 분석 시작
1016 라인에서 받은 mappedHandler 는 제대로된 핸들러가 넘겨져 null 이 아니게 되어 noHandlerFound() 메서드가 작동하지 않는다
HandlerAdapter
HandlerMapping 에서 가져온 Controller 를 실행하는 인터페이스 이다.
이미 HandlerMapping 으로 고객의 요청을 dispatch 할 클래스 및 메서드를 찾는데 성공
이제 해당 메서드를 실행해야 한다
그렇게 하기 위해서는 실행할 수 있는 HandlerAdapter 를 찾아야 한다
getHandlerAdapter() 함수를 살펴보자
함수들을 실행할 수 있는 HandlerAdapter 중에 RequestMappingHandlerAdapter 를 찾았다.
리턴함으로서 '요청이 dispatch 될 함수와 실행할 함수' 가 모두 준비되었음을 알 수 있다
HttpCache
Http 관련 캐싱을 지원하기 위한 코드
요청된 httpMethod 가 Get 이라면 헤더 정보를 탐색하여 NotModified 를 보내도 되는지 체크하는 로직 (line : 1027 ~ 1033)
전처리
위의 일련의 과정을 거쳐 우리는 대상 클래스와 메서드, 어댑터를 찾았다
그럼 이제 해당 요청을 처리하기 전, 데이터를 매핑하고 전처리를 수행하는 부분을 살펴보자
mappedHandler 에서 핸들러를 가져오고, ha.handle 함수를 호출한다
handleInternal 의 인자값으로 넘어가는 handler 는 우리가 선언한 @PostMapping 이 붙어있는 함수이다
처리를 수행하기 위해 handleInternal 함수를 실행
다음은 handleInternal 함수와 중요한 체크포인트 이다.
맨 아래에 있는 함수가 바로 메서드를 실행하는 함수
흥미로움. 계속 들어가보자
설명에는 View 확인이 필요한 경우 ModelAndView 를 준비하는 RequestMapping 처리기 메서드를 호출한다고 작성되어있다
ModelAndViewContainer? 나는 RestController 인데?
@RestController 는 @Controller 에서 @ResponseBody 를 붙인것과 같다
Controller 와 RestController 를 둘 다 처리하기 위해 ModelAndViewContainer 가 사용된다
거기서 setRequestHandled 로 Model 의 리턴 유무를 지정할 수 있다
지금 파란색으로 하이라이트 되어있는 부분이 우리가 @RestController 안에 선언해둔 Mapping 메서드를 동작시키는 부분
모든 호출 스텍을 나열하면 아래와 같다
호출 스택의 끝은 native
- DispatcherServlet.doDispatch
- AbstractHandlerMethodAdapter.handle
- RequestMappingHandlerAdapter.handleInternal
- RequestMappingHandlerAdapter.invokeHandlerMethod
- ServletInvocableHandler.invokeAndHandle
- InvocableHandlerMethod.invokeForRequest
- InvocableHandlerMethod.doInvoke
- NativeMethodAccessorImpl.invoke
- NativeMethodAccessorImpl.invoke0
마지막 호출 스택에서 native 함수가 동작하는데 그 이후로는 리플렉션을 이용한 바이트 코드가 동작하기 때문에 더이상 스택을 따라가지 못한다. 분석은 여기서 끝
ㅜ추가적으로 Json 데이터를 DTO 에 바인딩 해 주는 클래스를 찾았다
ModelFactory.invokeModelAttributeMethods