Spring DispatcherServletの紹介

Spring DispatcherServletの紹介

1. 前書き

簡単に言えば、Front Controllerデザインパターン,では、単一のコントローラーはresponsible for directing incoming HttpRequests to all of an application’s other controllers and handlersです。

SpringのDispatcherServletはこのパターンを実装しているため、HttpRequestsを適切なハンドラーに正しく調整する責任があります。

この記事では、examine the Spring DispatcherServlet’s request processing workflowと、このワークフローに参加するいくつかのインターフェイスを実装する方法について説明します。

2. DispatcherServletリクエスト処理

基本的に、Springアプリケーション内に実装されたDispatcherServlet handles an incoming HttpRequest, delegates the request, and processes that request according to the configured HandlerAdapter interfacesと、ハンドラー、コントローラーエンドポイント、および応答オブジェクトを指定する付随するアノテーション。

DispatcherServletがコンポーネントを処理する方法についてさらに詳しく見ていきましょう。

  • キーDispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTEの下のDispatcherServletに関連付けられたWebApplicationContextが検索され、プロセスのすべての要素で使用できるようになります。

  • DispatcherServletは、getHandler() –を使用して、ディスパッチャ用に構成されたHandlerAdapterインターフェースのすべての実装を検索します。検出および構成された各実装は、残りのプロセスを通じてhandle()を介して要求を処理します。

  • LocaleResolverはオプションでリクエストにバインドされ、プロセス内の要素がロケールを解決できるようにします

  • ThemeResolverはオプションでリクエストにバインドされ、ビューなどの要素が使用するテーマを決定できるようにします

  • MultipartResolverが指定されている場合、リクエストはMultipartFilesについて検査されます–見つかったものはすべてMultipartHttpServletRequestにラップされてさらに処理されます

  • WebApplicationContextで宣言されたHandlerExceptionResolver実装は、要求の処理中にスローされた例外をピックアップします

DispatcherServlethereを登録および設定するすべての方法について詳しく知ることができます。

3. HandlerAdapterインターフェース

HandlerAdapterインターフェースは、コントローラー、サーブレット、HttpRequests、およびいくつかの特定のインターフェースを介したHTTPパスの使用を容易にします。 The HandlerAdapter interface thus plays an essential role through the many stages of the DispatcherServlet request processing workflow

まず、各HandlerAdapter実装は、ディスパッチャのgetHandler()メソッドからHandlerExecutionChainに配置されます。 次に、実行チェーンが進むにつれて、これらの実装のそれぞれがHttpServletRequestオブジェクトをhandle()します。

次のセクションでは、最も重要で一般的に使用されるHandlerAdaptersのいくつかについて詳しく説明します。

3.1. マッピング

マッピングを理解するには、コントローラーがHandlerMappingインターフェースにとって非常に重要であるため、最初にコントローラーに注釈を付ける方法を確認する必要があります。

SimpleControllerHandlerAdapterを使用すると、@Controllerアノテーションなしでコントローラーを明示的に実装できます。

RequestMappingHandlerAdapterは、@RequestMappingアノテーション.でアノテーションが付けられたメソッドをサポートします

ここでは@Controllerアノテーションに焦点を当てますが、いくつかのexamples using the SimpleControllerHandlerAdapterを含む役立つリソースも利用できます。

関連付けられたWebApplicationContext内のThe @RequestMapping annotation sets the specific endpoint at which a handler will be available

‘/user/example'エンドポイントを公開および処理するControllerの例を見てみましょう。

@Controller
@RequestMapping("/user")
@ResponseBody
public class UserController {

    @GetMapping("/example")
    public User fetchUserExample() {
        // ...
    }
}

@RequestMappingアノテーションで指定されたパスは、HandlerMappingインターフェイスを介して内部的に管理されます。

URL構造は、当然、DispatcherServlet自体に関連しており、サーブレットマッピングによって決定されます。

したがって、DispatcherServletが「/」にマップされている場合、すべてのマッピングはそのマッピングの対象になります。

ただし、サーブレットマッピングが代わりに ‘/dispatcher‘である場合、@RequestMappingアノテーションはそのルートURLに相対的です。

サーブレットマッピングの場合はRemember that ‘/' is not the same as ‘/ '*! ‘/' is the default mapping and exposes all URL’s to the dispatcher’s area of responsibility.

‘/*' is confusing to a lot of newer Spring developers. 同じURLコンテキストを持つすべてのパスがディスパッチャの責任範囲内にあることを指定しているわけではありません。 代わりに、他のディスパッチャマッピングをオーバーライドして無視します。 したがって、「/ example」は404として表示されます!

そのため、非常に限られた状況*(フィルターの構成など)を除いて、‘/ 'は使用しないでください。

3.2. HTTPリクエストの処理

@Controllerまたは@RestControllerアノテーションで指定されたThe core responsibility of a DispatcherServlet is to dispatch incoming HttpRequests to the correct handlers

補足として、@Controller@RestControllerの主な違いは、応答の生成方法です。@RestControllerは、デフォルトで@ResponseBodyも定義します。

Springのコントローラーに関してさらに深く掘り下げた記事はhereにあります。

3.3. ViewResolverインターフェース

ViewResolverは、ApplicationContextオブジェクトの構成設定としてDispatcherServletに付加されます。

A ViewResolver determines both what kind of views are served by the dispatcher and from where they are served

以下は、JSPページをレンダリングするためにAppConfig に配置する構成の例です。

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

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

とても簡単です! これには3つの主要な部分があります。

  1. プレフィックスを設定します。これにより、デフォルトのURLパスが設定され、設定されたビューが検索されます。

  2. サフィックスを介して設定されるデフォルトのビュータイプ

  3. リゾルバにビュークラスを設定します。これにより、JSTLやTilesなどのテクノロジーをレンダリングビューに関連付けることができます。

One common question involves how precisely a dispatcher’s ViewResolverand the overall project directory structure are related。 基本を見てみましょう。

SpringのXML構成を使用したInternalViewResolverのパス構成の例を次に示します。

この例では、アプリケーションが次の場所でホストされていると想定します。

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インターフェース

The primary way to customize session, request, or cookie information for our dispatcher is through the LocaleResolver interface

CookieLocaleResolverは、Cookieを使用してステートレスアプリケーションプロパティを構成できるようにする実装です。 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) determines the relevant TimeZone object for the application Bean in question.

両方の方法は、上記のLocaleResolverの実装のそれぞれで使用できます。

3.5. ThemeResolverインターフェース

Springは、ビューにスタイルのテーマを提供します。

テーマを処理するようにディスパッチャを設定する方法を見てみましょう。

まず、let’s set up all the configuration necessary to find and use our static theme files。 実際のThemes自体を構成するには、ThemeSourceの静的リソースの場所を設定する必要があります(Themeオブジェクトには、これらのファイルで規定されているすべての構成情報が含まれています)。 これを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タグがビューに追加され、正しいスタイルが表示されます。

次のURLリクエストは、構成済みのThemeChangeIntercepter:に渡された「theme」パラメータを使用してexampleテーマをレンダリングします

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

3.6. MultipartResolverインターフェース

MultipartResolver実装は、マルチパートの要求を検査し、少なくとも1つのマルチパートが見つかった場合、プロセス内の他の要素によるさらなる処理のために、それらをMultipartHttpServletRequestでラップします。 AppConfigに追加:

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

MultipartResolver Beanを構成したので、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は、Webアプリケーション全体、単一のコントローラー、またはコントローラーのセットに対して均一なエラー処理を提供します。

To provide application-wide custom exception handling, create a class annotated with @ControllerAdvice

@ControllerAdvice
public class ExampleGlobalExceptionHandler {

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

@ExceptionHandlerで注釈が付けられたそのクラス内のすべてのメソッドは、ディスパッチャーの責任範囲内のすべてのコントローラーで使用できます。

DispatcherServlet’s ApplicationContextHandlerExceptionResolverインターフェースの実装は、そのディスパッチャの責任範囲whenever @ExceptionHandler is used as an annotationの下でintercept a specific controllerに利用可能であり、正しいクラスがパラメータとして渡されます。

@Controller
public class FooController{

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

handleException()メソッドは、例外CustomException1またはCustomException2のいずれかが発生した場合に、上記の例のFooControllerの例外ハンドラーとして機能するようになりました。

Here’sは、SpringWebアプリケーションでの例外処理についてさらに詳しく説明している記事です。

4. 結論

このチュートリアルでは、SpringのDispatcherServletとそれを構成するいくつかの方法を確認しました。

いつものように、このチュートリアルで使用されるソースコードはover on Githubで利用できます。