Spring

DispatcherServlet이란

라우브 2020. 8. 2. 17:25

DispatcherServlet이란?

모든 url 마다 서블릿을 생성하고, 그것을 web.xml에 등록하고 하는 과정이 굉장히 비효율적이었고,

이를 해결하기 위해 Front Conroller 개념이 도입되었다.

Front Controller는 모든 Http 요청을 받아서, 해당 요청을 처리할 Handler들에게 분배하는 역할을 한다.

Spring에서 이 Front Controller가 바로 DispatcherServlet이다.

 

 

DispatcherServlet 동작 원리

DispatcherServlet 초기화

  • 아래의 특별한 타입의 빈들을 찾거나, 기본 전략에 해당하는 빈들을 등록한다.
  • HandlerMapping: 핸들러를 찾아주는 인터페이스
  • HanlderAdapter: 핸들러를  실행하는 인터페이스
  • HandlerExceptionResolver
  • ViewResolver

DispatcherServlet 동작 순서

  1. 요청을 분석한다. (로케일, 테마, 멀티파트 등등)
  2. (HandlerMapping에게 위임하여) 요청을 처리할 핸들러를 찾는다.
  3. (등록되어 있는 HandlerAdapter 중에) 해당 핸들러를 실행할 수 있는 HandlerAdapter를 찾는다.
  4. 찾아낸 HandlerAdapter를 사용하여, 핸들러의 응답을 처리한다.
    • 핸들러의 리턴값을 보고 어떻게 처리할지 판단.
      • 뷰 이름에 해당하는 뷰를 찾아서 모델 데이터를 랜더링.
      • @ResponseEntity가 있다면 Converter를 사용해서 응답 본문을 생성.
  5. (부가적으로) 예외가 발생한다면, 예외 처리 핸들러에 요청 처리를 위임.
  6. 최종적으로 응답을 보냄.

 

https://hyesun03.github.io/2019/04/05/spring-dispatcher-servlet/

 

Dispatcher Servlet의 기본 Handler Mapping

우리가 아무것도 등록하지 않아도, Dispatcher Servlet은 두개의 Handler Mapping을 기본적으로 가지고 있다.

  • BeanNameUrlHandlerMapping : Bean 이름을 기반으로 Handler Mapping
  • RequestMappingHandlerMapping : 어노테이션 기반으로 Handler Mapping

이 두가지를 이용하여, 해당 요청을 처리할 수 있는 핸들러를 찾는다. 

 

Dispatcher Servlet의 기본 Handler Adapter

  • HttpRequestHandlerAdapter
  • SimpleControllerHandlerAdapter
  • RequestMappingHandlerAdapter

Dispatcher Servlet의 내부 동작

나는 HandlerMapping이니, HandlerAdapter니 아무것도 등록한 적 없는데 어떻게 사용할 수 있는 것일까?

Dispatcher Servlet 클래스를 타고 들어가 보면 아래와 같은 초기화 메서드를 볼 수 있다.

 protected void initStrategies(ApplicationContext context) {
        this.initMultipartResolver(context);
        this.initLocaleResolver(context);
        this.initThemeResolver(context);
        this.initHandlerMappings(context);
        this.initHandlerAdapters(context);
        this.initHandlerExceptionResolvers(context);
        this.initRequestToViewNameTranslator(context);
        this.initViewResolvers(context);
        this.initFlashMapManager(context);
    }

코드를 보면 알 수 있듯, Dispatcher Servlet이 생성될 때 전략들을 초기화 한다.

동작 메커니즘은 비슷하므로 대표적으로 initViewResolvers를 타고 들어가보면, 다음과 같다.

private void initViewResolvers(ApplicationContext context) {
        this.viewResolvers = null;
        if (this.detectAllViewResolvers) {
            Map<String, ViewResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false);
            if (!matchingBeans.isEmpty()) {
                this.viewResolvers = new ArrayList(matchingBeans.values());
                AnnotationAwareOrderComparator.sort(this.viewResolvers);
            }
        } else {
            try {
                ViewResolver vr = (ViewResolver)context.getBean("viewResolver", ViewResolver.class);
                this.viewResolvers = Collections.singletonList(vr);
            } catch (NoSuchBeanDefinitionException var3) {
            }
        }

        if (this.viewResolvers == null) {
            this.viewResolvers = this.getDefaultStrategies(context, ViewResolver.class);
            if (this.logger.isTraceEnabled()) {
                this.logger.trace("No ViewResolvers declared for servlet '" + this.getServletName() + "': using default strategies from DispatcherServlet.properties");
            }
        }

기본적으로 detectAllViewResolvers는 true로 설정되어있으므로, 첫번째 if문으로 들어가서,  구현되어 있는 viewResolver 객체들을 viewResolvers 객체에 삽입한다. 물론 없다면 null이 될 것이다. 그 어떤 viewResolver도 따로 구현하지 않았다면, viewResolvers가 null을 반환하기 때문에, 마지막 if 문을 실행한다. 마지막 if 문은 viewResolvers객체에 default ViewResolver를 삽입하는 코드이다.

이때, 디버거로 로그를 찍어보면 default viewResolver로 InternalResourceViewResolver가 들어왔으며, 해당 객체가 default view Resolver임을 알 수 있다.

이후 다음과 같이 커스텀 뷰 리졸버를 생성해 보았다.

이후 다시 디버거를 통해 로그를 찍어보니, 마지막 if 문 (this.viewResolvers == null) 을 실행하지 않았고, 

아래와 같이 내가 지정해 준 prefix, suffix가 잘 등록된 것을 알 수 있었다.

'Spring' 카테고리의 다른 글

Database Transaction과 데이터 동기화 -1  (0) 2021.08.01
스프링 MVC 구성 요소  (0) 2020.08.02
서블릿 리스너와 서블릿 필터  (0) 2020.08.01
서블릿이란 ?  (0) 2020.07.31
스프링 MVC 소개  (0) 2020.07.30