JUST WRITE

DispatcherSerlvet 본문

Programing/Spring

DispatcherSerlvet

천재보단범재 2021. 10. 15. 23:30
이 글은 Baeldung 사이트 'An Intro to the Spring DispatcherSerlvet'를 해석, 정리한 글입니다.

 

DispatcherSerlvet

DispatcherSerlvet

Spring에서 DispatcherSerlvet은 이런 Front Controller 디자인 패턴을 구현한 것이다.

DispatcherServlet은 HttpRequest를 관리한다.

HandlerAdapter

DispatcherServlet의 getHandler Method를 통해 HandlerAdapter를 구현한 것들을 찾는다.

HandlerAdapter에 정의한 대로 Request가 위임, 처리된다(handle Method로 처리)

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;

        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {
            ModelAndView mv = null;
            Exception dispatchException = null;

            try {
                processedRequest = checkMultipart(request);
                multipartRequestParsed = (processedRequest != request);

                // Determine handler for the current request.
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }

                // Determine handler adapter for the current request.
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

                // Process last-modified header, if supported by the handler.
                String method = request.getMethod();
                boolean isGet = "GET".equals(method);
                if (isGet || "HEAD".equals(method)) {
                    long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                    if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                        return;
                    }
                }

                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }

                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }

                
                applyDefaultViewName(processedRequest, mv);
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            }
            catch (Exception ex) {
                dispatchException = ex;
            }
            catch (Throwable err) {
                // As of 4.3, we're processing Errors thrown from handler methods as well,
                // making them available for @ExceptionHandler methods and other scenarios.
                dispatchException = new NestedServletException("Handler dispatch failed", err);
            }
            
            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
        }
        catch (Exception ex) {
            triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
        }
        catch (Throwable err) {
            triggerAfterCompletion(processedRequest, response, mappedHandler,
                    new NestedServletException("Handler processing failed", err));
        }
        finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                // Instead of postHandle and afterCompletion
                if (mappedHandler != null) {
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            }
            else {
                // Clean up any resources used by a multipart request.
                if (multipartRequestParsed) {
                    cleanupMultipart(processedRequest);
                }
            }
        }
    }

handle Method는 특별한 HttpRequest를 처리할 때 사용된다.

public interface HandlerAdapter {
    boolean supports(Object handler);
    
    ModelAndView handle(
      HttpServletRequest request,
      HttpServletResponse response, 
      Object handler) throws Exception;
    
    long getLastModified(HttpServletRequest request, Object handler);
}

Mapping

URL Path에 따라 HttpRequest가 어디에서 어떻게 처리될지 결정된다.

@RequestMapping Annotation을 통해서 특별한 endpoint를 지정할 수 있다.

URL 구조는 DispatcherSerlvet 기준으로 상대적이다.

주의! '/'와 '/*'는 다르다. '/'는 DispatcherServlet 영역 내 기준 URL일 뿐이다.

@Controller
@RequestMapping("/user")
@ResponseBody
public class UserController {
 
    @GetMapping("/example")
    public User fetchUserExample() {
        // ...
    }
}

HTTP Request Handling

@RestController, @Controller로 정의된 Handler로 HTTP Request를 전달한다.

Spring Controller는 따로 글로 정리할 예정이다.

ViewResolver

ViewResolver는 ApplicationContext에 정의되어 DispatcherServlet과 연결되어 있다.

어떤 종류 View가 제공될지 결정한다.

 

@Configuration
@EnableWebMvc
@ComponentScan("com.baeldung.springdispatcherservlet")
public class AppConfig implements WebMvcConfigurer {

    @Bean
    public UrlBasedViewResolver viewResolver() {
        UrlBasedViewResolver resolver
          = new UrlBasedViewResolver();
        resolver.setPrefix("/WEB-INF/view/");
        resolver.setSuffix(".jsp");
        resolver.setViewClass(JstlView.class);
        return resolver;
    }
}

ViewResolver와 프로젝트 Directory 구조가 연관이 많이 되어 있다.

View의 기본 위치는 WEB-INF 하위이다.

일반적인 Spring + Maven Project Directory 구조
src -|
      main -|
                java
                resources
                webapp -|
                             jsp
                             WEB-INF

LocaleResolver

Session, Request, Cookie 정보를 커스텀하는 1차적 방법은 LocaleResolver를 통해서다.

@Bean
public CookieLocaleResolver cookieLocaleResolverExample() {
    CookieLocaleResolver localeResolver 
      = new CookieLocaleResolver();
    localeResolver.setDefaultLocale(Locale.ENGLISH);
    localeResolver.setCookieName("locale-cookie-resolver-example");
    localeResolver.setCookieMaxAge(3600);
    return localeResolver;
}

@Bean 
public LocaleResolver sessionLocaleResolver() { 
    SessionLocaleResolver localeResolver = new SessionLocaleResolver(); 
    localeResolver.setDefaultLocale(Locale.US); 
    localResolver.setDefaultTimeZone(TimeZone.getTimeZone("UTC"));
    return localeResolver; 
}

ThemeResolver

Spring은 View에 Theme을 제공해준다.

ThemResolver를 통해 View에 Theme을 설정할 수 있다.

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/resources/**")
      .addResourceLocations("/", "/resources/")
      .setCachePeriod(3600)
      .resourceChain(true)
      .addResolver(new PathResourceResolver());
}

@Bean
public ResourceBundleThemeSource themeSource() {
    ResourceBundleThemeSource themeSource
      = new ResourceBundleThemeSource();
    themeSource.setDefaultEncoding("UTF-8");
    themeSource.setBasenamePrefix("themes.");
    return themeSource;
}

@Bean
public CookieThemeResolver themeResolver() {
    CookieThemeResolver resolver = new CookieThemeResolver();
    resolver.setDefaultThemeName("example");
    resolver.setCookieName("example-theme-cookie");
    return resolver;
}

@Bean
public ThemeChangeInterceptor themeChangeInterceptor() {
   ThemeChangeInterceptor interceptor
     = new ThemeChangeInterceptor();
   interceptor.setParamName("theme");
   return interceptor;
}

@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(themeChangeInterceptor());
}
<link rel="stylesheet" href="${ctx}/<spring:theme code='styleSheet'/>" type="text/css"/>

MultipartResolver

MultipartHttpServletRequest를 처리한다.

@Bean
public CommonsMultipartResolver multipartResolver() 
  throws IOException {
    CommonsMultipartResolver resolver
      = new CommonsMultipartResolver();
    resolver.setMaxUploadSize(10000000);
    return resolver;
}

HandlerExceptionResolver

HandlerExceptionResolver는 Web Application에서 발생하는 모든 에러를 처리하기 위해 사용된다.

@ControllerAdvice Annotation을 통해 모든 에러 처리를 Custom 할 수 있다.

@ExceptionHandler Annotation으로 모든 Controller에서 오는 것을 처리한다.

@ControllerAdvice
public class ExampleGlobalExceptionHandler {

    @ExceptionHandler
    @ResponseBody 
    public String handleExampleException(Exception e) {
        // ...
    }
}

아래처럼 특정 Controller에서 Exception을 가로챌 수도 있다.

CustomException1, CustomException2가 발생하면 아래 handleException Method가 처리할 것이다.

@Controller
public class FooController{

    @ExceptionHandler({ CustomException1.class, CustomException2.class })
    public void handleException() {
        // ...
    }
    // ...
}

[주요 용어]

Front Controller 디자인 패턴

1개의 Controller가 Application의 모든 HttpRequest를 다른 Controller나 Handler로 보내도록 하는 디자인 패턴

 

728x90
반응형

'Programing > Spring' 카테고리의 다른 글

Bean Annotations  (0) 2021.12.20
@Autowired  (0) 2021.10.22
MappingJackson2JsonView  (0) 2021.10.09
Singleton Registry  (0) 2021.10.02
Bean LifeCycle  (0) 2021.09.29
Comments