1. 소개

간단히 말해, Front Controller 디자인 패턴 에서 단일 컨트롤러는 들어오는 HttpRequest 를 모든 애플리케이션의 다른 컨트롤러 및 핸들러로 보내는 역할을 합니다.

Spring의 DispatcherServlet 은이 패턴을 구현하므로 HttpRequests 를 올바른 핸들러로 올바르게 조정해야합니다 .

이 기사에서는 Spring DispatcherServlet의 요청 처리 워크 플로 와이 워크 플로에 참여하는 여러 인터페이스를 구현하는 방법을 살펴 보겠습니다.

2. DispatcherServlet 요청 처리

기본적으로 DispatcherServlet 은 들어오는 HttpRequest를 처리 하고 요청을 위임 하며 Spring 애플리케이션 내에서 구현 된 구성된 HandlerAdapter 인터페이스 에 따라 해당 요청을 처리하고 핸들러, 컨트롤러 엔드 포인트 및 응답 객체를 지정하는 수반되는 어노테이션을 처리합니다.

DispatcherServlet 이 구성 요소를 처리하는 방법에 대해 더 자세히 살펴 보겠습니다 .

  • WebApplicationContext를 (A)에 연결된 DispatcherServlet이 키는 아래 DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE이 프로세스의 모든 요소를 검색하고 이용할 수있다
  • DispatcherServlet은 의 모든 구현 발견 HandlerAdapter를 사용하여 운영자에 대해 구성된 인터페이스 - 및 getHandler ()를 각각 발견하고 구성 구현 핸들을 통해 요청 핸들을 () 프로세스의 나머지 부분을 통해
  • LocaleResolver의은 임의로 로케일을 해결하는 과정에있는 요소를 사용하는 요청에 바인딩
  • ThemeResolver은 임의로 같은 뷰와 같은 요소를 사용할 수 있도록 테마를 결정하라는 요청에 바인딩
  • 경우 MultipartResolver가 지정된 요청을 위해 검사된다 의 MultipartFile S - 발견 된 모든은 래핑된다 MultipartHttpServletRequest를 추가 처리
  • WebApplicationContext 에서 선언 된 HandlerExceptionResolver 구현은 요청 처리 중에 throw되는 예외를 선택합니다.

여기 에서 DispatcherServlet 을 등록하고 설정하는 모든 방법에 대해 자세히 알아볼 수 있습니다 .

3. HandlerAdapter 인터페이스

HandlerAdapter의 인터페이스 컨트롤러, 서블릿의 사용을 용이하게 HttpRequests을 몇 가지 특정 인터페이스를 통해 경로 및 HTTP. 따라서 HandlerAdapter 인터페이스는 DispatcherServlet 요청 처리 워크 플로 의 여러 단계에서 필수적인 역할을합니다 .

먼저 각 HandlerAdapter 구현은 디스패처의 getHandler () 메서드 에서 HandlerExecutionChain에 배치됩니다 . 그런 다음 각 구현 은 실행 체인이 진행됨에 따라 HttpServletRequest 객체를 핸들 () 합니다 .

다음 섹션에서는 가장 중요하고 일반적으로 사용되는 몇 가지 HandlerAdapter 를 자세히 살펴 봅니다.

3.1. 매핑

매핑을 이해하려면 컨트롤러가 HandlerMapping 인터페이스에 매우 중요하기 때문에 컨트롤러에 어노테이션을 추가하는 방법을 먼저 살펴 봐야합니다 .

SimpleControllerHandlerAdapter는 없이 명시 적으로 컨트롤러의 구현을 가능하게 @Controller의 어노테이션.

RequestMappingHandlerAdapter의 지원 방법은 어노테이션 @RequestMapping의 어노테이션 .

여기서는 @Controller 어노테이션 에 초점을 맞출 것이지만 SimpleControllerHandlerAdapter를 사용하는 여러 예제가 있는 유용한 리소스 도 사용할 수 있습니다.

@RequestMapping 어노테이션은 핸들러가 사용할 수있는 특정 엔드 포인트 설정 내에서 의 WebApplicationContext 와 연관된를.

'/ user / example' 엔드 포인트 를 노출하고 처리 하는 컨트롤러 의 예를 살펴 보겠습니다 .

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

@RequestMapping 어노테이션에 지정된 경로 HandlerMapping 인터페이스 를 통해 내부적으로 관리 됩니다.

URL 구조는 당연히 DispatcherServlet 자체에 상대적 이며 서블릿 매핑에 의해 결정됩니다.

따라서 DispatcherServlet 이 '/'에 매핑되면 모든 매핑이 해당 매핑에 포함됩니다.

그러나 서블릿 매핑이 대신 ' / dispatcher '인 경우 @ RequestMapping 어노테이션은 해당 루트 URL에 상대적이됩니다.

'/'는 서블릿 매핑의 '/ *'와 동일하지 않습니다 ! '/'는 기본 매핑이며 모든 URL을 디스패처의 책임 영역에 노출합니다.

'/ *'는 많은 새로운 Spring 개발자들에게 혼란 스럽습니다. 동일한 URL 컨텍스트를 가진 모든 경로가 디스패처의 책임 영역에 있음을 지정하지는 않습니다. 대신 다른 디스패처 매핑을 무시하고 무시합니다. 따라서 '/ example'은 404로 표시됩니다!

따라서 '/ *'는 매우 제한된 상황 (예 : 필터 구성)을 제외하고 사용해서는 안됩니다 .

3.2. HTTP 요청 처리

DispatcherServlet 의 핵심 책임은 들어오는 HttpRequest@Controller 또는 @RestController 어노테이션으로 지정된 올바른 핸들러 로 디스패치하는 것 입니다.

참고로 @Controller@RestController 의 주요 차이점 은 응답이 생성 되는 방식입니다. @RestController 는 기본적으로 @ResponseBody 도 정의합니다 .

Spring의 컨트롤러에 대해 훨씬 더 깊이 들어가는 글은 여기 에서 찾을 수 있습니다 .

3.3. 의 ViewResolver 인터페이스

의 ViewResolver는 (A)에 부착된다 DispatcherServlet을 온 구성 설정 등 의 ApplicationContext 개체.

의 ViewResolver는 결정의 종류 뷰의 디스패처에 의해 그들이 제공하는 곳에서 봉사하는 것을 모두 .

다음은 JSP 페이지를 렌더링하기 위해 AppConfig 배치 할 구성의 예입니다 .

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

매우 간단합니다! 여기에는 세 가지 주요 부분이 있습니다.

  1. 프리픽스 설정, 내에서 설정된 뷰를 찾기위한 기본 URL 경로를 설정합니다.
  2. 접미사를 통해 설정되는 기본보기 유형
  3. JSTL 또는 Tiles와 같은 기술이 렌더링 된 뷰와 연관 될 수 있도록 리졸버에 뷰 클래스 설정

한 가지 일반적인 질문은 디스패처의 ViewResolver 와 전체 프로젝트 디렉토리 구조가 얼마나 정확하게 관련되어 있는지 와 관련이 있습니다 . 기본 사항을 살펴 보겠습니다.

다음은 Spring의 XML 구성을 사용 하는 InternalViewResolver에 대한 예제 경로 구성입니다 .

<property name="prefix" value="/jsp/"/>

예제를 위해 애플리케이션이 다음에서 호스팅되고 있다고 가정합니다.

http://localhost:8080/

이것은 로컬로 호스팅되는 Apache Tomcat 서버의 기본 주소 및 포트입니다.

애플리케이션이 dispatcherexample-1.0.0 이라고 가정하면 다음에서 JSP 뷰에 액세스 할 수 있습니다.

http://localhost:8080/dispatcherexample-1.0.0/jsp/

Maven을 사용하는 일반 Spring 프로젝트에서 이러한 뷰의 경로는 다음과 같습니다.

src -|
     main -|
            java
            resources
            webapp -|
                    jsp
                    WEB-INF

보기의 기본 위치는 WEB-INF 내에 있습니다. 위의 스 니펫에서 InternalViewResolver대해 지정된 경로 는 뷰를 사용할 수있는 'src / main / webapp'의 하위 디렉토리를 결정합니다.

3.4. LocaleResolver의 인터페이스

디스패처에 대한 세션, 요청 또는 쿠키 정보를 사용자 정의하는 기본 방법은 LocaleResolver 인터페이스를 사용하는 것 입니다.

CookieLocaleResolver 는 쿠키를 사용하여 상태 비 저장 애플리케이션 속성의 구성을 허용하는 구현입니다. AppConfig에 추가해 보겠습니다 .

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

SessionLocaleResolver 는 상태 저장 애플리케이션에서 세션 별 구성을 허용합니다.

setDefaultLocale 반면 () 메소드는, 지리적, 정치적, 또는 문화적 영역을 나타내는 setDefaultTimeZone은 ( ) 관련 결정 시간대의 응용 프로그램에 대한 객체 Bean 문제를.

위의 LocaleResolver 구현 각각에서 두 메서드를 모두 사용할 수 있습니다 .

3.5. ThemeResolver 인터페이스

Spring은 뷰에 대한 스타일 테마를 제공합니다.

테마를 처리하도록 디스패처를 구성하는 방법을 살펴 보겠습니다.

먼저 정적 테마 파일을 찾아 사용하는 데 필요한 모든 구성을 설정해 보겠습니다 . 실제 테마 자체 를 구성하려면 ThemeSource대한 정적 리소스 위치를 설정해야 합니다 ( 테마 개체에는 해당 파일에 규정 된 모든 구성 정보가 포함됨 ). 이것을 AppConfig에 추가하십시오 .

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

DispatcherServlet에서 관리하는 요청 ThemeChangeInterceptor 객체 에서 사용할 수있는 setParamName ()에 전달 된 지정된 매개 변수를 통해 테마를 수정할 수 있습니다 . AppConfig에 추가 :

@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());
}

다음 JSP 태그가 뷰에 추가되어 올바른 스타일이 표시됩니다.

<link rel="stylesheet" href="${ctx}/<spring:theme code='styleSheet'/>" type="text/css"/>

다음 URL 요청은 구성된 ThemeChangeIntercepter에 전달 된 'theme'매개 변수를 사용하여 예제 테마를 렌더링합니다 .

http://localhost:8080/dispatcherexample-1.0.0/?theme=example

3.6. MultipartResolver 인터페이스

MultipartResolver의 구현은 멀티 파트에 대한 요청을 검사하고, 그들을 감싸는 MultipartHttpServletRequest를 상기 적어도 하나 개의 멀티 찾으면 과정에서 다른 요소들에 의해 추가적으로 처리. AppConfig에 추가 :

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

이제 MultipartResolver 빈을 구성 했으므로 MultipartFile 요청 을 처리 할 컨트롤러를 설정하겠습니다 .

@Controller
public class MultipartController {

    @Autowired
    ServletContext context;

    @PostMapping("/upload")
    public ModelAndView FileuploadController(
      @RequestParam("file") MultipartFile file) 
      throws IOException {
        ModelAndView modelAndView = new ModelAndView("index");
        InputStream in = file.getInputStream();
        String path = new File(".").getAbsolutePath();
        FileOutputStream f = new FileOutputStream(
          path.substring(0, path.length()-1)
          + "/uploads/" + file.getOriginalFilename());
        int ch;
        while ((ch = in.read()) != -1) {
            f.write(ch);
        }
        f.flush();
        f.close();
        in.close();
        modelAndView.getModel()
          .put("message", "File uploaded successfully!");
        return modelAndView;
    }
}

일반 형식을 사용하여 지정된 엔드 포인트에 파일을 제출할 수 있습니다. 업로드 된 파일은 'CATALINA_HOME / bin / uploads'에서 사용할 수 있습니다.

3.7. HandlerExceptionResolver 인터페이스

Spring의 HandlerExceptionResolver 는 전체 웹 애플리케이션, 단일 컨트롤러 또는 컨트롤러 세트에 대해 균일 한 오류 처리를 제공합니다.

응용 프로그램 전체의 사용자 지정 예외 처리를 제공하려면 @ControllerAdvice 어노테이션이 달린 클래스를 만듭니다 .

@ControllerAdvice
public class ExampleGlobalExceptionHandler {

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

@ExceptionHandler로 어노테이션이 달린 해당 클래스 내의 모든 메서드 는 디스패처의 책임 영역 내의 모든 컨트롤러에서 사용할 수 있습니다.

DispatcherServlet의 ApplicationContext 에있는 HandlerExceptionResolver 인터페이스의 구현은 @ExceptionHandler 가 어노테이션으로 사용되고 올바른 클래스가 매개 변수로 전달 될 때마다 해당 디스패처의 책임 영역에서 특정 컨트롤러가로 챌 수 있습니다 .

@Controller
public class FooController{

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

이제 handleException () 메서드는 예외 CustomException1 또는 CustomException2 가 발생하는 경우 위의 예제에서 FooController대한 예외 처리기 역할을 합니다.

다음 은 Spring 웹 애플리케이션에서 예외 처리에 대해 자세히 설명하는 기사입니다.

4. 결론

이 예제에서는 Spring의 DispatcherServlet 과이를 구성하는 몇 가지 방법을 검토 했습니다.

항상 그렇듯이이 예제에 사용 된 소스 코드는 Github에서 사용할 수 있습니다 .