[Spring MVC] DispatcherServlet은 어떻게 request랑 controller를 이어줄까?
DispatcherServlet는 HandlerMapping이라는 중요한 역할을 해준다.
HandlerMapping이란 request와 이에 맞는 RequestController를 이어주는 역할을 하는데 어떻게 이어주는지 내부 구조가 궁금해졌다.
먼저 Dispatcher의 변수 선언을 보면
public class DispatcherServlet extends FrameworkServlet {
...
@Nullable
...
private boolean detectAllHandlerMappings = true;
...
...
}
다음과 같이 detectAllHandlerMappings라는 부분이 있다.
해당 플래그가 세워지고 아래 함수가 호출이 되는데
해당 함수에서 BeanFactoryUtils에서 beansOfTypeIncludingAncestors라는 메소드를 불러 matching Beans라는 HashMap을 만드는 것을 알 수 있다. 해당 HashMap은 key와 HandlerMapping이라는 value를 가지게 된다.
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
위의 메소드는 그럼 우리의 주제를 이해하는데 매우 중요한 요소 인것을 확인 할 수 있다.
해당 메소드는 context와 HandlerMapping.class라는 인수를 받게 되는데 내부 코드를 살펴보면
이처럼 우리가 넣어준 HandlerMapping이라는 클래스에 맞는 beans를 받아와서 result라는 LinkedHashMap에 값을 넣어주는 것을 볼 수 있다.
그리고 이후 여러 과정을 거쳐 결국에는
이런식으로 bean의 이름과 해당 bean의 instance를 반환하는 것을 알 수 있다.
그럼 BeanFactoryUtils.beanOfTypeIncludingAncestors가 linkedHashMap을 반환하고 해당 hashmap에는 bean과 beanInstance가 들어있는 것을 알 수 있었다. 그리고 추가적으로 이렇게 찾은 bean은 HandlerMapping이라는 클래스를 상속받은 클래스 일것이다.
HandlerMapping 인터페이스는
요런 식으로 나와 있는데 해당 클래스에는 PathPattern에 대한 메소드랑 getHandler라는 메소드가 사전에 정의 되어있는 것을 볼 수 있다.
여기서 PathPattern에 조금 눈여겨 보고 싶다. 추후에 좀 더 파보면 Bean객체가 등록될 때 RequestMapping에 어떤 처리가 되는지 알 수 있겠지만 Controller에 사용되는 RequiredMapping이라는 어노테이션은
path라는 변수를 기본적으로 가진다. 여기까지 유추해봤을 때 해당 HandlerMapping이라는 클래스로 선언 될 수 있는 Bean 객체는 Controller에 RequestMapping이 선언될 객체인것을 확인할 수 있다.
그리고 추가적으로 RequestMapping 안에 들어있는 path는 value가 된다는 것도 유념해 두어야한다.
getHandler는 HandlerExecutionChain이라는 형식을 반환하는데 HandlerExecutionChain은 아래에서 볼 수 있듯이 Handler와 Interceptor를 이어주는 역할을 한다.
DispathcerServlet은 현재 가지고 있는 handler를 모두 가져온다. 그리고 이러한 handler와 interceptor를 이어준 HandlerExecutionChain을 만든다.
이렇게 반환된 값을
HandlerAdapter에 넣어준다.
여기까지 내용을 정리하면 아래와 같다.
- Handler 는 @RequiredMapping이 선언된 Bean 객체이다.
- HandlerAdapter는 이러한 메소드에 Interceptor가 이어져있는 HandlerExecution Chain이다.
이제 DispatcherServlet이 Request를 받았을 때 사용하는 메소드인 doDispatch 메소드를 봐보면
request에 대해서 getHandler라는 메소드를 호출하는 것을 볼 수 있다.
getHandler메소드는
아까 우리가 보았던 HandlerExecutionChain을 request를 통해 가져오게 된다.
우리가 그럼 request가 beanName이고 value는 우리가 아까 정의했던 HandlerExecutionChain이다. request에서 우리는 URI를 추출 될 수 있다. 또한 우리가 알다싶이 Controller를 정의할때 @Controller어노테이션을 붙이게 되는데 해당 어노테이션의 정의를 살펴보면
@Component인것을 확인할 수 있다.
@Component로 선언된 Bean은 value가 bean의 이름이 된다. 근데 우리가 아까 봤던 @RequestMapping이라는 어노테이션으로 우리가 정의했던 path가 value로 들어가는 것을 확인했다.
따라서 우리는 Controller에서 @RequestMapping으로 정해준 Path가 Bean의 이름이 되고 해당 이름과 Bean객체가 HandlerMapping이라는 객체로 관리되다가 DispatcherServlet이 request로 들어온 경로를 바탕으로 path를 추출하고 이를 바탕으로 HandlerExeuctionChain이 호출 되는 것이었다!!