BeanNameUrlHandlerMapping 실제 코드 살펴보기

구동희·2022년 10월 23일
0

HandlerMapping

DispatcherServlet을 통해 들어온 클라이언트 요청을 처리할 적절한 Handler를 찾아오는 역할을 한다.
즉, HandlerMapping을 통해 요청을 처리할 수 있는 적절한 Handler를 찾아 올 수 있다.

스프링이 제공하는 HandlerMapping의 종류는 여러가지가 있다.
왜냐하면 클라이언트 요청을 처리하는 Handler 즉, Controller를 만드는 방법이 다양하기 때문이다.

그래서 Spring은 다양한 Handler들 중 요청 처리에 적절한 Handler를 가져오는 HandlerMapping 또한 여러가지 제공해준다.

여러가지 HandlerMapping 중 이번 포스팅에서는 BeanNameUrlHandlerMapping을 알아보자.


BeanNameUrlHandlerMapping

빈의 이름에 들어 있는 URLHTTP 요청의 URL과 비교하여 일치하는 빈을 찾아 준다.

예를들면 Handler 이름이 "/gudonghee"와 같은 Url형식 일 때, BeanNameUrlHandlerMapping에 등록해두었다가 "/gudonghee" 라는 Url 형식의 요청이 오면 일치하는 "/gudonghee" 이름의 Handler를 찾아준다는 것이다.

아직 감이 잘안온다.😮

테스트 코드를 통해서 BeanNameUrlHandlerMapping이 어떻게 Handler들을 등록하는지 살펴보자.

우선 위와 같이 @Configuration를 통해 ApplicationContextBeanNameUrlHandlerMapping 객체를 빈 등록한다.

그리고 빈 이름이 "/test"Controller를 등록한다. BeanNameUrlHandlerMapping"/"로 이름을 시작하는 Handler 들을 상태로 가지고 있기 때문에 WelcomeController를 상태로 가지고 있을것으로 예상이 된다.😮

테스트 코드 디버그 통해 확인 해보자.

우선 테스트코드를 통해 12번 줄과 같이 ApplicationContext를 읽어온 다음 16번 줄과 같이 ApplicationContext를 통해서 BeanNameUrlHandlerMapping을 가져온다.


디버그를 통해 확인해보면 예상과 같이 BeanNameUrlHandlerMapping 객체가 필드인 handlerMap 내부에 WelcomeController를 가지고 있음을 알 수 있다.

이번에는 빈 네임이 "/test" 였던 빈의 이름을 위 사진과 같이 reaver라는 이름으로 빈 등록하고 디버그를 해보자.

BeanNameUrlHandlerMappinghanlderMap이 비어있음을 확인 할 수 있다.

왜냐하면 BeanNameUrlHandlerMapping빈의 이름이 url로 시작하는 빈들만 handlerMap`에 저장하기 때문이다.

즉, URL/로 시작하기 때문에 빈 이름이 / 시작하는 경우에만 BeanNameUrlHandlerMappinghandler로 가지고 있는다.

아래에서 실제 코드를 통해 자세히 살펴보자.🙂

❓❓ 애노테이션을 통해 빈 등록이 가능한데 BeanNameUrlHandlerMapping이 필요할까?

물론 지금은 애노테이션을 기반으로 하는 HandlerMapping이 있기 때문에 BeanNameUrlHandlerMapping을 사용하는 것이 좋지 않아보인다.

하지만, 아직 BeanNameUrlHandlerMapping을 사용하는 기업 또는 개인이 있을수 있기 때문에 스프링은 호환성을 위해서 BeanNameUrlHandlerMapping를 코드에서 남겨두지 않았을까 한다.


BeanNameUrlHandlerMapping 내부 살펴보기

위에서 BeanNameUrlHandlerMapping어떤 Handler 들을 필드로 등록하는지 살펴보았다.
그렇다면 실제로 Handler를 등록하는 과정을 BeanNameUrlHandlerMapping 코드를 통해 살펴보자.

우선, BeanNameUrlHandlerMapping은 위와 같은 상속 구조를 가진다.

왼쪽 상단을 보면 HandlerMapping 인터페이스를 구현하고 있음을 알 수 있다.

오른쪽에는 ApplicationContext와 관련한 상속 구조로 보이지만 다음에 자세히 살펴보자.


BeanNameUrlHandlerMapping의 Handler 초기화

Tomcat이 실행되고 Servlet을 초기화 하기 위한 init() 메서드가 호출 될 때, DispatcherServlet은 위와 같이 initHandlerMappings() 메서드를 통해 HandlerMapping들을 초기화 한다.

그리고 DispatcherServletinitHandlerMappings()가 실행되면
BeanNameUrlHandlerMapping도 필드에 담을 handler들을 찾아서 초기화를 진행한다.

위 사진은 BeanNameUrlHandlerMapping의 부모 객체인 AbstractDetectingUrlHandlerMappinghandlerMapping 초기화를 위한 메서드이다.
BeanNameUrlHandlerMappinghandler 초기화 과정을 따라가 보면 AbstractDetectingUrlHandlerMappinginitApplicationContext() 메서드에서 이루어진다.

위 사진은 BeanNameUrlHandlerMapping의 부모 클래스 중 하나인 AbstractUrlHandlerMapping 클래스 이다.
72번 줄을 보면 handlerMap을 필드
로 가진다.

initApplicationContext() 메서드 과정이 끝나면,
앞선 테스트 코드 디버그로 살펴보았듯이 BeanNameUrlHandlerMappingurl을 key로 handler를 value로 handler들을 가지게 된다.

❓❓ 왜 Handler들을 초기화하는데 initApplicationContext()라는 이름의 메서드를 쓸까?

BeanNameUrlHandlerMapping을 초기화 하는데 initApplicationContext()라는 네이밍의 메서드를 사용하는 것이 어색하게 느껴졋다.
왜냐하면 initApplicationContext()라는 이름은 ApplicationContext 전반을 초기화한다고 느껴지기 때문이다.

그럼에도 BeanNameUrlHandlerMapping을 초기화 하는데 initApplicationContext()를 사용하는 이유는 BeanNameUrlHandlerMappingBean초기화를 도와주는 ApplicationObjectSupport를 상속하기 때문이지 않을까 싶다.😮

실제로 initApplicationContext()ApplicationObjectSupport객체에 정의되어 있는데,
ApplicationObjectSupport의 주석을 보면 컨텍스트 참조 및 초기화 콜백 메서드를 제공합니다. 라는 구문이있다.

그래서 BeanNameUrlHandlerMapping은 초기화를 위해서 이미 존재하는ApplicationObjectSupportinitApplicationContext()를 활용한게 아닐까 추측해본다.


initApplicationContext() 자세히보기

다시 돌아와서 AbstractDetectingUrlHandlerMappinginitApplicationConext() 메서드를 따라가보자.

super.initApplicationContext() 내부를 따라가 보면 위와같이 AbstractHandlerMappinginitApplicationContext() 메서드를 호출한다.

얼핏 보아도 Interceptor를 초기화하는 일련의 작업을 해줌을 알 수 있다.

이번 포스팅에서는 HandlerMapping이 Handler를 초기화하는 것에 집중하기 위해 넘어가겠다.

detectHandlers() 자세히보기

다시 AbstractDetectingUrlHandlerMappinginitApplicationContext() 메서드를 살펴보면 detectHandlers()메서드를 호출한다.

네이밍을 보면 handler들을 handlerMap에 등록 시켜주는 handler 초기화 메서드의 느낌이 진하게 느껴진다.
따라가보자.🙂

우선 71~72번 줄을 보면 ApplicationContext 객체를 통해서 beanName들을 가져온다.

그리고 beanName들을 기반으로 url을 찾고 Handler를 등록한다.
78번 줄의 determineUrlsForHandler() 메서드가 beanName에 적절한 url을 찾는 메서드이다.

한번 따라가 보자.

determineUrlsForHandler()를 따라가 보면 위와같이 추상메서드로 선언부만 있다.
따라가보면 determineUrlsForHandler() 구체적인 구현BeanNameUrlHandlerMapping에 정의되어 있다.

위 사진은 BeanNameUrlHandlerMapping 객체의 determineUrlsForHandler() 메서드의 구현부이다.

58~ 60번 줄을 보면 맨 처음 설명과 같이 BeanName"/"로 시작하는 경우에 빈 이름을 기반으로 url들을 지정해주는 것을 알 수 있다.

determineUrlsForHandler()를 통해서 BeanNameUrlHandlerMappingurl 기반의 BeanName을 가진 Bean 만을 HandlerMap에 등록 한다는 것을 알 수 있다.

이제 BeanName을 기반으로 url들을 가져왔다. 실제로 BeanNameUrl을 기반으로 Handler를 등록하는 메서드는 81번 줄의 registerHandler()이다.

거의 다 왔다. 따라가 보자.

위 사진은 AbstractUrlHandlerMappingregisterHandler() 메서드 이다.
for문을 통해 Handler를 등록하는 메서드를 호출 함을 알 수 있다.

위 메서드가 실제로 HandlerHandlerMapping에 등록하는 메서드이다.
코드가 굉장히 길다.🙂

그래서 핵심만 살펴보면url"/" 인 경우에는 433번 줄과 같이 rootHandler로 등록하고 "/*"인 경우에는 439번줄 과 같이 defaultHandler로 등록 함을 알 수 있다.

최종적으로는 446번 줄과 같이 HandlerMapurl을 키값으로 Handler를 등록함을 볼 수 있다.


BeanNameUrlHandlerMapping의 Handler를 DispatcherServlet에 전달하는 과정

이전 포스팅에서도 살펴보았듯이 DispatcherServletdoDispatch() 메서드로 클라이언트 요청을 처리할 때,

위와 같이 getHandler() 메서드로 HandlerMapping들로 부터 적절한 Handler를 가져온다.

그리고 getHanlder() 메서드는 HttpServletRequest에 적절한 Handler를 찾아서 반환한다.

1261번 줄을 보면 HandlerMappinggetHandler()로 적절한 Handler를 가져옴을 알 수 있다.

BeanNameUrlHandlerMappingAbstractHandlerMappinggetHandler()를 통해서 DispathcherServlet에게 Handler를 전달한다.

특이한점은 Handler를 바로 전달하지 않고 516번 줄과 같이 HandlerExecutionChain 객체에 한번 감싸서 전달한다는 점이다.

한번 getHandlerExecutionChain() 메서드를 따라가 보자.

코드를 보면 파라미터인 handlerHandlerExecutionChain 객체로 변경한 다음 interceptor들을 추가 함을 알 수 있다.

즉, DispatcherServlet 에게 바로 Handler를 전달 하는 것이 아닌 HandlerExecutionChain객체로 감싸서 전달하는 이유는 Interceptor 기능을 요청을 처리하기 위한 Handler에 추가하기 위함 이라는 것을 알 수 있다.


실제 HandlerExecutionChain 내부를 보면 위와 같이 필드로 Handlerinterceptor 들을 가지고 있다.


마치면서

이상으로 HandlerMapping 객체 중, BeanNameUrlHandlerMapping에 대해 알아보았다.

살펴본 코드 이외에도 많은 부분들이 있지만 DispatcherServlet과 마찬가지로 궁금하거나 필요하다고 느껴지면 추가로 학습해 볼 예정이다.

실제 코드를 통해 다른분들의 HandlerMapping에 대한 이해에 조금이나 마 도움이 되면 좋겠다.😊

profile
천천히 배워가는 개발꿈나무

0개의 댓글