boot-2.1.3(spring 5.1.5) 기준으로 작성하였다.
스프링에서 컨트롤러 메서드가 URL 에 매핑 되는 과정을 살펴본다. 개인적으로 스프링은 naming 이 정말 쉽게 잘 되어 있다고 생각한다.
RequestMapping 과 관련된 빈의 이름은 requestMappingHandlerMapping
이다.
빈 이름으로만 어떤 역할을 하는지 추정해보면 RequestMapping 과 Handler 를 매핑해 주는 빈이라고 유추가 가능하다.
빈 초기화는 ApplicationContext refresh 과정(11) 중에 일어난다.
@Bean
으로 설정 되어있는 RequestMappingHandlerMapping
빈의 생성 후 초기화(InitializingBean
) 하는 과정을 자세하게 살펴보자!
클래스 구조
핸들러 매핑(RequestMappingHandlerMapping bean initialize) 전체 흐름
- RequestMappingHandlerMapping 빈 생성
- 빈 초기화 하면서, initHandlerMethods 호출
- 빈 팩토리에 등록되어 있는 빈들 중
@Controller
또는@RequestMapping
를 가지고 있는 빈을 가져온다. - 핸들러가 될 수 있는 모든 메서드를 추출한다.(detectHandlerMethods)
- 추출된 메서드를 registry 에 등록한다.(registerHandlerMethod)
주요 함수
(1) requestMappingHandlerMapping()
- 빈을 생성하는 팩토리 메서드이다. RequestMappingHandlerMapping 의 생성자릍 통해 호출한 뒤, order 와 설정 정보들을 세팅한다.
Interceptor 가 추가되는 부분이기도 하다.
(2) processCandidateBean()
- 후보 빈 이름으로 타입 정보를 가져온다.
- 타입 정보를 분석하여 Annotation 으로 핸들러 여부를 파악한다. RequestMappingHandlerMapping 에서는
@Controller
와@RequestMapping
로 판별하도록 구현되어 있다.
물론@GetMapping
이나@RestController
와 같은 어노테이션도 해당된다.
(3) detectHandlerMethods(Object handler)
- Handler 빈 또는 빈 이름을 인자로 전달 받을 수 있다. 즉, ApplicationContext(빈 팩토리) 에서 Controller 의 빈 이름으로 빈의 타입을 가져온다.
빈의 Class 정보에서 reflect api 로 메서드(핸들러) 정보를 조회하여, 각 메서드의 어노테이션을 분석해 리퀘스트매핑 정보를 생성한다.
(4) getMappingForMethod(Method method, Class<?> handlerType)
- 컨트롤러와 핸들러의 리퀘스트매핑 정보(RequestMappingInfo 객체)를 생성하는 함수이다.
method(handler) 에서 RequestMapping annotation 의 정보를 기반으로 RequestMappingInfo 를 만든다.
handlerType(Controller) 에도 정보가 있다면 동일한 과정을 거치고, 두 정보를 조합한다.
(5) createRequestMappingInfo(AnnotatedElement element)
- 빌더 패턴을 이용하여 RequestMappingInfo 를 생성하는 함수이다.
@RequestMapping
어노테이션에 설정할 수 있는 정보들을 기반으로 객체를 생성한다.
(6) register(T mapping, Object handler, Method method)
- 앞의 과정에서 생성한 메서드 정보(HandlerMethod)들을 MappingRegistry 에 등록한다. 이곳에 등록된 정보들은 DispatcherServlet 에서 요청에 매핑되는 Handler 를 조회할 때 이용된다.
RequestMappingHandlerMapping 에서T mapping
타입은 RequestMappingInfo 로 결정된다.
MultiValueMap<String, T> urlLookup
에는 URL 을 키로 매핑 정보를 등록하고,Map<T, HandlerMethod> mappingLookup
에는 매핑 정보를 키로 핸들러를 등록한다.
정리
대부분의 함수가 상위 class 인 AbstractHandlerMethodMapping 의 함수이고 몇몇 abstract 함수만이 RequestMappingHandlerMapping 에서 구현되어 있다. RequestMapping 핸들러 판별 함수(isHandler
) , 등록할 매핑 정보를 만드는 함수(getMappingForMethod
) 등이 구현체에서 구현된 함수이다.
이렇게 정리하고 보니 새삼 스프링의 확장성이 대단하다고 느낀다. 구현체를 만들어 몇몇 함수만 구현해서 빈으로 등록하여 Custom 한 매핑정보를 만들어 핸들러와 매핑시킬 수 있으니 말이다.