본문 바로가기

IT/Web

DispatcherServlet 기본 동작

반응형

1. DispatcherServlet 기본 동작

  1. get http://localhost:8080/app/hello

  2. DispatcherServlet.java

    1. doService

      @Override
       protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
           logRequest(request);
           ...
    2. doDispatch 로 진입

      1. 멀티파트 요청(파일 업로드) 인지 확인

      2. 핸들러를 찾아오는 부분 : 해당 요청을 처리하는 핸들러를 찾아오는 부분 (대부분 전략 패턴을 통해 가지고 옴)

      3. DispatcherServlet이 들고 있는 여러개의 핸들러 맵핑중 RequestMappingHandlerMapping 을 선택

        1%20DispatcherServlet%20%E1%84%80%E1%85%B5%E1%84%87%E1%85%A9%E1%86%AB%20%E1%84%83%E1%85%A9%E1%86%BC%E1%84%8C%E1%85%A1%E1%86%A8%2003f33198c2dc40f3ab5d862940f403b1/Untitled.png

        • 해당 맵핑 핸들러가 우리가 설정한 Controller(url) 이나 get/post mapping 으로 지정한 핸들러를 찾아주는 객체

        • RequestMappingHandlerMapping 을 사용하여 핸들러를 찾아옴 [me.demo.HelloController#hello()]

          1%20DispatcherServlet%20%E1%84%80%E1%85%B5%E1%84%87%E1%85%A9%E1%86%AB%20%E1%84%83%E1%85%A9%E1%86%BC%E1%84%8C%E1%85%A1%E1%86%A8%2003f33198c2dc40f3ab5d862940f403b1/Untitled%201.png

      4. 핸들러 어뎁터를 찾아오는 영역, 핸들러 어뎁터는 핸들러를 실행해 주는 역할을 함

      5. 전달한 핸들러를 누가 실행할 수 있느냐를 찾는 과정

        • 현 상태에서는 디스패처서블릿이 아래 그림과 같은 어뎁터를 가지고 있음

          1%20DispatcherServlet%20%E1%84%80%E1%85%B5%E1%84%87%E1%85%A9%E1%86%AB%20%E1%84%83%E1%85%A9%E1%86%BC%E1%84%8C%E1%85%A1%E1%86%A8%2003f33198c2dc40f3ab5d862940f403b1/Untitled%202.png

        • 여기서는 RequestMappingHandlerAdapter 가 핸들러를 처리함

        • 핸들러를 실행할 수 있는 어뎁터를 찾음

      6. 핸들러와 어뎁터 둘다 찾았다면 실행 전 Get 요청인지 확인 후 캐싱 처리를 함(304 Not Modified 요청을 할지)

      7. HandlerAdapter 를 통해 핸들러를 처리함

    // DispatcherServlet.java
    @Override
    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        logRequest(request);
        ...

        try {
            doDispatch(request, response);
        }
        ...
    }
    ...
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        ...
        try{
            ...
            try {
                processedRequest = checkMultipart(request); // 1
                ...

                // Determine handler for the current request.
                mappedHandler = getHandler(processedRequest); // 2
                if (mappedHandler == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }
                ...
                // Determine handler adapter for the current request.
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // 4
                ...
                // Process last-modified header, if supported by the handler.
                String method = request.getMethod();
                boolean isGet = "GET".equals(method);
                if (isGet || "HEAD".equals(method)) { // 6
                    long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                    if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                        return;
                    }
                }
                ...
                // Actually invoke the handler.
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); // 7
                ...
            }
        }
  }
  ...
    @Nullable
    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { // 3
        if (this.handlerMappings != null) {
            for (HandlerMapping mapping : this.handlerMappings) {
                HandlerExecutionChain handler = mapping.getHandler(request);
                if (handler != null) {
                    return handler;
                }
            }
        }
        return null;
    }
    ...
    protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException { 
        if (this.handlerAdapters != null) {
            for (HandlerAdapter adapter : this.handlerAdapters) {
                if (adapter.supports(handler)) { // 5
                    return adapter;
                }
            }
            ...
        }
        ...
    }
  1. AbstractHandlerMethodAdapter / RequestMappingHandlerAdapter

    1. 실제 핸들러 메소드를 리플랙션 기능을 실행하는 invoke 메소드를 실행

      • handlerMethod 라는 객체 안에는 이미 해당 핸들러 메소드에 대한 내용이 있음

      • 리플랙션을 통해 해당 메소드를 호출함

        1%20DispatcherServlet%20%E1%84%80%E1%85%B5%E1%84%87%E1%85%A9%E1%86%AB%20%E1%84%83%E1%85%A9%E1%86%BC%E1%84%8C%E1%85%A1%E1%86%A8%2003f33198c2dc40f3ab5d862940f403b1/Untitled%203.png

      // AbstractHandlerMethodAdapter.java
       ...
       @Override
       @Nullable
       public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
               throws Exception {
      
           return handleInternal(request, response, (HandlerMethod) handler);
       }
       ...
      // RequestMappingHandlerAdapter.java
       ...
       @Override
       protected ModelAndView handleInternal(HttpServletRequest request,
               HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
           ...
           // Execute invokeHandlerMethod in synchronized block if required.
           if (this.synchronizeOnSession) {
               ...
           }
           else {
               // No synchronization on session demanded at all...
               mav = invokeHandlerMethod(request, response, handlerMethod); // 1
           }
       }
  2. HelloController

    1. invoke 메소드가 실행이 되면 우리가 작성한 실제 메소드가 실행됨
// HelloController.java
@RestController
public class HelloController {

    @Autowired
    private HelloService helloService;

    @GetMapping("/hello")
    public String hello() {
        return "hello, " + helloService.getName(); // 1
    }
}
  1. ServletInvocableHandlerMethod

    1. returnValue 에는 우리가 지정해둔 문자열 리턴값이 출력

      1%20DispatcherServlet%20%E1%84%80%E1%85%B5%E1%84%87%E1%85%A9%E1%86%AB%20%E1%84%83%E1%85%A9%E1%86%BC%E1%84%8C%E1%85%A1%E1%86%A8%2003f33198c2dc40f3ab5d862940f403b1/Untitled%204.png

    // ServletInvocableHandlerMethod.java
    public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
            Object... providedArgs) throws Exception {

        Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); // 1
        setResponseStatus(webRequest);
  1. HandlerMethodReturnValueHandlerComposite

    1. returnValueHandlers 를 찾음
      • returnValueHandlers 는 핸들러에서 리턴한 값을 어떻게 처리할꺼냐?
      • 문자열, ViewModel, ResponseEntity 등등을 리턴할 수 있기 때문
      • 해당 코드에서는 String 이면서도 RestController 이기 때문에 ResponseBody 어노테이션이 붙어있는 리턴값임
      • 그렇기 때문에 아래 여러개의 returnValueHandler 중 RequestResponseBodyMethodProcessor 가 처리를 담당함
      • RequestResponseBodyMethodProcessor 핸들러는 컨버터를 사용해서 http 본문에 넣어주는 처리를 해줌
        // HandlerMethodReturnValueHandlerComposite.java
        @Nullable
        private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
          boolean isAsyncValue = isAsyncReturnValue(value, returnType);
          for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) { // 1
              if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
                  continue;
              }
              if (handler.supportsReturnType(returnType)) {
                  return handler;
              }
          }
          return null;
        }
        1%20DispatcherServlet%20%E1%84%80%E1%85%B5%E1%84%87%E1%85%A9%E1%86%AB%20%E1%84%83%E1%85%A9%E1%86%BC%E1%84%8C%E1%85%A1%E1%86%A8%2003f33198c2dc40f3ab5d862940f403b1/Untitled%205.png
  2. 응답값 리턴~!

    1%20DispatcherServlet%20%E1%84%80%E1%85%B5%E1%84%87%E1%85%A9%E1%86%AB%20%E1%84%83%E1%85%A9%E1%86%BC%E1%84%8C%E1%85%A1%E1%86%A8%2003f33198c2dc40f3ab5d862940f403b1/Untitled%206.png

반응형