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이 호출 되는 것이었다!!
'개발 > Spring' 카테고리의 다른 글
[Spring] Spring Security의 이해 (0) | 2022.01.19 |
---|---|
[Spring] Spring 을 이용한 웹 서비스 구조 (0) | 2022.01.12 |
[Spring]@Transactional과 JUnit Test (2) | 2022.01.11 |
[Spring JPA] ORM과 JPA (1) | 2022.01.05 |
[Spring] Controller request 유효성 검사 (0) | 2022.01.05 |