클라이언트가 서버에 HTTP 요청을 보내면 DispatcherSerlvet
이라는 프론트 컨트롤러를 거치는데 그 DispatcherServlet
의 doDispatch
메소드의 동작방식을 코드레벨에서 간단히 정리해보겠습니다.
getHandler()
를 호출합니다. getHandler()
메소드는 handlerMapping
을 순회하며 이 요청을 처리할 수 있는 핸들러를 가져와 HandlerExecutionChain
객체에 집어넣습니다.
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
Iterator var2 = this.handlerMappings.iterator();
while(var2.hasNext()) {
HandlerMapping mapping = (HandlerMapping)var2.next();
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
핸들러 매핑은
@Controller
나@RequestMapping
을 가지고 있는 빈들을 등록하고 핸들러가 될 수 있는 모든 메서드를 추출해서 url을 key로 처리할 메소드(핸들러를) value로 저장해 놓습니다.
1번이 끝나면 HandlerExecutionChain
은 핸들러(컨트롤러객체)와 핸들러객체에 사용될 인터셉터 객체들을 가지게 됩니다.
getHandlerAdaptor()
를 호출합니다. HandlerAdapter
를 순회하며 이 핸들러의 결과를 ModelAndView
객체로 만들어줄 적절한 어댑터를 찾습니다.
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
Iterator var2 = this.handlerAdapters.iterator();
while(var2.hasNext()) {
HandlerAdapter adapter = (HandlerAdapter)var2.next();
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");
}
HandlerExecutionChain
에 있는 applyPreHandle()
를 호출합니다.
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
for(int i = 0; i < this.interceptorList.size(); this.interceptorIndex = i++) {
HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i);
if (!interceptor.preHandle(request, response, this.handler)) {
this.triggerAfterCompletion(request, response, (Exception)null);
return false;
}
}
return true;
}
보시면 HandlerIntercepter
가 담겨있는 List
를 순회하면서 preHandle()
을 호출해 핸들러가 실행되기 전 전처리 작업을 해줍니다. 근데preHandle
에서 false를 반환하면 인터셉터의 AfterCompletion
을 호출한 다음 클라이언트에 적절한 응답을 내립니다.
아까 찾은 HandlerAdapter
의 handle()
메소드를 실행합니다.
실제 컨트롤러 객체의 핸들러를 실행하고 비즈니스 로직을 거친 후 응답값을 modelAndView
에 채워서 반환합니다.
applyDefaultViewName
을 실행합니다.
핸들러를 실행한 후에 HandlerExecutionChain
에 있는 Intercepter
들을 순회하면서 postHandle()
로 후처리 작업을 합니다.
processDispatchResult
를 실행합니다.
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception {
생략
if (mv != null && !mv.wasCleared()) {
this.render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
생략
}
ModelAndView
객체에서 render()
를 호출합니다.
그러면 ViewResolver
로 찾은 뷰에 Model
의 데이터를 렌더링하고 반환합니다.
그리고 마지막으로 인터셉터의 afterCompletion()
부분을 인터셉터들을 순회하며 실행합니다.