개발/Spring

[Spring MVC] DispatcherServlet은 어떻게 request랑 controller를 이어줄까?

Jinhwan 2022. 1. 6. 18:10

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이 호출 되는 것이었다!!