[Spring] Spring MVC의 Request Flow 정리 (ViewResolver 생략)
Spring MVC의 요청 흐름을 아래의 그림으로 요약할 수 있고, 중간에 중요한 개념들을 정리하려고 한다.
DispatcherServlet
- 프론트 컨트롤러 패턴을 구현할 실체이다.
- 클라이언트의 모든 Request를 접수하고, 다른 컴포넌트에 역할을 위임한다.
- 주로 HandlerMapping, HandlerAdapter, ViewResolver로 역할을 위임한다.
HandlerMapping
특징
- Request의 URL과 매칭 되는 Handler를 선택한다.
- HandlerMapping 인터페이스를 구현한 여러 구현체들이 있고, 구현체들의 우선순위를 기준으로 매칭 되는 Handler를 선택한다.
- 여러 구현체들이 있지만, 대표적으로 RequestMappingHandlerMapping과 BeanNameUrlHandlerMapping이 있다.
RequestMappingHandlerMapping
- @RequestMapping과 같은 Annotation 기반의 Handler를 찾는다.
- 우선순위는 0으로 가장 높다.
BeanNameUrlHandlerMapping
- Spring Bean의 이름으로 Handler를 찾는다.
- 우선순위는 1이다.
동작 방식
DispatcherServlet 클래스 doService()의 doDispatch() 메서드가 실행되고, getHandler() 메서드에 의해 Handler를 찾는다. 이때, HandlerMapping을 구현한 구현체의 우선순위에 따라 찾게 된다.
DispatcherServlet 클래스의 doService()
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 생략...
try {
doDispatch(request, response);
} finally {
// 생략...
}
// 생략...
}
DispatcherServlet 클래스의 doDispatch()
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 생략...
try {
mappedHandler = getHandler(processedRequest);
} catch () {
// 생략
}
// 생략...
}
DispatcherServlet 클래스의 getHandler()
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
HandlerAdapter
특징
- DispatcherServlet에서 보낸 HttpServletRequest, HttpServletResponse 객체를 전달받고, 내부 로직에서 Controller를 실행한다.
- HandlerAdapter 인터페이스의 구현체들이 존재하고, 대표적으로 RequestMappingHandlerAdapter, HttpRequestHandlerAdapter, SimpleControllerHandlerAdapter가 있다.
RequestMappingHandlerAdapter
- Annotation 기반의 Controller에서 사용한다.
- 우선순위는 0
HttpRequestHandlerAdapter
- Http 형식을 처리한다.
- 우선순위는 1
SimpleControllerHandlerAdapter
- Controller 인터페이스를 구현한 클래스가 사용한다.
- 과거에 사용했으며, 현재는 잘 사용하지 않는다.
- 우선순위는 2
동작 방식
DispatcherServlet에서 getHandler() 메서드를 통해 Handler를 가져오고, getHandlerAdapter() 메서드를 통해 HandlerAdapter를 찾은 다음에 handle() 메서드에 의해 요청이 처리된다.
DispatcherServlet 클래스의 doDispatch()
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 생략...
try {
// Handler 조회
mappedHandler = getHandler(processedRequest);
// HandlerAdapter 조회
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// HandlerAdpater의 handle() 실행을 통해 Controller로 요청한다.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
} catch () {
// 생략
}
// 생략...
}
DispatcherServlet 클래스의 getHandlerAdapter()
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
HandlerAdapter를 구현한 클래스는 AbstractHandlerMethodAdapter가 있으며, handle() 메서드 내부의 handlerInternal() 메서드에 의해서 처리된다.
AbstractHandlerMethodAdapter 클래스의 handle()
@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
AbstractHandlerMethodAdapter 클래스의 handlerInternal()
// 하위 클래스에서 override 해야함
@Nullable
protected abstract ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception;
대표적으로 자주 사용되는 HandlerAdapter 구현체 중에서 RequestMappingHandlerAdapter가 있는데, handleInternal() 메서드 내부에서 checkRequest() 메서드를 통해 HttpServletRequest를 검증하고, invokeHandlerMethod() 메서드에 의해서 Controller로 요청을 보내게 된다.
RequestMappingHandlerAdapter 클래스의 handlerInternal()
@Nullable
protected abstract ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
// Request 검증
checkRequest(request);
// 조건에 따라 invokeHandlerMethod()를 호출한다. 직접 코드를 확인할 것
mav = invokeHandlerMethod(request, response, handlerMethod);
}
RequestMappingHandlerAdapter 클래스의 invokeHandlerMethod()
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
// 생략...
try {
// 생략...
}
finally {
// 생략...
}
}
invokeHandlerMethod() 메서드 내부에는 HandlerMethodArgumentResovlers를 통해 HttpServlet, Model, @RequestParam, @ModelAttribute, @RequestBody, HttpEntity와 같은 부분들을 유연하게 처리하는 로직이 있으며, HandlerMethodReturnValueHandler를 통해 @ResponseBody, HttpEntity를 처리하는 로직이 존재하며, 기타 로직들을 수행하여, 사용자의 요청을 Controller로 전달한다.
참고
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-1
https://velog.io/@jihoson94/Spring-MVC-HandlerAdapter-%EB%B6%84%EC%84%9D%ED%95%98%EA%B8%B0