1. 序章

簡単に言えば、フロントコントローラーデザインパターンでは、単一のコントローラーがアプリケーションの他のすべてのコントローラーとハンドラーに着信HttpRequestsを送信する責任があります。

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

この記事では、 Spring DispatcherServletのリクエスト処理ワークフローと、このワークフローに参加するいくつかのインターフェイスを実装する方法について説明します。

2. DispatcherServletリクエスト処理

基本的に、 DispatcherServletは、着信HttpRequestを処理し、要求を委任し、Springアプリケーション内に実装された構成済みのHandlerAdapterインターフェイスと、ハンドラー、コントローラーエンドポイント、および応答オブジェクトを指定する付随する注釈に従って、その要求を処理します。 。

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

  • キーDispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTEの下のDispatcherServletに関連付けられたWebApplicationContextが検索され、プロセスのすべての要素で使用できるようになります。
  • DispatcherServlet は、 getHandler()を使用してディスパッチャー用に構成された HandlerAdapter インターフェースのすべての実装を検索します– 検出および構成された各実装は、 handle( )残りのプロセスを通じて
  • LocaleResolver はオプションでリクエストにバインドされ、プロセス内の要素がロケールを解決できるようにします
  • ThemeResolver は、オプションでリクエストにバインドされ、ビューなどの要素が使用するテーマを決定できるようにします
  • MultipartResolver が指定されている場合、リクエストは MultipartFile について検査されます。見つかったものはすべて、MultipartHttpServletRequestにラップされてさらに処理されます。
  • HandlerExceptionResolver 実装は、 WebApplicationContext で宣言され、リクエストの処理中にスローされた例外を取得します

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

3. HandlerAdapterインターフェース

HandlerAdapter インターフェースは、コントローラー、サーブレット、 HttpRequests 、およびいくつかの特定のインターフェースを介したHTTPパスの使用を容易にします。 したがって、HandlerAdapterインターフェイスは、DispatcherServlet要求処理ワークフローの多くの段階で重要な役割を果たします。

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

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

3.1. マッピング

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

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

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

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

@RequestMappingアノテーションは、それに関連付けられたWebApplicationContext内でハンドラーが使用可能になる特定のエンドポイントを設定します。

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

@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[も定義します。 X188X]デフォルトで。

Springのコントローラーに関する詳細については、こちらをご覧ください。

3.3. ViewResolverインターフェース

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

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

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

  1. プレフィックスを設定します。これにより、設定されたビューを検索するためのデフォルトのURLパスが設定されます。
  2. 接尾辞を介して設定されるデフォルトのビュータイプ
  3. JSTLやTilesなどのテクノロジーをレンダリングされたビューに関連付けることができるリゾルバーにビュークラスを設定する

よくある質問の1つは、ディスパッチャのViewResolverとプロジェクトディレクトリ構造全体がどの程度正確に関連しているかです。 基本を見てみましょう。

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

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

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

http://localhost:8080/

これは、ローカルでホストされているApacheTomcatサーバーのデフォルトのアドレスとポートです。

アプリケーションの名前が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インターフェース

ディスパッチャのセッション、リクエスト、またはCookie情報をカスタマイズする主な方法は、LocaleResolverインターフェイスを使用することです。

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 )は、問題のアプリケーションBeanに関連するTimeZoneオブジェクトを決定します。

両方のメソッドは、LocaleResolverの上記の各実装で使用できます。

3.5. ThemeResolverインターフェース

春は私たちの見解に様式的なテーマを提供します。

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

まず、静的テーマファイルを見つけて使用するために必要なすべての構成をセットアップしましょう。 ThemeSource の静的リソースの場所を設定して、実際の Themes 自体を構成する必要があります( 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 に渡された指定されたパラメータを介してテーマを変更できます setParamName ()で利用可能 ThemeChangeInterceptor 物体追加 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’パラメーターを使用してexampleテーマをレンダリングします。

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

3.6. MultipartResolverインターフェース

MultipartResolver 実装は、マルチパートのリクエストを検査し、それらを MultipartHttpServletRequest でラップして、少なくとも1つのマルチパートが見つかった場合にプロセス内の他の要素によるさらなる処理を行います。 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アプリケーション全体、単一のコントローラー、またはコントローラーのセットに対して均一なエラー処理を提供します。

アプリケーション全体のカスタム例外処理を提供するには、@ControllerAdviceで注釈が付けられたクラスを作成します。

@ControllerAdvice
public class ExampleGlobalExceptionHandler {

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

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

DispatcherServletのApplicationContextHandlerExceptionResolverインターフェースの実装は、@ExceptionHandlerが注釈、および正しいクラスがパラメーターとして渡されます。

@Controller
public class FooController{

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

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

ここにSpringWebアプリケーションでの例外処理についてさらに詳しく説明した記事があります。

4. 結論

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

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