Spring Webコンテキスト

1前書き

WebアプリケーションでSpringを使用する場合、それをすべて結び付けるアプリケーションコンテキストを編成するためのいくつかのオプションがあります。

この記事では、Springが提供する最も一般的なオプションを分析して説明します。

2.ルートWebアプリケーションのコンテキスト

すべてのSpring Webアプリケーションには、ライフサイクルに関連付けられた関連アプリケーションコンテキスト、つまりルートWebアプリケーションコンテキストがあります。

これはSpring Web MVCよりも前の古い機能であるため、特にWebフレームワークテクノロジとは関係ありません。

サーブレットコンテキストリスナのおかげで、コンテキストはアプリケーションの起動時に開始され、停止時に破棄されます。すべての ApplicationContext 実装にこの機能があるわけではありませんが、最も一般的な種類のコンテキストも実行時に更新できます。

Webアプリケーションのコンテキストは常に WebApplicationContext のインスタンスです。これは、 ServletContext にアクセスするための規約で ApplicationContext を拡張するインターフェースです。

とにかく、通常、アプリケーションはこれらの実装の詳細を気にする必要はありません。

** 2.1. ContextLoaderListener

**

前のセクションで説明したルートWebアプリケーションコンテキストは、 spring-web モジュールの一部であるクラス org.springframework.web.context.ContextLoaderListener のリスナーによって管理されます。

  • デフォルトでは、リスナーは__/WEB-INF/applicationContext.xmlからXMLアプリケーションコンテキストをロードします。ただし、これらのデフォルトは変更できます。たとえば、XMLの代わりにJavaアノテーションを使用できます。

このリスナーは、Webアプリケーション記述子( web.xml ファイル)またはサーブレット3.x環境のプログラムで構成できます。

次のセクションでは、これらの各オプションについて詳しく説明します。

** 2.2. web.xml とXMLアプリケーションコンテキストの使用

**

web.xml を使用する場合は、通常どおりリスナーを構成します。

<listener>
    <listener-class>
        org.springframework.web.context.ContextLoaderListener
    </listener-class>
</listener>

contextConfigLocation パラメーターを使用して、XMLコンテキスト構成の別の場所を指定できます。

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/rootApplicationContext.xml</param-value>
</context-param>

または、コンマで区切られた複数の場所

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/context1.xml,/WEB-INF/context2.xml</param-value>
</context-param>

パターンを使うことさえできます。

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/** -context.xml</param-value>
</context-param>

いずれにせよ、 指定された場所からロードされたすべてのBean定義を組み合わせることによって 1つのコンテキストのみが定義されます。

2.3. web.xml とJavaアプリケーションコンテキストの使用

デフォルトのXMLベースのコンテキスト以外に、他の種類のコンテキストを指定することもできます。たとえば、代わりにJavaアノテーション設定を使用する方法を見てみましょう。

どのタイプのコンテキストをインスタンス化するかをリスナーに指示するには、 contextClass パラメーターを使用します。

<context-param>
    <param-name>contextClass</param-name>
    <param-value>
        org.springframework.web.context.support.AnnotationConfigWebApplicationContext
    </param-value>
</context-param>
  • 私たちの場合、 AnnotationConfigWebApplicationContext はそれを持っていませんので、それを提供しなければなりません。

したがって、1つ以上のアノテーション付きクラスをリストすることができます。

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
        com.baeldung.contexts.config.RootApplicationConfig,
        com.baeldung.contexts.config.NormalWebAppConfig
    </param-value>
</context-param>

または、コンテキストに1つ以上のパッケージをスキャンするように指示することもできます。

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>org.baeldung.bean.config</param-value>
</context-param>

そしてもちろん、2つのオプションを組み合わせて組み合わせることもできます。

2.4. ** Servlet 3.xを使ったプログラムによる設定

  • バージョン3のサーブレットAPIは web.xml ファイルによる設定を完全にオプションとしています** ライブラリは、リスナ、フィルタ、サーブレットなどを登録できるXML設定の一部であるWebフラグメントを提供できます。

また、ユーザーは、サーブレットベースのアプリケーションのすべての要素をプログラム的に定義できるAPIにアクセスできます。

spring-web モジュールはこれらの機能を利用し、起動時にアプリケーションのコンポーネントを登録するためのAPIを提供します。

Springはアプリケーションのクラスパスをスキャンして org.springframework.web.WebApplicationInitializer クラスのインスタンスを探します。これは単一のメソッドを持つインターフェースで、__void onStartup(ServletContext servletContext)はServletExceptionをスローします。これはアプリケーションの起動時に呼び出されます。

ここで、この機能を使用して、以前に見たのと同じタイプのルートWebアプリケーションコンテキストを作成する方法を見てみましょう。

2.5. サーブレット3.xとXMLアプリケーションコンテキストの使用

セクション2.2のように、XMLコンテキストから始めましょう。

前述の onStartup メソッドを実装します。

public class ApplicationInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext servletContext)
      throws ServletException {
       //...
    }
}
  • 実装を行ごとに分けてみましょう。

まずルートコンテキストを作成します。 XMLを使いたいので、それはXMLベースのアプリケーションコンテキストでなければなりません、そして我々はWeb環境にいるので、それは同様に WebApplicationContext を実装しなければなりません。

したがって、最初の行は、前に出てきた明示的な contextClass パラメータのバージョンです。これを使って、どの特定のコンテキスト実装を使用するかを決定します。

XmlWebApplicationContext rootContext = new XmlWebApplicationContext();

次に、2行目で、コンテキストからBean定義のロード元を指定します。 setConfigLocations は、プログラム的には web.xml contextConfigLocation パラメータと類似しています。

rootContext.setConfigLocations("/WEB-INF/rootApplicationContext.xml");

最後に、ルートコンテキストで ContextLoaderListener を作成し、それをサーブレットコンテナに登録します。ご覧のとおり、 ContextLoaderListener には WebApplicationContext を受け取り、それをアプリケーションで使用できるようにする適切なコンストラクタがあります。

servletContext.addListener(new ContextLoaderListener(rootContext));

2.6. サーブレット3.xとJavaアプリケーションコンテキストの使用

注釈ベースのコンテキストを使用したい場合は、前のセクションのコードスニペットを変更して、代わりに AnnotationConfigWebApplicationContext をインスタンス化することができます。

しかし、同じ結果を得るためのより特殊なアプローチを見てみましょう。

  • 先ほど見た WebApplicationInitializer クラスは汎用インターフェースです** Springは AbstractContextLoaderInitializer という抽象クラスを含む、より具体的な実装をいくつか提供しています。

その名の通り、その役割は ContextLoaderListener を作成し、それをサーブレットコンテナに登録することです。

ルートコンテキストを構築する方法を説明するだけです。

public class AnnotationsBasedApplicationInitializer
  extends AbstractContextLoaderInitializer {

    @Override
    protected WebApplicationContext createRootApplicationContext() {
        AnnotationConfigWebApplicationContext rootContext
          = new AnnotationConfigWebApplicationContext();
        rootContext.register(RootApplicationConfig.class);
        return rootContext;
    }
}

ここで、 ContextLoaderListener を登録する必要がなくなったことがわかります。これにより、ちょっとした定型コードを省くことができます。

より一般的な setConfigLocations の代わりに AnnotationConfigWebApplicationContext に固有の register メソッドを使用することにも注意してください。それを呼び出すことで、個々の @ Configuration アノテーション付きクラスをコンテキストに登録できるため、パッケージのスキャンを回避できます。

3ディスパッチャサーブレットコンテキスト

それでは、別の種類のアプリケーションコンテキストに焦点を絞りましょう。今回は、Springの一般的なWebアプリケーションサポートの一部ではなく、Spring MVCに固有の機能について説明します。

  • Spring MVCアプリケーションには、少なくとも1つのDispatcher Servletが設定されています** (ただし、1つ以上の可能性があります。その場合は後で説明します)。これは、着信要求を受け取り、それらを適切なコントローラメソッドにディスパッチしてビューを返すサーブレットです。

  • DispatcherServlet には関連付けられたアプリケーションコンテキストがあります** このようなコンテキストで定義されたBeanはサーブレットを設定し、コントローラやビューリゾルバなどのMVCオブジェクトを定義します。

最初にサーブレットのコンテキストを設定する方法を見てみましょう。詳細については後で説明します。

3.1. web.xml とXMLアプリケーションコンテキストの使用

DispatcherServlet は通常 web.xml で名前とマッピングを使って宣言されます。

<servlet>
    <servlet-name>normal-webapp</servlet-name>
    <servlet-class>
        org.springframework.web.servlet.DispatcherServlet
    </servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>normal-webapp</servlet-name>
    <url-pattern>/api/** </url-pattern>
</servlet-mapping>

特に指定されていない場合は、ロードするXMLファイルを決定するためにサーブレットの名前が使用されます。この例では、 WEB-INF/normal-webapp-servlet.xml というファイルを使用します。

ContextLoaderListener と同様に、XMLファイルへのパスを1つ以上指定することもできます。

<servlet>
    ...
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/normal/** .xml</param-value>
    </init-param>
</servlet>

3.2. web.xml とJavaアプリケーションコンテキストの使用

別の種類のコンテキストを使用したい場合は、 ContextLoaderListener を使用して手順を進めます。つまり、適切な contextConfigLocation と共に contextClass パラメータを指定します。

<servlet>
    <servlet-name>normal-webapp-annotations</servlet-name>
    <servlet-class>
        org.springframework.web.servlet.DispatcherServlet
    </servlet-class>
    <init-param>
        <param-name>contextClass</param-name>
        <param-value>
            org.springframework.web.context.support.AnnotationConfigWebApplicationContext
        </param-value>
    </init-param>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>com.baeldung.contexts.config.NormalWebAppConfig</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

3.3. サーブレット3.xとXMLアプリケーションコンテキストの使用

  • 繰り返しますが、 DispatcherServlet をプログラムで宣言するための2つの異なる方法を見て、一方をXMLコンテキストに、もう一方をJavaコンテキストに適用します。

それでは、一般的な WebApplicationInitializer とXMLアプリケーションコンテキストから始めましょう。

前に見たように、 onStartup メソッドを実装する必要があります。

ただし、今回はディスパッチャサーブレットも作成して登録します。

XmlWebApplicationContext normalWebAppContext = new XmlWebApplicationContext();
normalWebAppContext.setConfigLocation("/WEB-INF/normal-webapp-servlet.xml");
ServletRegistration.Dynamic normal
  = servletContext.addServlet("normal-webapp",
    new DispatcherServlet(normalWebAppContext));
normal.setLoadOnStartup(1);
normal.addMapping("/api/** ");

上記のコードとそれに相当する web.xml 設定要素との間に平行関係を簡単に描くことができます。

3.4. サーブレット3.xとJavaアプリケーションコンテキストの使用

今回は、 WebApplicationInitializer の特殊な実装を使用してアノテーションベースのコンテキストを設定します。

AbstractDispatcherServletInitializer

これは抽象クラスであり、前述のようにルートWebアプリケーションコンテキストを作成するほかに、1つのディスパッチャサーブレットを最小限の定型句で登録することを可能にします。

@Override
protected WebApplicationContext createServletApplicationContext() {

    AnnotationConfigWebApplicationContext secureWebAppContext
      = new AnnotationConfigWebApplicationContext();
    secureWebAppContext.register(SecureWebAppConfig.class);
    return secureWebAppContext;
}

@Override
protected String[]getServletMappings() {
    return new String[]{ "/s/api/** " };
}

ここで、サーブレットに関連付けられているコンテキストを作成するためのメソッドを見ることができます。これは、ルートコンテキストについて以前に見たのとまったく同じです。また、 web.xml のように、サーブレットのマッピングを指定するためのメソッドもあります。

** 4親と子のコンテキスト

**

これまでのところ、ルートWebアプリケーションコンテキストとディスパッチャサーブレットコンテキストの2種類のコンテキストが見られました。それでは、私たちは質問をするかもしれません: それらの文脈は関連していますか?

はい、彼らはそうです。実際には、 ルートコンテキストはすべてのディスパッチャサーブレットコンテキストの親です したがって、ルートWebアプリケーションコンテキストで定義されたBeanは各ディスパッチャサーブレットコンテキストに表示されますが、その逆はありません。

そのため、通常はルートコンテキストを使用してサービスBeanを定義し、ディスパッチャコンテキストには特にMVCに関連するBeanを含めます。

プログラムでディスパッチャサーブレットコンテキストを作成する方法も見ました。手動で親を設定した場合、Springはその決定をオーバーライドせず、このセクションは適用されません。

より単純なMVCアプリケーションでは、ただ1つのディスパッチャサーブレットに単一のコンテキストを関連付けるだけで十分です。過度に複雑なソリューションは必要ありません。

それでも、親子関係は、複数のディスパッチャサーブレットが設定されている場合に役立ちます。しかし、いつ私たちは複数のものを持つことを煩わすべきですか?

一般的に、 MVC設定の複数セットが必要な場合は 複数のディスパッチャサーブレットを宣言します たとえば、従来のMVCアプリケーションやWebサイトの安全で保護されていないセクションと一緒にREST APIを使用することができます。

リンク:/uploads/contexts-100x83.png%20100w[]

注: AbstractDispatcherServletInitializer を拡張すると(セクション3.4を参照)、ルートWebアプリケーションコンテキストと単一のディスパッチャサーブレットの両方が登録されます。

したがって、複数のサーブレットが必要な場合は、複数の AbstractDispatcherServletInitializer 実装が必要です。ただし、ルートコンテキストは1つしか定義できないか、アプリケーションが起動しません。

幸い、 createRootApplicationContext メソッドは null を返すことがあります。したがって、1つの AbstractContextLoaderInitializer と、ルートコンテキストを作成しない多数の AbstractDispatcherServletInitializer の実装を持つことができます。そのようなシナリオでは、初期化子を @ Order で明示的に順序付けることをお勧めします。

また、 AbstractDispatcherServletInitializer はサーブレットを特定の名前( dispatcher )で登録します。もちろん、同じ名前のサーブレットを複数持つことはできません。そのため、 getServletName をオーバーライドする必要があります。

@Override
protected String getServletName() {
    return "another-dispatcher";
}

5親と子のコンテキストの例

アプリケーションに2つの領域があるとします。たとえば、世界でアクセス可能なパブリック領域と、異なるMVC構成を持つ安全な領域です。ここでは、異なるメッセージを出力する2つのコントローラを定義します。

また、コントローラーの中にはかなりのリソースを保持するサービスを必要としているとします。偏在するのは永続性です。それから、リソースの使用量が2倍になるのを避けるために、そのサービスを1回だけインスタンス化することをお勧めします。

例を見てみましょう。

5.1. 共有サービス

私たちのこんにちは世界の例では、私たちは持続性の代わりにもっと簡単なよりグリーンなサービスに落ち着きました:

package com.baeldung.contexts.services;

@Service
public class GreeterService {
    @Resource
    private Greeting greeting;

    public String greet() {
        return greeting.getMessage();
    }
}

コンポーネントスキャンを使用して、ルートWebアプリケーションのコンテキストでサービスを宣言します。

@Configuration
@ComponentScan(basePackages = { "com.baeldung.contexts.services" })
public class RootApplicationConfig {
   //...
}

代わりにXMLを好むかもしれません。

<context:component-scan base-package="com.baeldung.contexts.services"/>

5.2. コントローラー

サービスを使用してグリーティングを出力する2つの単純なコントローラを定義しましょう。

package com.baeldung.contexts.normal;

@Controller
public class HelloWorldController {

    @Autowired
    private GreeterService greeterService;

    @RequestMapping(path = "/welcome")
    public ModelAndView helloWorld() {
        String message = "<h3>Normal " + greeterService.greet() + "</h3>";
        return new ModelAndView("welcome", "message", message);
    }
}
//"Secure" Controller
package com.baeldung.contexts.secure;

String message = "<h3>Secure " + greeterService.greet() + "</h3>";

ご覧のとおり、コントローラーは2つの異なるパッケージに入っており、異なるメッセージを表示します。1つは「通常」、もう1つは「安全」です。

** 5.3. ディスパッチャサーブレットコンテキスト

**

前述したように、各コントローラーに1つずつ、合計2つの異なるディスパッチャーサーブレットコンテキストを用意します。それでは、それらをJavaで定義しましょう。

----//Normal context
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = { "com.baeldung.contexts.normal" })
public class NormalWebAppConfig implements WebMvcConfigurer {
   //...
}
//"Secure" context
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = { "com.baeldung.contexts.secure" })
public class SecureWebAppConfig implements WebMvcConfigurer {
   //...
}
----

あるいは、私たちが好むのであれば、XMLで:

<!-- normal-webapp-servlet.xml -->
<context:component-scan base-package="com.baeldung.contexts.normal"/>

<!-- secure-webapp-servlet.xml -->
<context:component-scan base-package="com.baeldung.contexts.secure"/>

5.4. すべてを一緒に入れて

すべての要素が揃ったので、Springにそれらを接続するように指示するだけです。ルートコンテキストをロードし、2つのディスパッチャサーブレットを定義する必要があることを思い出してください。これを行うには複数の方法がありますが、ここではJavaとXMLの2つのシナリオに焦点を当てます。 Javaから始めましょう。

ルートコンテキストをロードするために AbstractContextLoaderInitializer を定義します。

@Override
protected WebApplicationContext createRootApplicationContext() {
    AnnotationConfigWebApplicationContext rootContext
      = new AnnotationConfigWebApplicationContext();
    rootContext.register(RootApplicationConfig.class);
    return rootContext;
}

次に、2つのサーブレットを作成する必要があるので、 AbstractDispatcherServletInitializer の2つのサブクラスを定義します。まず、「普通の」ものです。

@Override
protected WebApplicationContext createServletApplicationContext() {
    AnnotationConfigWebApplicationContext normalWebAppContext
      = new AnnotationConfigWebApplicationContext();
    normalWebAppContext.register(NormalWebAppConfig.class);
    return normalWebAppContext;
}

@Override
protected String[]getServletMappings() {
    return new String[]{ "/api/** " };
}

@Override
protected String getServletName() {
    return "normal-dispatcher";
}

それから、異なるコンテキストをロードし、異なるパスにマッピングされる「安全な」ものです。

@Override
protected WebApplicationContext createServletApplicationContext() {
    AnnotationConfigWebApplicationContext secureWebAppContext
      = new AnnotationConfigWebApplicationContext();
    secureWebAppContext.register(SecureWebAppConfig.class);
    return secureWebAppContext;
}

@Override
protected String[]getServletMappings() {
    return new String[]{ "/s/api/** " };
}

@Override
protected String getServletName() {
    return "secure-dispatcher";
}

これで終わりです。前のセクションで触れたことをそのまま適用しました。

  • これまでに説明した要素を組み合わせるだけで、 web.xml ** でも同じことができます。

ルートアプリケーションコンテキストを定義します。

<listener>
    <listener-class>
        org.springframework.web.context.ContextLoaderListener
    </listener-class>
</listener>

「通常の」ディスパッチャコンテキスト

<servlet>
    <servlet-name>normal-webapp</servlet-name>
    <servlet-class>
        org.springframework.web.servlet.DispatcherServlet
    </servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>normal-webapp</servlet-name>
    <url-pattern>/api/** </url-pattern>
</servlet-mapping>

そして最後に、「安全な」コンテキストです。

<servlet>
    <servlet-name>secure-webapp</servlet-name>
    <servlet-class>
        org.springframework.web.servlet.DispatcherServlet
    </servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>secure-webapp</servlet-name>
    <url-pattern>/s/api/** </url-pattern>
</servlet-mapping>

** 6. 複数のコンテキストを組み合わせる

**

親子以外にも、複数の設定場所を組み合わせる、大きなコンテキストを分割する、さまざまな問題を区別する方法などがあります。** 1つの例は既に見ました。複数のパスまたはパッケージで contextConfigLocation を指定すると、すべてのBean定義を、あたかも単一のXMLファイルまたはJavaクラスで順番に書かれているかのように組み合わせます。

ただし、他の方法でも同様の効果が得られ、さまざまな方法を併用することもできます。私たちの選択肢を検討しましょう。

1つの可能性は、コンポーネントスキャンです。これは、 別の記事に記載 を説明したものです。

6.1. 別のコンテキストへのコンテキストのインポート

あるいは、コンテキスト定義から別のコンテキスト定義をインポートすることもできます。

シナリオに応じて、さまざまな種類の輸入品があります。

Javaで @ Configuration クラスをインポートする:

@Configuration
@Import(SomeOtherConfiguration.class)
public class Config { ... }

JavaでのXMLコンテキスト定義など、他のタイプのリソースのロード

@Configuration
@ImportResource("classpath:basicConfigForPropertiesTwo.xml")
public class Config { ... }

最後に、XMLファイルを別のファイルに含めます。

<import resource="greeting.xml"/>

このように、私たちには素晴らしいアプリケーションを作成するために協力するサービス、コンポーネント、コントローラなどをまとめる多くの方法があります。

そして素晴らしいことは、IDEがそれらすべてを理解するということです。

** 7. Spring Boot Webアプリケーション+

**

  • Spring Bootはアプリケーションのコンポーネントを自動的に設定します。** そのため、一般的に、それらをどのように編成するかについて考える必要性は少なくなります。

それでも、内部的には、Bootはこれまで見てきたものも含めてSpring機能を使用しています。いくつか注目すべき違いを見てみましょう。

埋め込みコンテナ内で実行されているSpring Boot Webアプリケーション設計上、任意の[WebApplicationInitializer__]。

必要に応じて、選択したデプロイメント戦略に応じて、代わりに __SpringBootServletInitializerまたは ServletContextInitializerに同じロジックを記述できます。

ただし、この記事のようにサーブレット、フィルタ、およびリスナを追加する場合は、追加する必要はありません。実際には、Spring Bootはすべてのサーブレット関連Beanを自動的にコンテナに登録します。

@Bean
public Servlet myServlet() { ... }

そのように定義されたオブジェクトは規則に従ってマッピングされます。フィルタは自動的に/** 、つまりすべての要求にマッピングされます。単一のサーブレットを登録すると/にマッピングされ、それ以外の場合は各サーブレットはそのBean名にマッピングされます。

上記の規則がうまくいかない場合は、代わりに FilterRegistrationBean __ServletRegistrationBean、 または ServletListenerRegistrationBean__を定義できます。これらのクラスによって、登録の細かい部分を制御できます。

8結論

この記事では、Spring Webアプリケーションを構成および編成するために使用できるさまざまなオプションについて詳しく説明しました。

私たちはいくつかの機能、特にhttps://spring.io/blog/2007/06/11/using-a-shared-parent-application-context-in-a-multi-war-spring-application/を省きましたこれを書いている時点では、まだ[ Spring 5から抜けています ][エンタープライズアプリケーションにおける共有コンテキストのサポート]。

これらすべての例とコードスニペットの実装はhttps://github.com/eugenp/tutorials/tree/master/spring-all[GitHubプロジェクト]にあります - これはMavenプロジェクトです。そのままインポートして実行します。

前の投稿:Springコンポーネントスキャニング
次の投稿:ジャクソン - どのフィールドをシリアライズ/デシリアライズするかを決める