DispatcherServlet의 doDisPatch

이성준·2023년 1월 15일
0

Spring

목록 보기
11/11

클라이언트가 서버에 HTTP 요청을 보내면 DispatcherSerlvet이라는 프론트 컨트롤러를 거치는데 그 DispatcherServletdoDispatch메소드의 동작방식을 코드레벨에서 간단히 정리해보겠습니다.

초반부

1번

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;
    }

HanlderMapping

핸들러 매핑은 @Controller@RequestMapping을 가지고 있는 빈들을 등록하고 핸들러가 될 수 있는 모든 메서드를 추출해서 url을 key로 처리할 메소드(핸들러를) value로 저장해 놓습니다.

1번이 끝나면 HandlerExecutionChain은 핸들러(컨트롤러객체)와 핸들러객체에 사용될 인터셉터 객체들을 가지게 됩니다.

2번

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");
    }

중반부

3번

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을 호출한 다음 클라이언트에 적절한 응답을 내립니다.

4번

아까 찾은 HandlerAdapterhandle()메소드를 실행합니다.
실제 컨트롤러 객체의 핸들러를 실행하고 비즈니스 로직을 거친 후 응답값을 modelAndView에 채워서 반환합니다.

5번

applyDefaultViewName을 실행합니다.
핸들러를 실행한 후에 HandlerExecutionChain에 있는 Intercepter들을 순회하면서 postHandle()로 후처리 작업을 합니다.

6번

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()부분을 인터셉터들을 순회하며 실행합니다.

0개의 댓글