リファレンスドキュメントのこのパートは、Spring Framework に不可欠なすべてのテクノロジーを網羅しています。

これらの中で最も重要なのは、Spring Framework の Inversion of Control(IoC:制御の反転)コンテナーです。Spring Framework の IoC コンテナーを徹底的に扱った後、Spring のアスペクト指向プログラミング(AOP)テクノロジーを包括的な説明が続きます。Spring Framework には独自の AOP フレームワークがあり、概念的に理解しやすく、Java エンタープライズプログラミングにおける AOP 要件の 80% のスイートスポットにうまく対処します。

Spring の AspectJ との統合(現在、機能面で最も豊富で、Java エンタープライズ空間で最も成熟した AOP 実装)も提供されています。

1. IoC コンテナー

この章では、Spring の制御の反転(IoC)コンテナーについて説明します。

1.1. Spring IoC コンテナーと Bean の概要

この章では、制御の反転(IoC)原則の Spring Framework 実装について説明します。IoC は、依存性注入(DI)とも呼ばれます。これは、コンストラクター引数、ファクトリメソッドへの引数、またはファクトリメソッドが構築または返された後にオブジェクトインスタンスに設定されるプロパティを通じてのみ、オブジェクトが依存関係(つまり、操作する他のオブジェクト)を定義するプロセスです。コンテナーは、Bean を作成するときにそれらの依存関係を注入します。このプロセスは、基本的に、クラスの直接構築または Service Locator パターンなどのメカニズムを使用して、Bean 自身がその依存関係のインスタンス化や場所を制御することの逆(そのため、Inversion of Control という名前)です。

org.springframework.beans および org.springframework.context パッケージは、Spring Framework の IoC コンテナーの基盤です。BeanFactory (Javadoc) インターフェースは、あらゆるタイプのオブジェクトを管理できる高度な構成メカニズムを提供します。ApplicationContext (Javadoc) は BeanFactory のサブインターフェースです。以下を追加します。

  • Spring の AOP 機能との簡単な統合

  • メッセージリソースの処理 (国際化で使用するため)

  • イベント公開

  • Web アプリケーションで使用する WebApplicationContext などのアプリケーション層固有のコンテキスト。

つまり、BeanFactory は構成フレームワークと基本機能を提供し、ApplicationContext はエンタープライズ固有の機能をさらに追加します。ApplicationContext は BeanFactory の完全なスーパーセットであり、この章で Spring の IoC コンテナーの説明でのみ使用されます。ApplicationContext, の代わりに BeanFactory を使用する方法の詳細については、BeanFactory を参照してください。

Spring では、アプリケーションのバックボーンを形成し、Spring IoC コンテナーによって管理されるオブジェクトを Bean と呼びます。Bean は、Spring IoC コンテナーによってインスタンス化、アセンブル、管理されるオブジェクトです。それ以外の場合、Bean はアプリケーションの多くのオブジェクトの 1 つにすぎません。Bean とそれらの間の依存関係は、コンテナーが使用する構成メタデータに反映されます。

1.2. コンテナーの概要

org.springframework.context.ApplicationContext インターフェースは Spring IoC コンテナーを表し、Bean のインスタンス化、構成、組み立てを担当します。コンテナーは、構成メタデータを読み取ることにより、どのオブジェクトをインスタンス化、構成、アセンブルするかに関する指示を取得します。構成メタデータは、XML、Java アノテーション、または Java コードで表されます。アプリケーションを構成するオブジェクトと、それらのオブジェクト間の豊富な相互依存関係を表現できます。

ApplicationContext インターフェースのいくつかの実装が Spring で提供されます。スタンドアロンアプリケーションでは、ClassPathXmlApplicationContext (Javadoc) または FileSystemXmlApplicationContext (Javadoc) のインスタンスを作成するのが一般的です。XML は構成メタデータを定義するための従来の形式でしたが、これらの追加のメタデータ形式のサポートを宣言的に有効にするために少量の XML 構成を提供することにより、メタデータ形式として Java アノテーションまたはコードを使用するようにコンテナーに指示できます。

ほとんどのアプリケーションシナリオでは、Spring IoC コンテナーの 1 つ以上のインスタンスをインスタンス化するために明示的なユーザーコードは必要ありません。例:Web アプリケーションのシナリオでは、通常、アプリケーションの web.xml ファイル内のボイラープレート Web 記述子 XML の単純な 8 行(またはそれ以上)で十分です(Web アプリケーション用の便利な ApplicationContext インスタンス化を参照)。Eclipse Pleiades All in One (STS, Lombok 付属) または Eclipse 用 Spring Tools (英語) (Eclipse を使用した開発環境)を使用している場合、数回のマウスクリックまたはキーストロークでこの定型的な構成を簡単に作成できます。

次の図は、Spring の機能の概要を示しています。アプリケーションクラスは構成メタデータと組み合わされ、ApplicationContext が作成および初期化された後、完全に構成された実行可能なシステムまたはアプリケーションができます。

container magic
図 1: Spring IoC コンテナー

1.2.1. 構成メタデータ

前の図に示すように、Spring IoC コンテナーは、構成メタデータの形式を使用します。この構成メタデータは、アプリケーション開発者として、Spring コンテナーにアプリケーション内のオブジェクトのインスタンス化、構成、アセンブルを指示する方法を表します。

構成メタデータは従来、シンプルで直感的な XML 形式で提供されます。これは、この章のほとんどで、Spring IoC コンテナーの主要な概念と機能を伝えるために使用されます。

XML ベースのメタデータは、構成メタデータの唯一の許可された形式ではありません。Spring IoC コンテナー自体は、この構成メタデータが実際に書き込まれる形式から完全に切り離されています。最近では、多くの開発者が Spring アプリケーションに Java ベースの構成を選択しています。

Spring コンテナーで他の形式のメタデータを使用する方法については、以下を参照してください。

Spring 構成は、コンテナーが管理する必要がある少なくとも 1 つ、通常は複数の Bean 定義で構成されます。XML ベースの構成メタデータは、これらの Bean をトップレベルの <beans/> 要素内の <bean/> 要素として構成します。Java 構成では、通常、@Configuration クラス内で @Bean アノテーション付きメソッドを使用します。

これらの Bean 定義は、アプリケーションを構成する実際のオブジェクトに対応しています。通常、サービスレイヤーオブジェクト、データアクセスオブジェクト(DAO)、Struts Action インスタンスなどのプレゼンテーションオブジェクト、Hibernate SessionFactories などのインフラストラクチャオブジェクト、JMS Queues などを定義します。通常、ドメインオブジェクトを作成およびロードするのは通常 DAO とビジネスロジックの責任であるため、コンテナー内で詳細なドメインオブジェクトを構成することはありません。ただし、Spring と AspectJ の統合を使用して、IoC コンテナーの制御外で作成されたオブジェクトを構成できます。AspectJ を使用して Spring を使用してドメインオブジェクトを依存性注入するを参照してください。

次の例は、XML ベースの構成メタデータの基本構造を示しています。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="..." class="..."> (1) (2)
        <!-- collaborators and configuration for this bean go here -->
    </bean>

    <bean id="..." class="...">
        <!-- collaborators and configuration for this bean go here -->
    </bean>

    <!-- more bean definitions go here -->

</beans>
1id 属性は、個々の Bean 定義を識別する文字列です。
2class 属性は、Bean のタイプを定義し、完全修飾クラス名を使用します。

id 属性の値は、共同作業オブジェクトを指します。この例では、共同作業オブジェクトを参照するための XML は示されていません。詳細については、依存関係を参照してください。

1.2.2. コンテナーのインスタンス化

ApplicationContext コンストラクターに提供されるロケーションパスは、コンテナーがローカルファイルシステム、Java CLASSPATH などのさまざまな外部リソースから構成メタデータをロードできるようにするリソース文字列です。

Java
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
Kotlin
val context = ClassPathXmlApplicationContext("services.xml", "daos.xml")

Spring の IoC コンテナーについて学習した後、Spring の Resource 抽象化(リソースで説明)について詳しく知りたい場合があります。これは、URI 構文で定義された場所から InputStream を読み取るための便利なメカニズムを提供します。特に、アプリケーションコンテキストとリソースパスに従って、Resource パスはアプリケーションコンテキストの構築に使用されます。

次の例は、サービスレイヤーオブジェクト (services.xml) 構成ファイルを示しています。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- services -->

    <bean id="petStore" class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl">
        <property name="accountDao" ref="accountDao"/>
        <property name="itemDao" ref="itemDao"/>
        <!-- additional collaborators and configuration for this bean go here -->
    </bean>

    <!-- more bean definitions for services go here -->

</beans>

次の例は、データアクセスオブジェクト daos.xml ファイルを示しています。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="accountDao"
        class="org.springframework.samples.jpetstore.dao.jpa.JpaAccountDao">
        <!-- additional collaborators and configuration for this bean go here -->
    </bean>

    <bean id="itemDao" class="org.springframework.samples.jpetstore.dao.jpa.JpaItemDao">
        <!-- additional collaborators and configuration for this bean go here -->
    </bean>

    <!-- more bean definitions for data access objects go here -->

</beans>

前の例では、サービスレイヤーは PetStoreServiceImpl クラスと、タイプ JpaAccountDao および JpaItemDao の 2 つのデータアクセスオブジェクト(JPA オブジェクトリレーショナルマッピング標準に基づく)で構成されています。property name エレメントは JavaBean プロパティの名前を参照し、ref エレメントは別の Bean 定義の名前を参照します。id 要素と ref 要素の間のこのリンケージは、コラボレーションするオブジェクト間の依存関係を表します。オブジェクトの依存関係の構成の詳細については、依存関係を参照してください。

XML ベースの構成メタデータの作成

Bean 定義が複数の XML ファイルにまたがっていると便利です。多くの場合、個々の XML 構成ファイルは、アーキテクチャ内の論理層またはモジュールを表します。

アプリケーションコンテキストコンストラクターを使用して、これらすべての XML フラグメントから Bean 定義をロードできます。前のセクションで示したように、このコンストラクターは複数の Resource ロケーションを取ります。または、<import/> エレメントの 1 つ以上の出現箇所を使用して、Bean 定義を別のファイルからロードします。次の例は、その方法を示しています。

<beans>
    <import resource="services.xml"/>
    <import resource="resources/messageSource.xml"/>
    <import resource="/resources/themeSource.xml"/>

    <bean id="bean1" class="..."/>
    <bean id="bean2" class="..."/>
</beans>

前の例では、外部 Bean 定義は 3 つのファイル services.xmlmessageSource.xml および themeSource.xml からロードされます。すべての場所のパスは、インポートを実行する定義ファイルに関連するため、services.xml はインポートを実行するファイルと同じディレクトリまたはクラスパスの場所にある必要があり、messageSource.xml および themeSource.xml はインポートファイルの場所の resources の場所にある必要があります。ご覧のとおり、先頭のスラッシュは無視されます。ただし、これらのパスは相対パスであるため、スラッシュをまったく使用しない方が適切です。Spring スキーマに従って、インポートされるファイルの内容(最上位の <beans/> 要素を含む)は、有効な XML Bean 定義でなければなりません。

相対パス「../」を使用して親ディレクトリのファイルを参照することは可能ですが、推奨されません。これを行うと、現在のアプリケーションの外部にあるファイルに依存関係が作成されます。特に、この参照は classpath: URL(たとえば classpath:../services.xml)には推奨されません。この場合、ランタイム解決プロセスは「最も近い」クラスパスルートを選択し、その親ディレクトリを調べます。クラスパス構成の変更により、別の誤ったディレクトリが選択される場合があります。

相対パスの代わりに、たとえば file:C:/config/services.xml または classpath:/config/services.xml のような完全修飾リソースの場所を常に使用できます。ただし、アプリケーションの構成を特定の絶対ロケーションに結合していることに注意してください。一般に、このような絶対的な場所に対しては、たとえば、実行時に JVM システムプロパティに対して解決される "${ … }" プレースホルダーを介した間接性を維持することが望ましいです。

ネームスペース自体がインポートディレクティブ機能を提供します。context および util 名前空間など、Spring が提供する XML 名前空間の選択では、プレーンな Bean 定義を超えるさらなる構成機能を利用できます。

Groovy Bean 定義 DSL

外部化された構成メタデータのさらなる例として、Grails フレームワークで知られているように、Bean 定義は Spring の Groovy Bean Definition DSL でも表現できます。通常、このような構成は、次の例に示す構造を持つ「.groovy」ファイルに存在します。

beans {
    dataSource(BasicDataSource) {
        driverClassName = "org.hsqldb.jdbcDriver"
        url = "jdbc:hsqldb:mem:grailsDB"
        username = "sa"
        password = ""
        settings = [mynew:"setting"]
    }
    sessionFactory(SessionFactory) {
        dataSource = dataSource
    }
    myService(MyService) {
        nestedBean = { AnotherBean bean ->
            dataSource = dataSource
        }
    }
}

この構成スタイルは、XML Bean 定義とほぼ同等であり、Spring の XML 構成名前空間もサポートしています。また、importBeans ディレクティブを介して XML Bean 定義ファイルをインポートすることもできます。

1.2.3. コンテナーの使用

ApplicationContext は、さまざまな Bean とその依存関係のレジストリを維持することができる高度なファクトリのインターフェースです。メソッド T getBean(String name, Class<T> requiredType) を使用すると、Bean のインスタンスを取得できます。

ApplicationContext では、次の例に示すように、Bean 定義を読み取ってそれらにアクセスできます。

Java
// create and configure beans
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");

// retrieve configured instance
PetStoreService service = context.getBean("petStore", PetStoreService.class);

// use configured instance
List<String> userList = service.getUsernameList();
Kotlin
import org.springframework.beans.factory.getBean

// create and configure beans
val context = ClassPathXmlApplicationContext("services.xml", "daos.xml")

// retrieve configured instance
val service = context.getBean<PetStoreService>("petStore")

// use configured instance
var userList = service.getUsernameList()

Groovy 構成では、ブートストラップは非常に似ています。Groovy 対応の異なるコンテキスト実装クラスがあります(ただし、XML Bean 定義も理解します)。次の例は、Groovy 構成を示しています。

Java
ApplicationContext context = new GenericGroovyApplicationContext("services.groovy", "daos.groovy");
Kotlin
val context = GenericGroovyApplicationContext("services.groovy", "daos.groovy")

最も柔軟なバリアントは、次の例に示すように、リーダーデリゲートと組み合わせた GenericApplicationContext です。たとえば、XML ファイルの XmlBeanDefinitionReader です。

Java
GenericApplicationContext context = new GenericApplicationContext();
new XmlBeanDefinitionReader(context).loadBeanDefinitions("services.xml", "daos.xml");
context.refresh();
Kotlin
val context = GenericApplicationContext()
XmlBeanDefinitionReader(context).loadBeanDefinitions("services.xml", "daos.xml")
context.refresh()

次の例に示すように、Groovy ファイルに GroovyBeanDefinitionReader を使用することもできます。

Java
GenericApplicationContext context = new GenericApplicationContext();
new GroovyBeanDefinitionReader(context).loadBeanDefinitions("services.groovy", "daos.groovy");
context.refresh();
Kotlin
val context = GenericApplicationContext()
GroovyBeanDefinitionReader(context).loadBeanDefinitions("services.groovy", "daos.groovy")
context.refresh()

同じリーダーデリゲートを同じ ApplicationContext で組み合わせて、さまざまな構成ソースから Bean 定義を読み取ることができます。

その後、getBean を使用して、Bean のインスタンスを取得できます。ApplicationContext インターフェースには、Bean を取得するためのその他のメソッドがいくつかありますが、理想的には、アプリケーションコードで Bean を使用しないでください。実際、アプリケーションコードには getBean() メソッドをまったく呼び出さないようにし、Spring API にまったく依存しないようにします。例:Spring の Web フレームワークとの統合は、コントローラーや JSF 管理の Bean などのさまざまな Web フレームワークコンポーネントへの依存性注入を提供し、メタデータ(オートワイヤーアノテーションなど)を通じて特定の Bean への依存性を宣言できます。

1.3. Bean の概要

Spring IoC コンテナーは、1 つ以上の Bean を管理します。これらの Bean は、コンテナーに提供する構成メタデータを使用して作成されます(たとえば、XML <bean/> 定義の形式で)。

コンテナー自体の中では、これらの Bean 定義は BeanDefinition オブジェクトとして表され、(他の情報とともに)次のメタデータを含んでいます:

  • パッケージ修飾クラス名 : 通常、定義されている Bean の実際の実装クラス。

  • Bean 動作構成要素。Bean がコンテナー内でどのように動作するかを指定します(スコープ、ライフサイクルコールバックなど)。

  • Bean が機能するために必要な他の Bean への参照。これらの参照は、共同エディターまたは依存関係とも呼ばれます。

  • 新しく作成されたオブジェクトに設定する他の構成設定。たとえば、プールのサイズ制限や、接続プールを管理する Bean で使用する接続数など。

このメタデータは、各 Bean 定義を構成する一連のプロパティに変換されます。次の表で、これらのプロパティについて説明します。

表 1: Bean 定義
プロパティ 説明…

クラス

Bean のインスタンス化

名前

Bean の命名

スコープ

Bean スコープ

コンストラクター引数

依存性注入

プロパティ

依存性注入

オートワイヤーモード

共同エディターのオートワイヤー

遅延初期化モード

遅延初期化された Bean

初期化メソッド

初期化コールバック

破棄メソッド

破棄コールバック

特定の Bean を作成する方法に関する情報を含む Bean 定義に加えて、ApplicationContext 実装では、コンテナーの外部で(ユーザーによって)作成される既存のオブジェクトの登録も許可します。これは、BeanFactory DefaultListableBeanFactory 実装を返す getBeanFactory() メソッドを介して ApplicationContext の BeanFactory にアクセスすることにより行われます。DefaultListableBeanFactory は、registerSingleton(..) および registerBeanDefinition(..) メソッドを介してこの登録をサポートします。ただし、一般的なアプリケーションは、通常の Bean 定義メタデータを介して定義された Bean のみで機能します。

Bean のメタデータと手動で提供されたシングルトンインスタンスは、コンテナーがオートワイヤーやその他のイントロスペクションの段階で適切に判断できるように、できるだけ早く登録する必要があります。既存のメタデータおよび既存のシングルトンインスタンスの上書きはある程度サポートされていますが、実行時の新規 Bean の登録 (ファクトリへのライブアクセスと同時に) は公式にはサポートされておらず、同時アクセス例外、Bean コンテナーでの矛盾した状態、またはその両方につながる可能性があります。

1.3.1. Bean の命名

すべての Bean には 1 つ以上の識別子があります。これらの識別子は、Bean をホストするコンテナー内で一意である必要があります。Bean には通常 1 つの識別子しかありません。ただし、複数必要な場合は、余分なものをエイリアスと見なすことができます。

XML ベースの構成メタデータでは、id 属性、name 属性、またはその両方を使用して、Bean 識別子を指定します。id 属性を使用すると、ID を 1 つだけ指定できます。通常、これらの名前は英数字(「myBean」、「someService」など)ですが、特殊文字を含めることもできます。Bean に他のエイリアスを導入する場合は、name 属性でコンマ(,)、セミコロン(;)、または空白で区切って指定することもできます。歴史的な注記として、Spring 3.1, より前のバージョンでは、id 属性は xsd:ID タイプとして定義され、可能な文字が制限されていました。3.1 以降、xsd:string タイプとして定義されています。Bean id の一意性は、XML パーサーではなく、コンテナーによって強制されることに注意してください。

Bean に name または id を提供する必要はありません。name または id を明示的に指定しない場合、コンテナーはその Bean の一意の名前を生成します。ただし、ref 要素または Service Locator スタイルのルックアップを使用して、その Bean を名前で参照する場合は、名前を指定する必要があります。名前を提供しない動機は、内部 Bean の使用とコラボレーターのオートワイヤーに関連しています。

Bean の命名規則

規則は、Bean の命名時にインスタンスフィールド名に標準の Java 規則を使用することです。つまり、Bean 名は小文字で始まり、そこからキャメルケースになります。そのような名前の例には、accountManageraccountServiceuserDaologinController などがあります。

Bean に一貫して名前を付けると、構成が読みやすくなり、理解しやすくなります。また、Spring AOP を使用すると、名前で関連付けられた一連の Bean にアドバイスを適用するときに非常に役立ちます。

クラスパスでコンポーネントをスキャンすると、Spring は前述のルールに従って、名前のないコンポーネントの Bean 名を生成します。基本的には、単純なクラス名を取得し、その最初の文字を小文字に変換します。ただし、複数の文字があり、最初の文字と 2 番目の文字の両方が大文字である(異常な)特殊なケースでは、元の大文字と小文字が保持されます。これらは、java.beans.Introspector.decapitalize (Spring がここで使用する)で定義されているものと同じルールです。
Bean 定義外の Bean のエイリアス

Bean 定義自体では、id 属性で指定された最大 1 つの名前と name 属性の他の任意の数の組み合わせを使用して、Bean に複数の名前を指定できます。これらの名前は、同じ Bean と同等のエイリアスにすることができ、アプリケーション内の各コンポーネントがそのコンポーネント自体に固有の Bean 名を使用して共通の依存関係を参照できるようにするなど、いくつかの状況で役立ちます。

ただし、Bean が実際に定義されているすべてのエイリアスを指定することは必ずしも適切ではありません。他の場所で定義されている Bean のエイリアスを導入することが望ましい場合があります。これは、構成が各サブシステム間で分割され、各サブシステムが独自のオブジェクト定義のセットを持つ大規模なシステムの場合に一般的です。XML ベースの構成メタデータでは、<alias/> 要素を使用してこれを実現できます。次の例は、その方法を示しています。

<alias name="fromName" alias="toName"/>

この場合、fromName という名前の Bean(同じコンテナー内)は、この別名定義の使用後、toName と呼ばれることもあります。

例:サブシステム A の構成メタデータは、subsystemA-dataSource という名前の DataSource を参照する場合があります。サブシステム B の構成メタデータは、subsystemB-dataSource という名前の DataSource を参照する場合があります。これら両方のサブシステムを使用するメインアプリケーションを作成する場合、メインアプリケーションは myApp-dataSource という名前で DataSource を参照します。3 つの名前すべてが同じオブジェクトを参照するようにするには、次のエイリアス定義を構成メタデータに追加できます。

<alias name="myApp-dataSource" alias="subsystemA-dataSource"/>
<alias name="myApp-dataSource" alias="subsystemB-dataSource"/>

これで、各コンポーネントとメインアプリケーションは一意の名前で dataSource を参照でき、他の定義と衝突しないことが保証されます(効果的に名前空間を作成します)が、同じ Bean を参照します。

Java 構成

Java 構成を使用する場合、@Bean アノテーションを使用してエイリアスを提供できます。詳細については、@Bean アノテーションの使用を参照してください。

1.3.2. Bean のインスタンス化

Bean 定義は、本質的に 1 つ以上のオブジェクトを作成するためのレシピです。コンテナーは、要求されると名前付き Bean のレシピを調べ、その Bean 定義によってカプセル化された構成メタデータを使用して、実際のオブジェクトを作成(または取得)します。

XML ベースの構成メタデータを使用する場合、<bean/> 要素の class 属性でインスタンス化されるオブジェクトのタイプ(またはクラス)を指定します。通常、この class 属性(内部的には BeanDefinition インスタンスの Class プロパティ)は必須です。(例外については、インスタンスファクトリメソッドを使用したインスタンス化および Bean 定義の継承を参照してください) Class プロパティは、次の 2 つの方法のいずれかで使用できます。

  • 通常、new 演算子を使用した Java コードと多少同等の、コンストラクターをリフレクティブに呼び出すことにより、コンテナー自体が直接 Bean を作成する場合に構築される Bean クラスを指定します。

  • オブジェクトを作成するために呼び出される static ファクトリメソッドを含む実際のクラスを指定するには、あまり一般的ではないが、コンテナーがクラスで static ファクトリメソッドを呼び出して Bean を作成します。static ファクトリメソッドの呼び出しから返されるオブジェクトタイプは、同じクラスまたは完全に別のクラスです。

内部クラス名

static ネストクラスの Bean 定義を構成する場合は、ネストクラスのバイナリ名を使用する必要があります。

例: com.example パッケージに SomeThing というクラスがあり、この SomeThing クラスに OtherThing という static ネストされたクラスがある場合、Bean 定義の class 属性の値は com.example.SomeThing$OtherThing になります。

名前に $ 文字を使用して、ネストされたクラス名を外部クラス名から分離していることに注意してください。

コンストラクターによるインスタンス化

コンストラクターアプローチによって Bean を作成すると、すべての通常クラスが Spring で使用でき、互換性があります。つまり、開発中のクラスは、特定のインターフェースを実装したり、特定の方法でコーディングしたりする必要はありません。Bean クラスを指定するだけで十分です。ただし、その特定の Bean に使用する IoC のタイプによっては、デフォルト(空の)コンストラクターが必要になる場合があります。

Spring IoC コンテナーは、管理したいほぼすべてのクラスを管理できます。真の JavaBeans の管理に限定されません。ほとんどの Spring ユーザーは、デフォルト(引数なし)コンストラクターと、コンテナー内のプロパティをモデルにした適切な setter および getter のみを備えた実際の JavaBeans を好みます。コンテナーには、よりエキゾチックな非 Bean スタイルのクラスを含めることもできます。たとえば、JavaBean 仕様に絶対に準拠していないレガシー接続プールを使用する必要がある場合、Spring もそれを管理できます。

XML ベースの構成メタデータを使用すると、Bean クラスを次のように指定できます。

<bean id="exampleBean" class="examples.ExampleBean"/>

<bean name="anotherExample" class="examples.ExampleBeanTwo"/>

コンストラクターに引数を提供し(必要な場合)、オブジェクトの構築後にオブジェクトインスタンスプロパティを設定するメカニズムの詳細については、依存関係の注入を参照してください。

静的ファクトリメソッドを使用したインスタンス化

静的ファクトリメソッドを使用して作成する Bean を定義する場合、class 属性を使用して static ファクトリメソッドを含むクラスを指定し、factory-method という名前の属性を使用してファクトリメソッド自体の名前を指定します。このメソッドを(後述のオプションの引数を使用して)呼び出し、ライブオブジェクトを返すことができるはずです。ライブオブジェクトは、その後、コンストラクターを介して作成されたかのように扱われます。そのような Bean 定義の 1 つの用途は、レガシーコードで static ファクトリを呼び出すことです。

次の Bean 定義は、ファクトリメソッドを呼び出して Bean を作成することを指定しています。定義では、返されるオブジェクトのタイプ(クラス)は指定せず、ファクトリメソッドを含むクラスのみを指定します。この例では、createInstance() メソッドは静的メソッドである必要があります。次の例は、ファクトリメソッドを指定する方法を示しています。

<bean id="clientService"
    class="examples.ClientService"
    factory-method="createInstance"/>

次の例は、前述の Bean 定義で機能するクラスを示しています。

Java
public class ClientService {
    private static ClientService clientService = new ClientService();
    private ClientService() {}

    public static ClientService createInstance() {
        return clientService;
    }
}
Kotlin
class ClientService private constructor() {
    companion object {
        private val clientService = ClientService()
        fun createInstance() = clientService
    }
}

ファクトリメソッドに(オプションの)引数を提供し、オブジェクトがファクトリから返された後にオブジェクトインスタンスプロパティを設定するメカニズムの詳細については、依存関係と構成の詳細を参照してください。

インスタンスファクトリメソッドを使用したインスタンス化

静的ファクトリメソッドによるインスタンス化と同様に、インスタンスファクトリメソッドによるインスタンス化は、コンテナーから既存の Bean の非静的メソッドを呼び出して、新しい Bean を作成します。このメカニズムを使用するには、class 属性を空のままにし、factory-bean 属性で、オブジェクトを作成するために呼び出されるインスタンスメソッドを含む現在の(または親または祖先)コンテナーの Bean の名前を指定します。factory-method 属性を使用して、ファクトリメソッド自体の名前を設定します。次の例は、そのような Bean を構成する方法を示しています。

<!-- the factory bean, which contains a method called createInstance() -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
    <!-- inject any dependencies required by this locator bean -->
</bean>

<!-- the bean to be created via the factory bean -->
<bean id="clientService"
    factory-bean="serviceLocator"
    factory-method="createClientServiceInstance"/>

次の例は、対応するクラスを示しています。

Java
public class DefaultServiceLocator {

    private static ClientService clientService = new ClientServiceImpl();

    public ClientService createClientServiceInstance() {
        return clientService;
    }
}
Kotlin
class DefaultServiceLocator {
    companion object {
        private val clientService = ClientServiceImpl()
    }
    fun createClientServiceInstance(): ClientService {
        return clientService
    }
}

次の例に示すように、1 つのファクトリクラスは複数のファクトリメソッドも保持できます。

<bean id="serviceLocator" class="examples.DefaultServiceLocator">
    <!-- inject any dependencies required by this locator bean -->
</bean>

<bean id="clientService"
    factory-bean="serviceLocator"
    factory-method="createClientServiceInstance"/>

<bean id="accountService"
    factory-bean="serviceLocator"
    factory-method="createAccountServiceInstance"/>

次の例は、対応するクラスを示しています。

Java
public class DefaultServiceLocator {

    private static ClientService clientService = new ClientServiceImpl();

    private static AccountService accountService = new AccountServiceImpl();

    public ClientService createClientServiceInstance() {
        return clientService;
    }

    public AccountService createAccountServiceInstance() {
        return accountService;
    }
}
Kotlin
class DefaultServiceLocator {
    companion object {
        private val clientService = ClientServiceImpl()
        private val accountService = AccountServiceImpl()
    }

    fun createClientServiceInstance(): ClientService {
        return clientService
    }

    fun createAccountServiceInstance(): AccountService {
        return accountService
    }
}

このアプローチは、ファクトリ Bean 自体を依存性注入(DI)によって管理および構成できることを示しています。依存関係と構成の詳細を参照してください。

Spring のドキュメントでは、「ファクトリ Bean」は、Spring コンテナーで構成され、インスタンスまたは静的ファクトリメソッドを介してオブジェクトを作成する Bean を指します。対照的に、FactoryBean (大文字表記に注意)は、Spring 固有の FactoryBean  実装クラスを指します。
Bean のランタイムタイプの決定

特定の Bean の実行時タイプを決定することは簡単ではありません。Bean メタデータ定義で指定されたクラスは、単なる初期クラス参照であり、宣言されたファクトリメソッドと組み合わされるか、FactoryBean クラスであり、Bean のランタイムタイプが異なるか、インスタンスの場合はまったく設定されない可能性があります。レベルのファクトリメソッド(代わりに、指定された factory-bean 名を介して解決されます)。さらに、AOP プロキシは、ターゲット Bean の実際のタイプ(実装されたインターフェースのみ)の限定的な公開で、インターフェースベースのプロキシで Bean インスタンスをラップする場合があります。

特定の Bean の実際のランタイムタイプを調べるには、指定された Bean 名の BeanFactory.getType 呼び出しをお勧めします。これは、上記のすべてのケースを考慮に入れ、BeanFactory.getBean 呼び出しが同じ Bean 名に対して返すオブジェクトのタイプを返します。

1.4. 依存関係

典型的なエンタープライズアプリケーションは、単一のオブジェクト(または Spring 用語では Bean)で構成されていません。最も単純なアプリケーションでさえ、エンドユーザーが一貫したアプリケーションと見なすものを提示するために連携するいくつかのオブジェクトを持っています。この次のセクションでは、スタンドアロンの多数の Bean 定義の定義から、ゴールを達成するためにオブジェクトが協力する完全に実現されたアプリケーションに至るまでの方法について説明します。

1.4.1. 依存性注入

依存性注入(DI)は、コンストラクターの引数、ファクトリメソッドへの引数、またはオブジェクトインスタンスの構築後に設定されるプロパティを通じてのみ、オブジェクトが依存関係(つまり、動作する他のオブジェクト)を定義するプロセスです。ファクトリメソッドから返されます。コンテナーは、Bean を作成するときにそれらの依存関係を注入します。このプロセスは、基本的に、クラスの直接構築または Service Locator パターンを使用して、Bean 自体のインスタンス化または依存関係の位置を制御する Bean 自体の逆(つまり、Inversion of Control)です。

DI の原則によりコードは簡潔になり、オブジェクトに依存関係が提供されると、デカップリングがより効果的になります。オブジェクトは依存関係を検索せず、依存関係の場所またはクラスを知りません。その結果、特に依存関係がインターフェースまたは抽象基本クラスにある場合、クラスのテストが容易になり、ユニットテストでスタブまたはモックの実装を使用できるようになります。

DI は、コンストラクターベースの依存性注入setter ベースの依存性注入の 2 つの主要なバリアントに存在します。

コンストラクターベースの依存性注入

コンストラクターベースの DI は、それぞれが依存関係を表すいくつかの引数を使用してコンストラクターを呼び出すコンテナーによって実現されます。特定の引数を使用して static ファクトリメソッドを呼び出して Bean を構築することはほぼ同等であり、この説明ではコンストラクターと static ファクトリメソッドの引数を同様に扱います。次の例は、コンストラクターの注入でのみ依存関係を注入できるクラスを示しています。

Java
public class SimpleMovieLister {

    // the SimpleMovieLister has a dependency on a MovieFinder
    private MovieFinder movieFinder;

    // a constructor so that the Spring container can inject a MovieFinder
    public SimpleMovieLister(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // business logic that actually uses the injected MovieFinder is omitted...
}
Kotlin
// a constructor so that the Spring container can inject a MovieFinder
class SimpleMovieLister(private val movieFinder: MovieFinder) {
    // business logic that actually uses the injected MovieFinder is omitted...
}

このクラスには特別なものは何もないことに注意してください。これは、コンテナー固有のインターフェース、基本クラス、またはアノテーションに依存しない POJO です。

コンストラクター引数解決

コンストラクターの引数解決の一致は、引数の型を使用して発生します。Bean 定義のコンストラクター引数に潜在的なあいまいさが存在しない場合、コンストラクター引数が Bean 定義で定義される順序は、Bean がインスタンス化されるときにそれらの引数が適切なコンストラクターに提供される順序です。次のクラスを検討してください。

Java
package x.y;

public class ThingOne {

    public ThingOne(ThingTwo thingTwo, ThingThree thingThree) {
        // ...
    }
}
Kotlin
package x.y

class ThingOne(thingTwo: ThingTwo, thingThree: ThingThree)

ThingTwo クラスと ThingThree クラスが継承によって関連付けられていないと仮定すると、潜在的なあいまいさは存在しません。次の構成は正常に機能し、<constructor-arg/> 要素でコンストラクター引数のインデックスまたはタイプを明示的に指定する必要はありません。

<beans>
    <bean id="beanOne" class="x.y.ThingOne">
        <constructor-arg ref="beanTwo"/>
        <constructor-arg ref="beanThree"/>
    </bean>

    <bean id="beanTwo" class="x.y.ThingTwo"/>

    <bean id="beanThree" class="x.y.ThingThree"/>
</beans>

別の Bean が参照される場合、タイプは既知であり、一致が発生する可能性があります(前の例の場合のように)。<value>true</value> などの単純なタイプが使用される場合、Spring は値のタイプを判別できないため、ヘルプなしではタイプごとに一致できません。次のクラスを検討してください。

Java
package examples;

public class ExampleBean {

    // Number of years to calculate the Ultimate Answer
    private int years;

    // The Answer to Life, the Universe, and Everything
    private String ultimateAnswer;

    public ExampleBean(int years, String ultimateAnswer) {
        this.years = years;
        this.ultimateAnswer = ultimateAnswer;
    }
}
Kotlin
package examples

class ExampleBean(
    private val years: Int, // Number of years to calculate the Ultimate Answer
    private val ultimateAnswer: String// The Answer to Life, the Universe, and Everything
)
コンストラクター引数型の一致

上記のシナリオでは、type 属性を使用してコンストラクター引数の型を明示的に指定した場合、コンテナーは単純型との型一致を使用できます。次の例に示すように:

<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg type="int" value="7500000"/>
    <constructor-arg type="java.lang.String" value="42"/>
</bean>
コンストラクター引数インデックス

次の例に示すように、index 属性を使用して、コンストラクター引数のインデックスを明示的に指定できます。

<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg index="0" value="7500000"/>
    <constructor-arg index="1" value="42"/>
</bean>

複数の単純な値のあいまいさを解決することに加えて、インデックスを指定すると、コンストラクターに同じ型の 2 つの引数がある場合のあいまいさを解決できます。

インデックスは 0 ベースです。
コンストラクター引数名

次の例に示すように、コンストラクターパラメーター名を使用して値を明確にすることもできます。

<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg name="years" value="7500000"/>
    <constructor-arg name="ultimateAnswer" value="42"/>
</bean>

この機能をそのまま使用するには、Spring がコンストラクターからパラメーター名を検索できるように、デバッグフラグを有効にしてコードをコンパイルする必要があることに注意してください。デバッグフラグを使用してコードをコンパイルできない場合、またはコンパイルしたくない場合は、@ConstructorProperties: Oracle JDK アノテーションを使用して、コンストラクター引数に明示的に名前を付けることができます。サンプルクラスは次のようになります。

Java
package examples;

public class ExampleBean {

    // Fields omitted

    @ConstructorProperties({"years", "ultimateAnswer"})
    public ExampleBean(int years, String ultimateAnswer) {
        this.years = years;
        this.ultimateAnswer = ultimateAnswer;
    }
}
Kotlin
package examples

class ExampleBean
@ConstructorProperties("years", "ultimateAnswer")
constructor(val years: Int, val ultimateAnswer: String)
setter ベースの依存性注入

setter ベースの DI は、引数なしのコンストラクターまたは引数なしの static ファクトリメソッドを呼び出して Bean をインスタンス化した後に、コンテナーが setter メソッドを Bean で呼び出すことによって実現されます。

次の例は、純粋な setter インジェクションを使用することによってのみ依存関係をインジェクトできるクラスを示しています。このクラスは従来の Java です。これは、コンテナー固有のインターフェース、基本クラス、またはアノテーションに依存しない POJO です。

Java
public class SimpleMovieLister {

    // the SimpleMovieLister has a dependency on the MovieFinder
    private MovieFinder movieFinder;

    // a setter method so that the Spring container can inject a MovieFinder
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // business logic that actually uses the injected MovieFinder is omitted...
}
Kotlin
class SimpleMovieLister {

    // a late-initialized property so that the Spring container can inject a MovieFinder
    lateinit var movieFinder: MovieFinder

    // business logic that actually uses the injected MovieFinder is omitted...
}

ApplicationContext は、管理する Bean のコンストラクターベースおよび setter ベースの DI をサポートします。また、いくつかの依存関係がコンストラクターアプローチによって既に注入された後、setter ベースの DI もサポートしています。BeanDefinition の形式で依存関係を設定し、PropertyEditor インスタンスと組み合わせて使用して、プロパティをある形式から別の形式に変換します。ただし、ほとんどの Spring ユーザーはこれらのクラスを直接(つまり、プログラムで)使用せず、XML bean 定義、アノテーション付きコンポーネント(つまり、@Component@Controller などのアノテーションが付けられたクラス)、または Java ベースの @Configuration クラスの @Bean メソッドを使用します。これらのソースは、内部で BeanDefinition のインスタンスに変換され、Spring IoC コンテナーインスタンス全体をロードするために使用されます。

コンストラクターベースまたは setter ベースの DI ?

コンストラクターベースの DI と setter ベースの DI を混在させることができるため、必須の依存関係にはコンストラクターを使用し、オプションの依存関係には setter メソッドまたは構成メソッドを使用することをお勧めします。setter メソッドで @Required アノテーションを使用すると、プロパティを必須の依存関係にすることができます。ただし、引数のプログラムによる検証を伴うコンストラクター注入が望ましいです。

Spring チームは通常、アプリケーションコンポーネントを不変オブジェクトとして実装し、必要な依存関係が null でないことを保証できるため、コンストラクター注入を推奨しています。さらに、コンストラクターが注入したコンポーネントは常に、完全に初期化された状態でクライアント(呼び出し)コードに返されます。副次的な注意事項として、コンストラクター引数が多数あることはコードの悪臭であり、クラスの責任が多すぎる可能性があることを意味し、関心事の適切な分離に対処するためにリファクタリングする必要があります。

Setter インジェクションは、主に、クラス内で適切なデフォルト値を割り当てることができるオプションの依存関係にのみ使用する必要があります。それ以外の場合、コードが依存関係を使用するすべての場所で非 null チェックを実行する必要があります。setter インジェクションの利点の 1 つは、setter メソッドが、そのクラスのオブジェクトを後で再構成または再インジェクションしやすくすることです。JMX MBean による管理は、setter インジェクションの魅力的なユースケースです。

特定のクラスに最も意味のある DI スタイルを使用します。場合によっては、ソースがないサードパーティクラスを処理するときに、選択が行われます。例:サードパーティのクラスが setter メソッドを公開しない場合、コンストラクター注入が DI の唯一の利用可能な形式である可能性があります。

依存関係解決プロセス

コンテナーは、次のように Bean 依存関係の解決を実行します。

  • ApplicationContext は、すべての Bean を記述する構成メタデータで作成および初期化されます。構成メタデータは、XML、Java コード、またはアノテーションによって指定できます。

  • 各 Bean の依存関係は、プロパティ、コンストラクター引数、または静的ファクトリメソッドの引数の形式で表されます(通常のコンストラクターの代わりにそれを使用する場合)。これらの依存関係は、Bean が実際に作成されるときに Bean に提供されます。

  • 各プロパティまたはコンストラクターの引数は、設定する値の実際の定義、またはコンテナー内の別の Bean への参照です。

  • 値である各プロパティまたはコンストラクター引数は、指定された形式からそのプロパティまたはコンストラクター引数の実際の型に変換されます。デフォルトでは、Spring は、ストリング形式で提供された値を、intlongStringboolean などのすべての組み込みタイプに変換できます。

Spring コンテナーは、コンテナーの作成時に各 Bean の構成を検証します。ただし、Bean が実際に作成されるまで、Bean プロパティ自体は設定されません。シングルトンスコープで事前インスタンス化(デフォルト)に設定された Bean は、コンテナーの作成時に作成されます。スコープは Bean スコープで定義されています。それ以外の場合、Bean はリクエストされたときにのみ作成されます。Bean を作成すると、Bean の依存関係とその依存関係の依存関係(など)が作成および割り当てられるため、Bean のグラフが作成される可能性があります。これらの依存関係間の解決の不一致は、遅れて、つまり、影響を受ける Bean を最初に作成したときに表示されることに注意してください。

循環依存関係

主にコンストラクター注入を使用する場合、解決できない循環依存シナリオを作成することができます。

次に例を示します : クラス A は、コンストラクター注入を通じてクラス B のインスタンスを必要とし、クラス B は、コンストラクター注入を通じてクラス A のインスタンスを必要とします。クラス A および B の Bean を相互に注入するように構成すると、Spring IoC コンテナーは実行時にこの循環参照を検出し、BeanCurrentlyInCreationException をスローします。

考えられる解決策の 1 つは、一部のクラスのソースコードを編集して、コンストラクターではなく setter で構成することです。または、コンストラクター注入を避け、setter 注入のみを使用します。つまり、推奨されていませんが、setter インジェクションで循環依存関係を構成できます。

典型的な場合(循環依存関係なし)とは異なり、Bean A と Bean B の間の循環依存関係により、完全に初期化される前に、一方の Bean が他方に強制的に注入されます(従来の鶏と卵のシナリオ)。

通常、Spring が正しいことを行うと信頼できます。コンテナーのロード時に、存在しない Bean への参照や循環依存などの構成の課題を検出します。Spring は、Bean が実際に作成されるときに、プロパティを設定し、可能な限り遅く依存関係を解決します。これは、正しくロードされた Spring コンテナーは、オブジェクトまたはその依存関係の作成に課題がある場合、オブジェクトをリクエストしたときに例外を後で生成できることを意味します。たとえば、Bean は、欠落または無効の結果として例外をスローします。プロパティ。構成の課題の可視性が遅れる可能性があるため、ApplicationContext 実装はデフォルトでシングルトン Bean を事前にインスタンス化します。これらの Bean を実際に必要になる前に作成するための事前の時間とメモリが必要になりますが、ApplicationContext の作成時ではなく、後から構成の課題を発見します。このデフォルトの動作をオーバーライドして、シングルトン Bean が事前にインスタンス化されるのではなく、遅延して初期化されるようにすることができます。

循環依存性が存在しない場合、1 つ以上のコラボレーション Bean が依存 Bean に注入されると、各コラボレーション Bean は、依存 Bean に注入される前に完全に構成されます。つまり、Bean A が Bean B に依存する場合、Spring コンテナーは、Bean A で setter メソッドを呼び出す前に、Bean B を完全に構成します。つまり、Bean がインスタンス化され(事前にインスタンス化されたシングルトンでない場合)、その依存関係が設定され、関連するライフサイクルメソッド(構成済みの init メソッドInitializingBean コールバックメソッドなど)が呼び出されます。

依存性注入の例

次の例では、setter ベースの DI に XML ベースの構成メタデータを使用しています。Spring XML 構成ファイルのごく一部は、次のようにいくつかの Bean 定義を指定しています。

<bean id="exampleBean" class="examples.ExampleBean">
    <!-- setter injection using the nested ref element -->
    <property name="beanOne">
        <ref bean="anotherExampleBean"/>
    </property>

    <!-- setter injection using the neater ref attribute -->
    <property name="beanTwo" ref="yetAnotherBean"/>
    <property name="integerProperty" value="1"/>
</bean>

<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>

次の例は、対応する ExampleBean クラスを示しています。

Java
public class ExampleBean {

    private AnotherBean beanOne;

    private YetAnotherBean beanTwo;

    private int i;

    public void setBeanOne(AnotherBean beanOne) {
        this.beanOne = beanOne;
    }

    public void setBeanTwo(YetAnotherBean beanTwo) {
        this.beanTwo = beanTwo;
    }

    public void setIntegerProperty(int i) {
        this.i = i;
    }
}
Kotlin
class ExampleBean {
    lateinit var beanOne: AnotherBean
    lateinit var beanTwo: YetAnotherBean
    var i: Int = 0
}

上記の例では、setter は XML ファイルで指定されたプロパティと一致するように宣言されています。次の例では、コンストラクターベースの DI を使用しています。

<bean id="exampleBean" class="examples.ExampleBean">
    <!-- constructor injection using the nested ref element -->
    <constructor-arg>
        <ref bean="anotherExampleBean"/>
    </constructor-arg>

    <!-- constructor injection using the neater ref attribute -->
    <constructor-arg ref="yetAnotherBean"/>

    <constructor-arg type="int" value="1"/>
</bean>

<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>

次の例は、対応する ExampleBean クラスを示しています。

Java
public class ExampleBean {

    private AnotherBean beanOne;

    private YetAnotherBean beanTwo;

    private int i;

    public ExampleBean(
        AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {
        this.beanOne = anotherBean;
        this.beanTwo = yetAnotherBean;
        this.i = i;
    }
}
Kotlin
class ExampleBean(
        private val beanOne: AnotherBean,
        private val beanTwo: YetAnotherBean,
        private val i: Int)

Bean 定義で指定されたコンストラクター引数は、ExampleBean のコンストラクターへの引数として使用されます。

ここで、コンストラクターを使用する代わりに、Spring が static ファクトリメソッドを呼び出してオブジェクトのインスタンスを返すように指示されている、この例のバリアントを考えます。

<bean id="exampleBean" class="examples.ExampleBean" factory-method="createInstance">
    <constructor-arg ref="anotherExampleBean"/>
    <constructor-arg ref="yetAnotherBean"/>
    <constructor-arg value="1"/>
</bean>

<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>

次の例は、対応する ExampleBean クラスを示しています。

Java
public class ExampleBean {

    // a private constructor
    private ExampleBean(...) {
        ...
    }

    // a static factory method; the arguments to this method can be
    // considered the dependencies of the bean that is returned,
    // regardless of how those arguments are actually used.
    public static ExampleBean createInstance (
        AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {

        ExampleBean eb = new ExampleBean (...);
        // some other operations...
        return eb;
    }
}
Kotlin
class ExampleBean private constructor() {
    companion object {
        // a static factory method; the arguments to this method can be
        // considered the dependencies of the bean that is returned,
        // regardless of how those arguments are actually used.
        fun createInstance(anotherBean: AnotherBean, yetAnotherBean: YetAnotherBean, i: Int): ExampleBean {
            val eb = ExampleBean (...)
            // some other operations...
            return eb
        }
    }
}

static ファクトリメソッドへの引数は、コンストラクターが実際に使用された場合とまったく同じように、<constructor-arg/> 要素によって提供されます。ファクトリメソッドによって返されるクラスの型は、static ファクトリメソッドを含むクラスと同じ型である必要はありません(ただし、この例ではそうです)。インスタンス(非静的)ファクトリメソッドは(class 属性の代わりに factory-bean 属性を使用することを除いて)基本的に同じ方法で使用できるため、ここではそれらの詳細については説明しません。

1.4.2. 依存関係と構成の詳細

前のセクションで記述されていたように、Bean プロパティとコンストラクター引数を、他の管理対象 Bean(コラボレーター)への参照またはインラインで定義された値として定義できます。Spring の XML ベースの構成メタデータは、この目的のために <property/> および <constructor-arg/> 要素内のサブ要素タイプをサポートします。

ストレート値 (プリミティブ、文字列など)

<property/> 要素の value 属性は、人間が読める文字列表現としてプロパティまたはコンストラクター引数を指定します。Spring の変換サービスは、これらの値を String からプロパティまたは引数の実際のタイプに変換するために使用されます。次の例は、設定されるさまざまな値を示しています。

<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <!-- results in a setDriverClassName(String) call -->
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
    <property name="username" value="root"/>
    <property name="password" value="misterkaoli"/>
</bean>

次の例では、さらに簡潔な XML 構成に p-namespace を使用しています。

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close"
        p:driverClassName="com.mysql.jdbc.Driver"
        p:url="jdbc:mysql://localhost:3306/mydb"
        p:username="root"
        p:password="misterkaoli"/>

</beans>

上記の XML はより簡潔です。ただし、Bean 定義を作成するときにプロパティの自動補完をサポートする IDE(IntelliJ IDEA Eclipse Pleiades All in One (STS, Lombok 付属) または Eclipse 用 Spring Tools (英語) など)を使用しない限り、設計時ではなく実行時にタイプミスが検出されます。このような IDE の支援を強くお勧めします。

次のように、java.util.Properties インスタンスを構成することもできます。

<bean id="mappings"
    class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">

    <!-- typed as a java.util.Properties -->
    <property name="properties">
        <value>
            jdbc.driver.className=com.mysql.jdbc.Driver
            jdbc.url=jdbc:mysql://localhost:3306/mydb
        </value>
    </property>
</bean>

Spring コンテナーは、JavaBeans PropertyEditor メカニズムを使用して、<value/> エレメント内のテキストを java.util.Properties インスタンスに変換します。これは便利なショートカットであり、Spring チームが value 属性スタイルよりもネストされた <value/> 要素の使用を好む数少ない場所の 1 つです。

idref 要素

idref 要素は、コンテナー内の別の Bean の id (文字列値 - 参照ではない)を <constructor-arg/> または <property/> 要素に渡すための単なるエラー防止方法です。次の例は、その使用方法を示しています。

<bean id="theTargetBean" class="..."/>

<bean id="theClientBean" class="...">
    <property name="targetName">
        <idref bean="theTargetBean"/>
    </property>
</bean>

上記の Bean 定義スニペットは、次のスニペットと(実行時に)まったく同じです。

<bean id="theTargetBean" class="..." />

<bean id="client" class="...">
    <property name="targetName" value="theTargetBean"/>
</bean>

idref タグを使用すると、コンテナーはデプロイの時点で、参照された名前の Bean が実際に存在することを検証できるため、最初の形式が 2 番目の形式よりも好ましいです。2 番目のバリエーションでは、client Bean の targetName プロパティに渡される値の検証は実行されません。誤字は、client Bean が実際にインスタンス化されたときにのみ検出されます(最も致命的な結果になる可能性があります)。client Bean がプロトタイプ Bean である場合、このタイプミスと結果の例外は、コンテナーがデプロイされてからずっと後に発見される可能性があります。

idref エレメントの local 属性は、通常の bean 参照を超える値を提供しないため、4.0 Bean XSD ではサポートされなくなりました。4.0 スキーマにアップグレードするときに、既存の idref local 参照を idref bean に変更します。

<idref/> 要素が価値をもたらす一般的な場所(少なくとも Spring 2.0 より前のバージョン)は、ProxyFactoryBean Bean 定義の AOP インターセプターの構成にあります。インターセプター名を指定するときに <idref/> 要素を使用すると、インターセプター ID のスペルミスを防ぐことができます。

他の Bean への参照 (コラボレーター)

ref 要素は、<constructor-arg/> または <property/> 定義要素内の最後の要素です。ここでは、Bean の指定されたプロパティの値を、コンテナーによって管理される別の Bean(コラボレーター)への参照に設定します。参照される Bean は、プロパティが設定される Bean の依存関係であり、プロパティが設定される前に必要に応じて初期化されます。(コラボレーターがシングルトン Bean である場合、コンテナーによって既に初期化されている可能性があります)すべての参照は、最終的には別のオブジェクトへの参照です。スコープと検証は、bean 属性または parent 属性を介して他のオブジェクトの ID または名前を指定するかどうかによって異なります。

<ref/> タグの bean 属性を介してターゲット Bean を指定するのが最も一般的な形式であり、同じ XML ファイル内にあるかどうかに関係なく、同じコンテナーまたは親コンテナー内の Bean への参照を作成できます。bean 属性の値は、ターゲット Bean の id 属性と同じでも、ターゲット Bean の name 属性の値の 1 つと同じでもかまいません。次の例は、ref 要素の使用方法を示しています。

<ref bean="someBean"/>

parent 属性を介してターゲット Bean を指定すると、現在のコンテナーの親コンテナーにある Bean への参照が作成されます。parent 属性の値は、ターゲット Bean の id 属性またはターゲット Bean の name 属性の値のいずれかと同じである場合があります。ターゲット Bean は、現在のコンテナーの親コンテナーに存在する必要があります。この Bean 参照バリアントは、主にコンテナーの階層があり、親 Bean と同じ名前のプロキシで親コンテナー内の既存の Bean をラップする場合に使用する必要があります。次のリストのペアは、parent 属性の使用方法を示しています。

<!-- in the parent context -->
<bean id="accountService" class="com.something.SimpleAccountService">
    <!-- insert dependencies as required as here -->
</bean>
<!-- in the child (descendant) context -->
<bean id="accountService" <!-- bean name is the same as the parent bean -->
    class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="target">
        <ref parent="accountService"/> <!-- notice how we refer to the parent bean -->
    </property>
    <!-- insert other configuration and dependencies as required here -->
</bean>
ref エレメントの local 属性は、通常の bean 参照を超える値を提供しないため、4.0 Bean XSD ではサポートされなくなりました。4.0 スキーマにアップグレードするときに、既存の ref local 参照を ref bean に変更します。
インナー bean

<property/> または <constructor-arg/> 要素内の <bean/> 要素は、次の例に示すように、内部 Bean を定義します。

<bean id="outer" class="...">
    <!-- instead of using a reference to a target bean, simply define the target bean inline -->
    <property name="target">
        <bean class="com.example.Person"> <!-- this is the inner bean -->
            <property name="name" value="Fiona Apple"/>
            <property name="age" value="25"/>
        </bean>
    </property>
</bean>

内部 Bean 定義には、定義済みの ID または名前は必要ありません。指定されている場合、コンテナーはそのような値を識別子として使用しません。コンテナーは、作成時に scope フラグも無視します。これは、内部 Bean は常に匿名であり、常に外部 Bean で作成されるためです。インナー Bean に独立してアクセスしたり、内包する Bean 以外のコラボレーション Bean に注入したりすることはできません。

コーナーケースとして、たとえばシングルトン Bean に含まれるリクエストスコープの内部 Bean の場合、カスタムスコープから破棄コールバックを受け取ることができます。内側の Bean インスタンスの作成は、含まれる Bean に関連付けられていますが、破棄コールバックにより、リクエストスコープのライフサイクルに参加できます。これは一般的なシナリオではありません。通常、内部 Bean は、含まれる Bean のスコープを単に共有します。

コレクション

<list/><set/><map/> および <props/> エレメントは、Java Collection タイプ ListSetMap および Properties のプロパティおよび引数をそれぞれ設定します。次の例は、それらの使用方法を示しています。

<bean id="moreComplexObject" class="example.ComplexObject">
    <!-- results in a setAdminEmails(java.util.Properties) call -->
    <property name="adminEmails">
        <props>
            <prop key="administrator">[email protected] (英語)  </prop>
            <prop key="support">[email protected] (英語)  </prop>
            <prop key="development">[email protected] (英語)  </prop>
        </props>
    </property>
    <!-- results in a setSomeList(java.util.List) call -->
    <property name="someList">
        <list>
            <value>a list element followed by a reference</value>
            <ref bean="myDataSource" />
        </list>
    </property>
    <!-- results in a setSomeMap(java.util.Map) call -->
    <property name="someMap">
        <map>
            <entry key="an entry" value="just some string"/>
            <entry key ="a ref" value-ref="myDataSource"/>
        </map>
    </property>
    <!-- results in a setSomeSet(java.util.Set) call -->
    <property name="someSet">
        <set>
            <value>just some string</value>
            <ref bean="myDataSource" />
        </set>
    </property>
</bean>

マップキーまたは値の値、または設定値は、次の要素のいずれかです。

bean | ref | idref | list | set | map | props | value | null
コレクションのマージ

Spring コンテナーは、コレクションのマージもサポートしています。アプリケーション開発者は、親 <list/><map/><set/> または <props/> 要素を定義し、子 <list/><map/><set/> または <props/> 要素が親コレクションから値を継承およびオーバーライドするようにできます。つまり、子コレクションの値は、親コレクションと子コレクションの要素をマージした結果であり、子コレクションの要素は親コレクションで指定された値をオーバーライドします。

マージに関するこのセクションでは、親子 Bean メカニズムについて説明します。親および子 Bean 定義を持つ読者未知は、続行する前に関連するセクションを読みたい場合があります。

次の例は、コレクションのマージを示しています。

<beans>
    <bean id="parent" abstract="true" class="example.ComplexObject">
        <property name="adminEmails">
            <props>
                <prop key="administrator">[email protected] (英語)  </prop>
                <prop key="support">[email protected] (英語)  </prop>
            </props>
        </property>
    </bean>
    <bean id="child" parent="parent">
        <property name="adminEmails">
            <!-- the merge is specified on the child collection definition -->
            <props merge="true">
                <prop key="sales">[email protected] (英語)  </prop>
                <prop key="support">[email protected] (英語)  </prop>
            </props>
        </property>
    </bean>
<beans>

child Bean 定義の adminEmails プロパティの <props/> 要素の merge=true 属性の使用に注意してください。child Bean がコンテナーによって解決およびインスタンス化されると、結果のインスタンスには、子の adminEmails コレクションを親の adminEmails コレクションとマージした結果を含む adminEmailsProperties コレクションが含まれます。次のリストに結果を示します。

子 Properties コレクションの値セットは親 <props/> からすべてのプロパティ要素を継承し、support 値の子の値は親コレクションの値をオーバーライドします。

このマージ動作は、<list/><map/> および <set/> コレクションタイプに同様に適用されます。<list/> 要素の特定の場合、List コレクションタイプに関連付けられたセマンティクス(つまり、値の ordered コレクションの概念)は維持されます。親の値は、子リストのすべての値よりも優先されます。MapSet および Properties コレクションタイプの場合、順序付けは存在しません。コンテナーが内部で使用する、関連する MapSet および Properties 実装タイプの基礎となるコレクションタイプには、順序付けのセマンティクスは有効ではありません。

コレクションのマージの制限

異なるコレクションタイプ(Map と List など)をマージすることはできません。そうしようとすると、適切な Exception がスローされます。merge 属性は、下位の継承された子定義で指定する必要があります。親コレクション定義で merge 属性を指定することは冗長であり、目的のマージにはなりません。

強く型付けされたコレクション

Java 5 でのジェネリック型の導入により、強く型付けされたコレクションを使用できます。つまり、String 要素のみを含むことができるように Collection 型を宣言することができます。Spring を使用して、厳密に型指定された Collection を Bean に依存性注入すると、Spring の型変換サポートを利用して、厳密に型指定された Collection インスタンスの要素を適切な型に変換してから、Collection 次の Java クラスと Bean 定義は、その方法を示しています。

Java
public class SomeClass {

    private Map<String, Float> accounts;

    public void setAccounts(Map<String, Float> accounts) {
        this.accounts = accounts;
    }
}
Kotlin
class SomeClass {
    lateinit var accounts: Map<String, Float>
}
<beans>
    <bean id="something" class="x.y.SomeClass">
        <property name="accounts">
            <map>
                <entry key="one" value="9.99"/>
                <entry key="two" value="2.75"/>
                <entry key="six" value="3.99"/>
            </map>
        </property>
    </bean>
</beans>

something Bean の accounts プロパティがインジェクション用に準備されると、強く型付けされた Map<String, Float> の要素タイプに関するジェネリック情報がリフレクションによって利用可能になります。Spring の型変換インフラストラクチャは、さまざまな値要素を Float 型であると認識し、文字列値(9.99, 2.753.99)は実際の Float 型に変換されます。

NULL および空の文字列値

Spring は、プロパティなどの空の引数を空の Strings として扱います。次の XML ベースの構成メタデータスニペットは、email プロパティを空の String 値("")に設定します。

<bean class="ExampleBean">
    <property name="email" value=""/>
</bean>

上記の例は、次の Java コードと同等です。

Java
exampleBean.setEmail("");
Kotlin
exampleBean.email = ""

<null/> 要素は null 値を処理します。次のリストに例を示します。

<bean class="ExampleBean">
    <property name="email">
        <null/>
    </property>
</bean>

上記の構成は、次の Java コードと同等です。

Java
exampleBean.setEmail(null);
Kotlin
exampleBean.email = null
p-namespace を使用した XML ショートカット

p- 名前空間では、ネストされた <property/> 要素の代わりに bean 要素の属性を使用して、プロパティ値をコラボレーションする Bean、またはその両方を記述することができます。

Spring は、XML スキーマ定義に基づく名前空間を持つ拡張可能な構成形式をサポートしています。この章で説明する beans 構成フォーマットは、XML スキーマドキュメントで定義されています。ただし、p-namespace は XSD ファイルでは定義されておらず、Spring のコアにのみ存在します。

次の例は、同じ結果に解決される 2 つの XML スニペット(1 つ目は標準 XML 形式を使用し、2 つ目は p-namespace を使用)を示しています。

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean name="classic" class="com.example.ExampleBean">
        <property name="email" value="[email protected] (英語)  "/>
    </bean>

    <bean name="p-namespace" class="com.example.ExampleBean"
        p:email="[email protected] (英語)  "/>
</beans>

この例は、Bean 定義の email と呼ばれる p 名前空間の属性を示しています。これは、Spring にプロパティ宣言を含めるように指示します。前述したように、p-namespace にはスキーマ定義がないため、属性の名前をプロパティ名に設定できます。

次の例には、さらに 2 つの Bean 定義が含まれており、両方とも別の Bean への参照を持っています。

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean name="john-classic" class="com.example.Person">
        <property name="name" value="John Doe"/>
        <property name="spouse" ref="jane"/>
    </bean>

    <bean name="john-modern"
        class="com.example.Person"
        p:name="John Doe"
        p:spouse-ref="jane"/>

    <bean name="jane" class="com.example.Person">
        <property name="name" value="Jane Doe"/>
    </bean>
</beans>

この例には、p-namespace を使用したプロパティ値だけでなく、プロパティ参照を宣言するための特別な形式も含まれています。最初の Bean 定義では <property name="spouse" ref="jane"/> を使用して Bean john から Bean jane への参照を作成しますが、2 番目の Bean 定義では p:spouse-ref="jane" を属性として使用してまったく同じことを行います。この場合、spouse はプロパティ名ですが、-ref 部分は、これがストレート値ではなく、別の Bean への参照であることを示しています。

p-namespace は、標準の XML 形式ほど柔軟ではありません。例:プロパティ参照を宣言する形式は、Ref で終わるプロパティと衝突しますが、標準の XML 形式は衝突しません。3 つのアプローチすべてを同時に使用する XML ドキュメントを作成しないように、アプローチを慎重に選択し、チームメンバーに伝えることをお勧めします。
c-namespace を使用した XML ショートカット

p-namespace を使用した XML ショートカットと同様に、Spring 3.1, で導入された c-namespace では、ネストされた constructor-arg 要素ではなく、コンストラクター引数を構成するためのインライン属性を使用できます。

次の例では、c: 名前空間を使用して、コンストラクターベースの依存性注入からと同じことを行います。

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:c="http://www.springframework.org/schema/c"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="beanTwo" class="x.y.ThingTwo"/>
    <bean id="beanThree" class="x.y.ThingThree"/>

    <!-- traditional declaration with optional argument names -->
    <bean id="beanOne" class="x.y.ThingOne">
        <constructor-arg name="thingTwo" ref="beanTwo"/>
        <constructor-arg name="thingThree" ref="beanThree"/>
        <constructor-arg name="email" value="[email protected] (英語)  "/>
    </bean>

    <!-- c-namespace declaration with argument names -->
    <bean id="beanOne" class="x.y.ThingOne" c:thingTwo-ref="beanTwo"
        c:thingThree-ref="beanThree" c:email="[email protected] (英語)  "/>

</beans>

c: 名前空間は、名前でコンストラクター引数を設定するために、p: の名前空間(Bean 参照の末尾の -ref)と同じ規則を使用します。同様に、XSD スキーマ(Spring コア内に存在する)で定義されていない場合でも、XML ファイルで宣言する必要があります。

コンストラクターの引数名が使用できないまれな場合(通常、デバッグ情報なしでバイトコードがコンパイルされた場合)、次のように引数インデックスへのフォールバックを使用できます。

<!-- c-namespace index declaration -->
<bean id="beanOne" class="x.y.ThingOne" c:_0-ref="beanTwo" c:_1-ref="beanThree"
    c:_2="[email protected] (英語)  "/>
XML 文法により、XML 属性名は数字で始めることができないため(インデックスの表記法では先頭の _ の存在が必要です(一部の IDE では許可されていますが)。対応するインデックス表記は <constructor-arg> 要素にも使用できますが、通常は宣言の単純な順序で十分なので一般的には使用されません。

実際には、コンストラクター解決メカニズムは引数のマッチングにおいて非常に効率的です。本当に必要な場合を除き、構成全体で名前表記を使用することをお勧めします。

複合プロパティ名

最終プロパティ名を除くパスのすべてのコンポーネントが null でない限り、Bean プロパティを設定するときに複合またはネストされたプロパティ名を使用できます。以下の Bean 定義を考慮してください。

<bean id="something" class="things.ThingOne">
    <property name="fred.bob.sammy" value="123" />
</bean>

something Bean には fred プロパティがあり、bob プロパティには sammy プロパティがあり、その最終 sammy プロパティには 123 の値が設定されています。これが機能するためには、Bean の作成後に something の fred プロパティと fred の bob プロパティが null であってはなりません。そうでない場合、NullPointerException がスローされます。

1.4.3. depends-on を使用する

Bean が別の Bean の依存関係である場合、通常は、ある Bean が別の Bean のプロパティとして設定されていることを意味します。通常、XML ベースの構成メタデータの <ref/> 要素でこれを実現します。ただし、Bean 間の依存関係が直接的でない場合があります。例は、データベースドライバーの登録など、クラス内の静的初期化子をトリガーする必要がある場合です。depends-on 属性は、この要素を使用する Bean が初期化される前に、1 つ以上の Bean を明示的に初期化することを明示的に許可できます。次の例では、depends-on 属性を使用して、単一の Bean への依存関係を表現しています。

<bean id="beanOne" class="ExampleBean" depends-on="manager"/>
<bean id="manager" class="ManagerBean" />

複数の Bean への依存関係を表現するには、depends-on 属性の値として Bean 名のリストを指定します(コンマ、空白、セミコロンは有効な区切り文字です)。

<bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao">
    <property name="manager" ref="manager" />
</bean>

<bean id="manager" class="ManagerBean" />
<bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />
depends-on 属性は、初期化時の依存性と、シングルトン Bean の場合のみ、対応する破棄時の依存性の両方を指定できます。特定の Bean との depends-on 関連を定義する依存 Bean は、特定の Bean 自体が破棄される前に最初に破棄されます。depends-on はシャットダウン順序も制御できます。

1.4.4. 遅延初期化された Bean

デフォルトでは、ApplicationContext 実装は、初期化プロセスの一部としてすべてのシングルトン Bean を熱心に作成および構成します。通常、この事前インスタンス化が望ましいのは、構成または周囲の環境のエラーが数時間または数日後ではなく、すぐに発見されるためです。この動作が望ましくない場合、Bean 定義を遅延初期化済みとしてマークすることにより、シングルトン Bean の事前インスタンス化を防ぐことができます。遅延初期化された Bean は、起動時ではなく、最初にリクエストされたときに Bean インスタンスを作成するように IoC コンテナーに指示します。

XML では、次の例に示すように、この動作は <bean/> 要素の lazy-init 属性によって制御されます。

<bean id="lazy" class="com.something.ExpensiveToCreateBean" lazy-init="true"/>
<bean name="not.lazy" class="com.something.AnotherBean"/>

上記の構成が ApplicationContext によって使用される場合、ApplicationContext の開始時に lazy Bean は事前にインスタンス化されませんが、not.lazy Bean は事前にインスタンス化されます。

ただし、遅延初期化された Bean が遅延初期化されていないシングルトン Bean の依存関係である場合、ApplicationContext はシングルトンの依存関係を満たす必要があるため、起動時に遅延初期化 Bean を作成します。レイジー初期化された Bean は、レイジー初期化されていない他の場所のシングルトン Bean に注入されます。

次の例に示すように、<beans/> 要素の default-lazy-init 属性を使用して、コンテナーレベルで遅延初期化を制御することもできます。

<beans default-lazy-init="true">
    <!-- no beans will be pre-instantiated... -->
</beans>

1.4.5. 共同エディターのオートワイヤー

Spring コンテナーは、コラボレーションする Bean 間の関連をオートワイヤーできます。ApplicationContext の内容をインスペクションすることにより、Spring に Bean のコラボレーター(他の Bean)を自動的に解決させることができます。オートワイヤーには次の利点があります。

  • オートワイヤーにより、プロパティまたはコンストラクター引数を指定する必要性を大幅に減らすことができます。(この点では、この章の他の場所で説明されている Bean テンプレートなどの他のメカニズムも重要です。)

  • オートワイヤーは、オブジェクトの進化に合わせて構成を更新できます。例:クラスに依存関係を追加する必要がある場合、構成を変更する必要なく、その依存関係を自動的に満たすことができます。自動ベース接続は、コードベースがより安定したときに明示的な接続に切り替えるオプションを無効にすることなく、開発中に特に役立ちます。

XML ベースの構成メタデータ(依存性注入を参照)を使用する場合、<bean/> エレメントの autowire 属性を使用して、Bean 定義のオートワイヤーモードを指定できます。オートワイヤー機能には 4 つのモードがあります。Bean ごとにオートワイヤーを指定するため、オートワイヤーするものを選択できます。次の表に、4 つのオートワイヤーモードを示します。

テーブル 2: オートワイヤーモード
モード 説明

no

(デフォルト)オートワイヤーなし。Bean 参照は、ref 要素によって定義する必要があります。共同作業者を明示的に指定すると、制御と明確さが向上するため、大きいデプロイの場合、デフォルト設定を変更することはお勧めしません。ある程度、システムの構造をドキュメント化します。

byName

プロパティ名によるオートワイヤー。Spring は、オートワイヤーが必要なプロパティと同じ名前の Bean を探します。例:Bean 定義が名前によるオートワイヤーに設定され、master プロパティが含まれている(つまり、setMaster(..) メソッドがある)場合、Spring は master という名前の Bean 定義を探し、それを使用してプロパティを設定します。

byType

コンテナーにプロパティタイプの Bean が 1 つだけ存在する場合、プロパティを自動接続します。複数存在する場合、致命的な例外がスローされます。これは、その Bean に対して byType オートワイヤーを使用できないことを示しています。一致する Bean がない場合、何も起こりません(プロパティは設定されません)。

constructor

byType に似ていますが、コンストラクター引数に適用されます。コンテナー内にコンストラクター引数タイプの Bean が 1 つしかない場合、致命的なエラーが発生します。

byType または constructor オートワイヤーモードでは、配列と型付きコレクションを接続できます。このような場合、依存関係を満たすために、予想されるタイプに一致するコンテナー内のすべてのオートワイヤー候補が提供されます。予想されるキータイプが String の場合、強く型付けされた Map インスタンスをオートワイヤーできます。オートワイヤーされた Map インスタンスの値は、予想されるタイプに一致するすべての Bean インスタンスで構成され、Map インスタンスのキーには対応する Bean 名が含まれています。

オートワイヤーの制限と欠点

オートワイヤーは、プロジェクト全体で一貫して使用される場合に最適に機能します。オートワイヤーが一般的に使用されない場合、開発者が 1 つまたは 2 つの Bean 定義のみを接続するためにそれを使用することは混乱を招く可能性があります。

オートワイヤーの制限と欠点を考慮してください。

  • property および constructor-arg 設定の明示的な依存関係は、常にオートワイヤーをオーバーライドします。プリミティブ、StringsClasses などの単純なプロパティ(およびそのような単純なプロパティの配列)をオートワイヤーすることはできません。この制限は仕様によるものです。

  • オートワイヤーは、明示的な接続ほど正確ではありません。ただし、前の表で記述されていたように、Spring は、予期しない結果が生じる可能性のあるあいまいな場合に推測を避けるように注意しています。Spring 管理対象オブジェクト間の関連は、明示的にドキュメント化されなくなりました。

  • Spring コンテナーからドキュメントを生成するツールでは、接続情報を利用できない場合があります。

  • コンテナー内の複数の Bean 定義は、setter メソッドまたはオートワイヤーされるコンストラクター引数で指定されたタイプと一致する場合があります。配列、コレクション、または Map インスタンスの場合、これは必ずしも問題ではありません。ただし、単一の値を期待する依存関係の場合、このあいまいさは勝手に解決されません。一意の Bean 定義が利用できない場合、例外がスローされます。

後者のシナリオでは、いくつかのオプションがあります。

  • 明示的な接続を優先してオートワイヤーを放棄します。

  • 次のセクションで説明されるように、autowire-candidate 属性を false に設定することによって Bean 定義のオートワイヤーを避けてください。

  • <bean/> 要素の primary 属性を true に設定することにより、単一の Bean 定義を 1 次候補として指定します。

  • アノテーションベースのコンテナー構成に従って、アノテーションベースの構成で利用可能な、よりきめ細かい制御を実装します。

オートワイヤーから Bean を除外する

Bean ごとに、Bean をオートワイヤーから除外できます。Spring の XML 形式で、<bean/> 要素の autowire-candidate 属性を false に設定します。コンテナーは、その特定の Bean 定義をオートワイヤーインフラストラクチャー(@Autowired などのアノテーションスタイル構成を含む)で使用できないようにします。

autowire-candidate 属性は、タイプベースのオートワイヤーのみに影響するように設計されています。指定された Bean がオートワイヤー候補としてマークされていない場合でも、名前による明示的な参照には影響しません。結果として、名前によるオートワイヤーは、名前が一致する場合、Bean を注入します。

Bean 名に対するパターンマッチングに基づいて、オートワイヤーの候補を制限することもできます。最上位の <beans/> 要素は、default-autowire-candidates 属性内で 1 つ以上のパターンを受け入れます。例:オートワイヤー候補のステータスを、名前が Repository で終わる Bean に制限するには、値 *Repository を指定します。複数のパターンを提供するには、コンマ区切りリストで定義します。Bean 定義の autowire-candidate 属性の明示的な値 true または false が常に優先されます。このような Bean の場合、パターンマッチングルールは適用されません。

これらの手法は、オートワイヤーによって他の Bean に注入したくない Bean に役立ちます。除外された Bean 自体をオートワイヤーを使用して構成できないという意味ではありません。むしろ、Bean 自体は他の Bean のオートワイヤーの候補ではありません。

1.4.6. メソッドインジェクション

ほとんどのアプリケーションシナリオでは、コンテナー内のほとんどの Bean はシングルトンです。シングルトン Bean が別のシングルトン Bean またはシングルトン以外の Bean が別のシングルトン以外の Bean と連携する必要がある場合、通常、一方の Bean を他方のプロパティとして定義することで依存関係を処理します。Bean ライフサイクルが異なる場合に問題が発生します。シングルトン Bean A は、おそらく A の各メソッド呼び出しで、シングルトンでない(プロトタイプ)Bean B を使用する必要があるとします。コンテナーは、シングルトン Bean A を 1 回だけ作成し、プロパティを設定する機会を 1 回だけ取得します。コンテナーは、Bean A に Bean B の新しいインスタンスが必要になるたびに提供することはできません。

解決策は、制御の反転を停止ことです。ApplicationContextAware インターフェースを実装し、Bean A が必要とするたびに、コンテナーへの getBean("B") 呼び出しで(通常は新しい)Bean B インスタンスを要求することにより、Bean A にコンテナーを認識させることができます。次の例は、このアプローチを示しています。

Java
// a class that uses a stateful Command-style class to perform some processing
package fiona.apple;

// Spring-API imports
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class CommandManager implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    public Object process(Map commandState) {
        // grab a new instance of the appropriate Command
        Command command = createCommand();
        // set the state on the (hopefully brand new) Command instance
        command.setState(commandState);
        return command.execute();
    }

    protected Command createCommand() {
        // notice the Spring API dependency!
        return this.applicationContext.getBean("command", Command.class);
    }

    public void setApplicationContext(
            ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
Kotlin
// a class that uses a stateful Command-style class to perform some processing
package fiona.apple

// Spring-API imports
import org.springframework.context.ApplicationContext
import org.springframework.context.ApplicationContextAware

class CommandManager : ApplicationContextAware {

    private lateinit var applicationContext: ApplicationContext

    fun process(commandState: Map<*, *>): Any {
        // grab a new instance of the appropriate Command
        val command = createCommand()
        // set the state on the (hopefully brand new) Command instance
        command.state = commandState
        return command.execute()
    }

    // notice the Spring API dependency!
    protected fun createCommand() =
            applicationContext.getBean("command", Command::class.java)

    override fun setApplicationContext(applicationContext: ApplicationContext) {
        this.applicationContext = applicationContext
    }
}

ビジネスコードは Spring Framework を認識し、結合しているため、上記は望ましくありません。Spring IoC コンテナーのやや高度な機能であるメソッドインジェクションを使用すると、このユースケースをきれいに処理できます。

このブログエントリ (英語) でメソッドインジェクションの動機について詳しく読むことができます。

ルックアップメソッドインジェクション

ルックアップメソッドインジェクションは、コンテナー管理 Bean のメソッドをオーバーライドし、コンテナー内の別の名前付き Bean のルックアップ結果を返すコンテナーの機能です。前のセクションで説明したシナリオのように、通常、ルックアップにはプロトタイプ Bean が含まれます。Spring Framework は、CGLIB ライブラリからのバイトコード生成を使用してこのメソッドインジェクションを実装し、メソッドをオーバーライドするサブクラスを動的に生成します。

  • この動的なサブクラス化が機能するためには、Spring Bean コンテナーサブクラスが final にすることはできず、オーバーライドするメソッドも final にすることはできません。

  • abstract メソッドを持つクラスを単体テストするには、クラスを自分でサブクラス化し、abstract メソッドのスタブ実装を提供する必要があります。

  • 具体的なメソッドは、コンポーネントのスキャンにも必要です。これには、具象クラスを取得する必要があります。

  • さらに重要な制限は、ルックアップメソッドがファクトリメソッドでは機能せず、特に構成クラスの @Bean メソッドでは機能しないことです。その理由は、この場合、コンテナーがインスタンスの作成を担当しないため、実行時に生成されるサブクラスをその場で作成できないからです。

前のコードスニペットの CommandManager クラスの場合、Spring コンテナーは createCommand() メソッドの実装を動的にオーバーライドします。再加工された例が示すように、CommandManager クラスには Spring 依存関係はありません。

Java
package fiona.apple;

// no more Spring imports!

public abstract class CommandManager {

    public Object process(Object commandState) {
        // grab a new instance of the appropriate Command interface
        Command command = createCommand();
        // set the state on the (hopefully brand new) Command instance
        command.setState(commandState);
        return command.execute();
    }

    // okay... but where is the implementation of this method?
    protected abstract Command createCommand();
}
Kotlin
package fiona.apple

// no more Spring imports!

abstract class CommandManager {

    fun process(commandState: Any): Any {
        // grab a new instance of the appropriate Command interface
        val command = createCommand()
        // set the state on the (hopefully brand new) Command instance
        command.state = commandState
        return command.execute()
    }

    // okay... but where is the implementation of this method?
    protected abstract fun createCommand(): Command
}

注入されるメソッド(この場合は CommandManager)を含むクライアントクラスでは、注入されるメソッドには次の形式の署名が必要です。

<public|protected> [abstract] <return-type> theMethodName(no-arguments);

メソッドが abstract の場合、動的に生成されたサブクラスがメソッドを実装します。それ以外の場合、動的に生成されたサブクラスは、元のクラスで定義された具象メソッドをオーバーライドします。次の例を考えてみましょう。

<!-- a stateful bean deployed as a prototype (non-singleton) -->
<bean id="myCommand" class="fiona.apple.AsyncCommand" scope="prototype">
    <!-- inject dependencies here as required -->
</bean>

<!-- commandProcessor uses statefulCommandHelper -->
<bean id="commandManager" class="fiona.apple.CommandManager">
    <lookup-method name="createCommand" bean="myCommand"/>
</bean>

commandManager として識別された Bean は、myCommand Bean の新しいインスタンスが必要になるたびに、独自の createCommand() メソッドを呼び出します。実際に必要な場合は、myCommand Bean をプロトタイプとしてデプロイするように注意する必要があります。シングルトンの場合、毎回 myCommand Bean の同じインスタンスが返されます。

または、次の例に示すように、アノテーションベースのコンポーネントモデル内で、@Lookup アノテーションを使用してルックアップメソッドを宣言できます。

Java
public abstract class CommandManager {

    public Object process(Object commandState) {
        Command command = createCommand();
        command.setState(commandState);
        return command.execute();
    }

    @Lookup("myCommand")
    protected abstract Command createCommand();
}
Kotlin
abstract class CommandManager {

    fun process(commandState: Any): Any {
        val command = createCommand()
        command.state = commandState
        return command.execute()
    }

    @Lookup("myCommand")
    protected abstract fun createCommand(): Command
}

または、より慣用的に、ターゲット Bean がルックアップメソッドの宣言された戻り型に対して解決されることに依存できます。

Java
public abstract class CommandManager {

    public Object process(Object commandState) {
        MyCommand command = createCommand();
        command.setState(commandState);
        return command.execute();
    }

    @Lookup
    protected abstract MyCommand createCommand();
}
Kotlin
abstract class CommandManager {

    fun process(commandState: Any): Any {
        val command = createCommand()
        command.state = commandState
        return command.execute()
    }

    @Lookup
    protected abstract fun createCommand(): Command
}

通常、抽象クラスがデフォルトで無視される Spring のコンポーネントスキャンルールと互換性を持たせるために、具体的なスタブ実装でこのようなアノテーション付きルックアップメソッドを宣言する必要があります。この制限は、明示的に登録または明示的にインポートされた Bean クラスには適用されません。

別のスコープのターゲット Bean にアクセスする別の方法は、ObjectFactoryProvider インジェクションポイントです。依存関係としてのスコープ Bean を参照してください。

ServiceLocatorFactoryBean (org.springframework.beans.factory.config パッケージ内)が役立つこともあります。

任意のメソッドの置換

ルックアップメソッドインジェクションよりも有用性の低いメソッドインジェクションは、マネージド Bean の任意のメソッドを別のメソッド実装に置き換える機能です。この機能が実際に必要になるまで、このセクションの残りを安全にスキップできます。

XML ベースの構成メタデータを使用すると、replaced-method 要素を使用して、デプロイされた Bean の既存のメソッド実装を別のメソッド実装に置き換えることができます。computeValue というメソッドをオーバーライドする次のクラスを検討してください。

Java
public class MyValueCalculator {

    public String computeValue(String input) {
        // some real code...
    }

    // some other methods...
}
Kotlin
class MyValueCalculator {

    fun computeValue(input: String): String {
        // some real code...
    }

    // some other methods...
}

次の例に示すように、org.springframework.beans.factory.support.MethodReplacer インターフェースを実装するクラスは、新しいメソッド定義を提供します。

Java
/**
 * meant to be used to override the existing computeValue(String)
 * implementation in MyValueCalculator
 */
public class ReplacementComputeValue implements MethodReplacer {

    public Object reimplement(Object o, Method m, Object[] args) throws Throwable {
        // get the input value, work with it, and return a computed result
        String input = (String) args[0];
        ...
        return ...;
    }
}
Kotlin
/**
* meant to be used to override the existing computeValue(String)
* implementation in MyValueCalculator
*/
class ReplacementComputeValue : MethodReplacer {

    override fun reimplement(obj: Any, method: Method, args: Array<out Any>): Any {
        // get the input value, work with it, and return a computed result
        val input = args[0] as String;
        ...
        return ...;
    }
}

元のクラスをデプロイしてメソッドのオーバーライドを指定する Bean 定義は、次の例のようになります。

<bean id="myValueCalculator" class="x.y.z.MyValueCalculator">
    <!-- arbitrary method replacement -->
    <replaced-method name="computeValue" replacer="replacementComputeValue">
        <arg-type>String</arg-type>
    </replaced-method>
</bean>

<bean id="replacementComputeValue" class="a.b.c.ReplacementComputeValue"/>

<replaced-method/> 要素内で 1 つ以上の <arg-type/> 要素を使用して、オーバーライドされるメソッドのメソッドシグネチャーを示すことができます。引数の署名は、メソッドがオーバーロードされ、クラス内に複数のバリアントが存在する場合にのみ必要です。便宜上、引数の型文字列は完全修飾型名の部分文字列である場合があります。例:以下はすべて java.lang.String に一致します:

java.lang.String
String
Str

多くの場合、引数の数はそれぞれの可能な選択肢を区別するのに十分なので、引数タイプに一致する最短の文字列のみを入力できるようにすることで、このショートカットは多くの入力を節約できます。

1.5. Bean スコープ

Bean 定義を作成するとき、その Bean 定義によって定義されたクラスの実際のインスタンスを作成するためのレシピを作成します。Bean 定義がレシピであるという考え方は重要です。これは、クラスと同様に、単一のレシピから多くのオブジェクトインスタンスを作成できることを意味するためです。

特定の Bean 定義から作成されたオブジェクトにプラグインされるさまざまな依存関係と構成値を制御できるだけでなく、特定の Bean 定義から作成されたオブジェクトのスコープも制御できます。このアプローチは強力で柔軟です。なぜなら、Java クラスレベルでオブジェクトのスコープをベイク処理する代わりに、構成を通じて作成するオブジェクトのスコープを選択できるからです。Bean は、いくつかのスコープのいずれかにデプロイされるように定義できます。Spring Framework は 6 つのスコープをサポートしますが、そのうち 4 つは Web 対応の ApplicationContext を使用する場合にのみ使用可能です。カスタムスコープを作成することもできます。

次の表に、サポートされているスコープを示します。

表 3: Bean スコープ
スコープ 説明

singleton

(デフォルト)Spring IoC コンテナーごとに、単一の Bean 定義を単一のオブジェクトインスタンスにスコープします。

prototype

単一の Bean 定義を任意の数のオブジェクトインスタンスにスコープします。

request

単一の Bean 定義を単一の HTTP リクエストのライフサイクルにスコープします。つまり、各 HTTP リクエストには、単一の Bean 定義の背後から作成された Bean の独自のインスタンスがあります。Web 対応 Spring ApplicationContext のコンテキストでのみ有効です。

session

単一の Bean 定義を HTTP Session のライフサイクルにスコープします。Web 対応 Spring ApplicationContext のコンテキストでのみ有効です。

application

単一の Bean 定義を ServletContext のライフサイクルにスコープします。Web 対応 Spring ApplicationContext のコンテキストでのみ有効です。

Web ソケット

単一の Bean 定義を WebSocket のライフサイクルにスコープします。Web 対応 Spring ApplicationContext のコンテキストでのみ有効です。

Spring 3.0, 以降、スレッドスコープは使用可能ですが、デフォルトでは登録されていません。詳細については、SimpleThreadScope (Javadoc) のドキュメントを参照してください。このカスタムスコープまたは他のカスタムスコープを登録する方法については、カスタムスコープの使用を参照してください。

1.5.1. シングルトンスコープ

シングルトン Bean の 1 つの共有インスタンスのみが管理され、その Bean 定義に一致する 1 つ以上の ID を持つ Bean のすべてのリクエストにより、その 1 つの特定の Bean インスタンスが Spring コンテナーによって返されます。

別の言い方をすれば、Bean 定義を定義し、シングルトンとしてスコープされている場合、Spring IoC コンテナーは、その Bean 定義によって定義されたオブジェクトのインスタンスを 1 つだけ作成します。この単一のインスタンスは、そのようなシングルトン Bean のキャッシュに格納され、その名前付き Bean に対する以降のすべてのリクエストと参照は、キャッシュされたオブジェクトを返します。次の図は、シングルトンスコープの仕組みを示しています。

singleton

Spring のシングルトン Bean の概念は、Gang of Four(GoF)パターンブックで定義されているシングルトンパターンとは異なります。GoF シングルトンは、ClassLoader ごとに特定のクラスのインスタンスが 1 つだけ作成されるように、オブジェクトのスコープをハードコードします。Spring シングルトンの範囲は、コンテナーごとおよび Bean ごとと最もよく説明されています。つまり、単一の Spring コンテナー内の特定のクラスに対して 1 つの Bean を定義すると、Spring コンテナーはその Bean 定義によって定義されたクラスのインスタンスを 1 つだけ作成します。シングルトンスコープは、Spring のデフォルトスコープです。Bean を XML のシングルトンとして定義するには、次の例に示すように Bean を定義できます。

<bean id="accountService" class="com.something.DefaultAccountService"/>

<!-- the following is equivalent, though redundant (singleton scope is the default) -->
<bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>

1.5.2. プロトタイプスコープ

Bean デプロイの非シングルトンプロトタイプスコープでは、その特定の Bean のリクエストが行われるたびに、新しい Bean インスタンスが作成されます。つまり、Bean が別の Bean に注入されるか、コンテナーで getBean() メソッド呼び出しを介してリクエストされます。原則として、すべてのステートフル Bean にはプロトタイプスコープを使用し、ステートレス Bean にはシングルトンスコープを使用する必要があります。

次の図は、Spring プロトタイプスコープを示しています。

prototype

(典型的な DAO は会話状態を保持しないため、データアクセスオブジェクト(DAO)は通常、プロトタイプとして構成されません。シングルトンダイアグラムのコアを再利用する方が簡単でした)

次の例では、Bean を XML のプロトタイプとして定義しています。

<bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>

他のスコープとは対照的に、Spring はプロトタイプ Bean の完全なライフサイクルを管理しません。コンテナーは、プロトタイプオブジェクトをインスタンス化、構成、またはその他の方法でアセンブルし、クライアントに渡します。そのプロトタイプインスタンスの記録はありません。初期化ライフサイクルコールバックメソッドは、スコープに関係なくすべてのオブジェクトで呼び出されますが、プロトタイプの場合、構成された破棄ライフサイクルコールバックは呼び出されません。クライアントコードは、プロトタイプスコープのオブジェクトをクリーンアップし、プロトタイプ Bean が保持する高価なリソースを解放する必要があります。Spring コンテナーにプロトタイプスコープ Bean が保持するリソースを解放させるには、クリーンアップが必要な Bean への参照を保持するカスタム Bean ポストプロセッサーを使用してみてください。

いくつかの点で、プロトタイプスコープの Bean に関する Spring コンテナーのロールは、Java new オペレーターに代わるものです。その時点以降のすべてのライフサイクル管理は、クライアントが処理する必要があります。(Spring コンテナー内の Bean のライフサイクルの詳細については、ライフサイクルコールバックを参照してください。)

1.5.3. プロトタイプ Bean 依存関係を持つシングルトン Bean

プロトタイプ Bean に依存するシングルトンスコープ Bean を使用する場合、インスタンス化時に依存関係が解決されることに注意してください。プロトタイプスコープの Bean をシングルトンスコープの Bean に依存性注入すると、新しいプロトタイプ Bean がインスタンス化され、シングルトン Bean に依存性注入されます。プロトタイプインスタンスは、シングルトンスコープの Bean に提供される唯一のインスタンスです。

ただし、シングルトンスコープの Bean が、プロトタイプスコープの Bean の新しいインスタンスを実行時に繰り返し取得するとします。プロトタイプスコープの Bean をシングルトン Bean に依存性注入することはできません。その注入は、Spring コンテナーがシングルトン Bean をインスタンス化し、依存性を解決および注入するときに 1 回しか発生しないためです。プロトタイプ Bean の新しいインスタンスが実行時に複数回必要な場合は、メソッドインジェクションを参照してください

1.5.4. リクエスト、セッション、アプリケーション、WebSocket スコープ

requestsessionapplication および websocket スコープは、Web 対応の Spring ApplicationContext 実装(XmlWebApplicationContext など)を使用する場合にのみ使用可能です。これらのスコープを ClassPathXmlApplicationContext などの通常の Spring IoC コンテナーで使用すると、不明な Bean スコープについて文句を言う IllegalStateException がスローされます。

Web の初期設定

requestsessionapplication および websocket レベル(Web スコープの Bean)で Bean のスコープをサポートするには、Bean を定義する前にいくつかのマイナーな初期構成が必要です。(この初期セットアップは、標準スコープ singleton および prototype には必要ありません。)

この初期設定を達成する方法は、特定のサーブレット環境によって異なります。

Spring Web MVC 内の Spring DispatcherServlet によって処理されるリクエスト内で、スコープ付き Bean にアクセスする場合、特別な設定は必要ありません。DispatcherServlet は、関連するすべての状態をすでに公開しています。

Spring の DispatcherServlet の外部で処理されるリクエストで Servlet 2.5 Web コンテナーを使用する場合(たとえば、JSF または Struts を使用する場合)、org.springframework.web.context.request.RequestContextListenerServletRequestListener を登録する必要があります。Servlet 3.0+ の場合、これは WebApplicationInitializer インターフェースを使用してプログラムで実行できます。または、古いコンテナーの場合、次の宣言を Web アプリケーションの web.xml ファイルに追加します。

<web-app>
    ...
    <listener>
        <listener-class>
            org.springframework.web.context.request.RequestContextListener
        </listener-class>
    </listener>
    ...
</web-app>

または、リスナーの設定に課題がある場合は、Spring の RequestContextFilter の使用を検討してください。フィルターマッピングは、周囲の Web アプリケーションの構成に依存するため、必要に応じて変更する必要があります。次のリストは、Web アプリケーションのフィルター部分を示しています。

<web-app>
    ...
    <filter>
        <filter-name>requestContextFilter</filter-name>
        <filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>requestContextFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    ...
</web-app>

DispatcherServletRequestContextListener および RequestContextFilter はすべてまったく同じことを行います。つまり、HTTP リクエストオブジェクトを、そのリクエストを処理している Thread にバインドします。これにより、リクエストスコープおよびセッションスコープの Bean が、コールチェーンのさらに先で利用可能になります。

リクエストスコープ

Bean 定義の次の XML 構成を検討してください。

<bean id="loginAction" class="com.something.LoginAction" scope="request"/>

Spring コンテナーは、すべての HTTP リクエストの loginAction Bean 定義を使用して、LoginAction Bean の新しいインスタンスを作成します。つまり、loginAction Bean は、HTTP リクエストレベルでスコープされます。同じ loginAction Bean 定義から作成された他のインスタンスはこれらの状態の変化を認識しないため、作成されたインスタンスの内部状態を必要なだけ変更できます。これらは個々のリクエストに固有のものです。リクエストの処理が完了すると、リクエストのスコープにある Bean は破棄されます。

アノテーション駆動型コンポーネントまたは Java 構成を使用する場合、@RequestScope アノテーションを使用して、コンポーネントを request スコープに割り当てることができます。次の例は、その方法を示しています。

Java
@RequestScope
@Component
public class LoginAction {
    // ...
}
Kotlin
@RequestScope
@Component
class LoginAction {
    // ...
}
セッションスコープ

Bean 定義の次の XML 構成を検討してください。

<bean id="userPreferences" class="com.something.UserPreferences" scope="session"/>

Spring コンテナーは、単一の HTTP Session の存続期間に userPreferences Bean 定義を使用して、UserPreferences Bean の新しいインスタンスを作成します。言い換えると、userPreferences Bean は HTTP Session レベルで効果的にスコープされます。リクエストスコープ Bean と同様に、同じ userPreferences Bean 定義から作成されたインスタンスも使用している他の HTTP Session インスタンスはこれらの状態の変化を認識しないため、作成されるインスタンスの内部状態を必要なだけ変更できます。なぜなら、それらは個々の HTTP Session に特有です。HTTP Session が最終的に破棄されると、その特定の HTTP Session にスコープされた Bean も破棄されます。

アノテーション駆動型コンポーネントまたは Java 構成を使用する場合、@SessionScope アノテーションを使用して、コンポーネントを session スコープに割り当てることができます。

Java
@SessionScope
@Component
public class UserPreferences {
    // ...
}
Kotlin
@SessionScope
@Component
class UserPreferences {
    // ...
}
アプリケーションスコープ

Bean 定義の次の XML 構成を検討してください。

<bean id="appPreferences" class="com.something.AppPreferences" scope="application"/>

Spring コンテナーは、Web アプリケーション全体に対して appPreferences Bean 定義を 1 回使用して、AppPreferences Bean の新しいインスタンスを作成します。つまり、appPreferences Bean は ServletContext レベルでスコープされ、通常の ServletContext 属性として保存されます。これは Spring シングルトン Bean に多少似ていますが、2 つの重要な点で異なります:Spring「ApplicationContext」(特定の Web アプリケーションには複数ある場合があります)ごとではなく、ServletContext ごとにシングルトンであり、実際に公開されているため、ServletContext 属性として表示されます。

アノテーション駆動型コンポーネントまたは Java 構成を使用する場合、@ApplicationScope アノテーションを使用して、コンポーネントを application スコープに割り当てることができます。次の例は、その方法を示しています。

Java
@ApplicationScope
@Component
public class AppPreferences {
    // ...
}
Kotlin
@ApplicationScope
@Component
class AppPreferences {
    // ...
}
依存関係としてのスコープ Bean

Spring IoC コンテナーは、オブジェクト(Bean)のインスタンス化だけでなく、共同作業者(または依存関係)の接続も管理します。(たとえば)HTTP リクエストスコープの Bean を、より寿命の長いスコープの別の Bean に注入する場合、スコープ付き Bean の代わりに AOP プロキシを注入することを選択できます。つまり、スコープオブジェクトと同じパブリックインターフェースを公開するプロキシオブジェクトを挿入する必要がありますが、関連するスコープ(HTTP リクエストなど)から実際のターゲットオブジェクトを取得し、メソッド呼び出しを実際のオブジェクトに委譲することもできます。

singleton をスコープとする Bean 間で <aop:scoped-proxy/> を使用することもできます。その場合、参照はシリアライズ可能な中間プロキシを通過するため、デシリアライズ時にターゲットシングルトン Bean を再取得できます。

<aop:scoped-proxy/> をスコープ prototype の Bean に対して宣言すると、共有プロキシでのすべてのメソッド呼び出しは、呼び出しが転送される新しいターゲットインスタンスの作成につながります。

また、スコーププロキシは、より短いスコープから Bean にライフサイクルセーフな方法でアクセスする唯一の方法ではありません。また、インジェクションポイント (つまり、コンストラクターまたは setter 引数、あるいは自動フィールドです) を ObjectFactory<MyTargetBean> として宣言することで、getObject() 呼び出しが必要になるたびに、インスタンスを保持したり個別に格納したりすることなく、現在のインスタンスをオンデマンドで取得できるようにすることもできます。

拡張バリアントとして、ObjectProvider<MyTargetBean> を宣言できます。これは、getIfAvailable や getIfUnique など、いくつかの追加のアクセスバリアントを提供します。

これの JSR-330 バリアントは Provider と呼ばれ、Provider<MyTargetBean> 宣言と、検索の試行ごとに対応する get() 呼び出しで使用されます。JSR-330 全体の詳細については、こちらを参照してください。

次の例の構成は 1 行のみですが、その背後にある「理由」と「方法」を理解することが重要です。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- an HTTP Session-scoped bean exposed as a proxy -->
    <bean id="userPreferences" class="com.something.UserPreferences" scope="session">
        <!-- instructs the container to proxy the surrounding bean -->
        <aop:scoped-proxy/> (1)
    </bean>

    <!-- a singleton-scoped bean injected with a proxy to the above bean -->
    <bean id="userService" class="com.something.SimpleUserService">
        <!-- a reference to the proxied userPreferences bean -->
        <property name="userPreferences" ref="userPreferences"/>
    </bean>
</beans>
1 プロキシを定義する行。

このようなプロキシを作成するには、子の <aop:scoped-proxy/> 要素をスコープ付き Bean 定義に挿入します(作成するプロキシのタイプの選択および XML スキーマベースの構成を参照)。requestsession およびカスタムスコープレベルでスコープされた Bean の定義に <aop:scoped-proxy/> 要素が必要なのはなぜですか?次のシングルトン Bean 定義を検討し、前述のスコープに対して定義する必要があるものと比較してください(以下の userPreferences Bean 定義は不完全であることに注意してください)。

<bean id="userPreferences" class="com.something.UserPreferences" scope="session"/>

<bean id="userManager" class="com.something.UserManager">
    <property name="userPreferences" ref="userPreferences"/>
</bean>

上記の例では、シングルトン Bean(userManager)に HTTP Session -scoped Bean(userPreferences)への参照が挿入されています。ここでの顕著な点は、userManager Bean はシングルトンであるということです。コンテナーごとに 1 回だけインスタンス化され、その依存関係(この場合は 1 つのみ、userPreferences Bean)も 1 回だけ注入されます。これは、userManager Bean がまったく同じ userPreferences オブジェクト(つまり、最初に注入されたオブジェクト)でのみ動作することを意味します。

これは、寿命の短いスコープ Bean を寿命の長いスコープ Bean に注入するときの動作ではありません(たとえば、Bean を依存関係としてシングルトン Bean にコラボレーションする HTTP Session -scoped を注入します)。むしろ、単一の userManager オブジェクトが必要であり、HTTP Session の存続期間中、HTTP Session に固有の userPreferences オブジェクトが必要です。コンテナーは、UserPreferences クラス(理想的には UserPreferences インスタンスであるオブジェクト)とまったく同じパブリックインターフェースを公開するオブジェクトを作成します。オブジェクトは、スコープメカニズム(HTTP リクエスト、Session など)から実際の UserPreferences オブジェクトをフェッチできます。コンテナーは、このプロキシオブジェクトを userManager Bean に注入します。これは、この UserPreferences 参照がプロキシであることを認識していません。この例では、UserManager インスタンスが依存関係が注入された UserPreferences オブジェクトのメソッドを呼び出すとき、実際にはプロキシのメソッドを呼び出しています。次に、プロキシは(この場合)HTTP Session から実際の UserPreferences オブジェクトをフェッチし、取得した実際の UserPreferences オブジェクトにメソッド呼び出しを委譲します。

次の例に示すように、request- Bean および session-scoped Bean をコラボレーションオブジェクトに注入する場合は、次の(正しい完全な)構成が必要です。

<bean id="userPreferences" class="com.something.UserPreferences" scope="session">
    <aop:scoped-proxy/>
</bean>

<bean id="userManager" class="com.something.UserManager">
    <property name="userPreferences" ref="userPreferences"/>
</bean>
作成するプロキシのタイプの選択

デフォルトでは、Spring コンテナーが <aop:scoped-proxy/> 要素でマークアップされた Bean のプロキシを作成すると、CGLIB ベースのクラスプロキシが作成されます。

CGLIB プロキシは、public メソッド呼び出しのみをインターセプトします!そのようなプロキシで非 public メソッドを呼び出さないでください。これらは、実際のスコープターゲットオブジェクトに委譲されません。

または、<aop:scoped-proxy/> 要素の proxy-target-class 属性の値に false を指定することにより、Spring コンテナーを設定して、そのようなスコープ Bean の標準 JDK インターフェースベースのプロキシを作成できます。JDK インターフェースベースのプロキシを使用すると、そのようなプロキシに影響を与えるためにアプリケーションクラスパスに追加のライブラリを必要としないことを意味します。ただし、スコープ付き Bean のクラスは少なくとも 1 つのインターフェースを実装する必要があり、スコープ付き Bean が挿入されるすべてのコラボレーターは、そのインターフェースの 1 つを介して Bean を参照する必要があります。次の例は、インターフェースに基づいたプロキシを示しています。

<!-- DefaultUserPreferences implements the UserPreferences interface -->
<bean id="userPreferences" class="com.stuff.DefaultUserPreferences" scope="session">
    <aop:scoped-proxy proxy-target-class="false"/>
</bean>

<bean id="userManager" class="com.stuff.UserManager">
    <property name="userPreferences" ref="userPreferences"/>
</bean>

クラスベースまたはインターフェースベースのプロキシの選択の詳細については、プロキシメカニズムを参照してください。

1.5.5. カスタムスコープ

Bean スコーピングメカニズムは拡張可能です。独自のスコープを定義することも、既存のスコープを再定義することもできますが、後者は悪い習慣と見なされ、組み込みの singleton および prototype スコープをオーバーライドすることはできません。

カスタムスコープの作成

カスタムスコープを Spring コンテナーに統合するには、このセクションで説明する org.springframework.beans.factory.config.Scope インターフェースを実装する必要があります。独自のスコープを実装する方法のアイデアについては、Spring Framework 自体と Scope (Javadoc) javadoc で提供される Scope 実装を参照してください。これにより、実装する必要があるメソッドが詳細に説明されます。

Scope インターフェースには、スコープからオブジェクトを取得し、スコープから削除し、破棄するための 4 つのメソッドがあります。

たとえば、セッションスコープの実装は、セッションスコープの Bean を返します(存在しない場合、メソッドは、Bean の新しいインスタンスを、将来の参照のためにセッションにバインドした後に返します)。次のメソッドは、基になるスコープからオブジェクトを返します。

Java
Object get(String name, ObjectFactory<?> objectFactory)
Kotlin
fun get(name: String, objectFactory: ObjectFactory<*>): Any

たとえば、セッションスコープの実装は、基になるセッションからセッションスコープの Bean を削除します。オブジェクトが返されますが、指定された名前のオブジェクトが見つからない場合は null を返すことができます。次のメソッドは、基になるスコープからオブジェクトを削除します。

Java
Object remove(String name)
Kotlin
fun remove(name: String): Any

次のメソッドは、スコープが破棄されたとき、またはスコープ内の指定されたオブジェクトが破棄されたときにスコープが呼び出すコールバックを登録します。

Java
void registerDestructionCallback(String name, Runnable destructionCallback)
Kotlin
fun registerDestructionCallback(name: String, destructionCallback: Runnable)

破棄コールバックの詳細については、javadoc または Spring スコープの実装を参照してください。

次のメソッドは、基になるスコープの会話識別子を取得します。

Java
String getConversationId()
Kotlin
fun getConversationId(): String

この識別子はスコープごとに異なります。セッションスコープの実装の場合、この識別子はセッション識別子にすることができます。

カスタムスコープの使用

1 つ以上のカスタム Scope 実装を作成してテストした後、Spring コンテナーに新しいスコープを認識させる必要があります。以下の方法は、新しい Scope を Spring コンテナーに登録する中心的な方法です。

Java
void registerScope(String scopeName, Scope scope);
Kotlin
fun registerScope(scopeName: String, scope: Scope)

このメソッドは、Spring に同梱されているほとんどの具体的な ApplicationContext 実装の BeanFactory プロパティを介して利用可能な ConfigurableBeanFactory インターフェースで宣言されています。

registerScope(..) メソッドの最初の引数は、スコープに関連付けられた一意の名前です。Spring コンテナー自体のこのような名前の例は、singleton および prototype です。registerScope(..) メソッドの 2 番目の引数は、登録して使用するカスタム Scope 実装の実際のインスタンスです。

カスタム Scope 実装を作成し、次の例に示すように登録するとします。

次の例では、Spring に含まれていますが、デフォルトでは登録されていない SimpleThreadScope を使用します。手順は、独自のカスタム Scope 実装でも同じです。
Java
Scope threadScope = new SimpleThreadScope();
beanFactory.registerScope("thread", threadScope);
Kotlin
val threadScope = SimpleThreadScope()
beanFactory.registerScope("thread", threadScope)

その後、次のように、カスタム Scope のスコープ規則に準拠する Bean 定義を作成できます。

<bean id="..." class="..." scope="thread">

カスタム Scope 実装を使用すると、スコープのプログラムによる登録に限定されません。次の例に示すように、CustomScopeConfigurer クラスを使用して、Scope の登録を宣言的に行うこともできます。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
        <property name="scopes">
            <map>
                <entry key="thread">
                    <bean class="org.springframework.context.support.SimpleThreadScope"/>
                </entry>
            </map>
        </property>
    </bean>

    <bean id="thing2" class="x.y.Thing2" scope="thread">
        <property name="name" value="Rick"/>
        <aop:scoped-proxy/>
    </bean>

    <bean id="thing1" class="x.y.Thing1">
        <property name="thing2" ref="thing2"/>
    </bean>

</beans>
<aop:scoped-proxy/> を FactoryBean 実装に配置する場合、スコープは getObject() から返されるオブジェクトではなく、ファクトリ Bean 自体です。

1.6. Bean の性質のカスタマイズ

Spring Framework は、Bean の性質をカスタマイズするために使用できる多くのインターフェースを提供します。このセクションでは、次のようにグループ化します。

1.6.1. ライフサイクルコールバック

Bean ライフサイクルのコンテナーの管理とやり取りするために、Spring InitializingBean および DisposableBean インターフェースを実装できます。コンテナーは、前者の場合は afterPropertiesSet() を、後者の場合は destroy() を呼び出して、Bean の初期化および破棄時に Bean が特定のアクションを実行できるようにします。

JSR-250 @PostConstruct および @PreDestroy アノテーションは、一般に、最新の Spring アプリケーションでライフサイクルコールバックを受信するためのベストプラクティスと見なされています。これらのアノテーションを使用すると、Bean は Spring 固有のインターフェースに結合されません。詳細については、@PostConstruct および @PreDestroy の使用を参照してください。

JSR-250 アノテーションを使用したくないが、それでもカップリングを除去したい場合は、init-method および destroy-method Bean 定義メタデータを検討してください。

内部的に、Spring Framework は BeanPostProcessor 実装を使用して、適切なメソッドを見つけて呼び出すことができるコールバックインターフェースを処理します。カスタム機能またはその他のライフサイクル動作が必要な場合、Spring はデフォルトでは提供していませんが、BeanPostProcessor を自分で実装できます。詳細については、コンテナー拡張ポイントを参照してください。

初期化および破棄のコールバックに加えて、Spring 管理オブジェクトは Lifecycle インターフェースを実装することもできます。これにより、これらのオブジェクトは、コンテナー自体のライフサイクルによって駆動される起動およびシャットダウンプロセスに参加できます。

このセクションでは、ライフサイクルコールバックインターフェースについて説明します。

初期化コールバック

org.springframework.beans.factory.InitializingBean インターフェースにより、Bean は、コンテナーが Bean で必要なすべてのプロパティを設定した後に初期化作業を実行できます。InitializingBean インターフェースは単一のメソッドを指定します:

Java
void afterPropertiesSet() throws Exception;
Kotlin
fun afterPropertiesSet()

InitializingBean インターフェースはコードを Spring に不必要に結合するため、使用しないことをお勧めします。または、@PostConstruct アノテーションを使用するか、POJO 初期化メソッドを指定することをお勧めします。XML ベースの構成メタデータの場合、init-method 属性を使用して、引数なしの void 署名を持つメソッドの名前を指定できます。Java 構成では、@Bean の initMethod 属性を使用できます。ライフサイクルコールバックの受信を参照してください。次の例を考えてみましょう。

<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
Java
public class ExampleBean {

    public void init() {
        // do some initialization work
    }
}
Kotlin
class ExampleBean {

    fun init() {
        // do some initialization work
    }
}

上記の例は、次の例(2 つのリストで構成されています)とほぼ同じ効果があります。

<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
Java
public class AnotherExampleBean implements InitializingBean {

    @Override
    public void afterPropertiesSet() {
        // do some initialization work
    }
}
Kotlin
class AnotherExampleBean : InitializingBean {

    override fun afterPropertiesSet() {
        // do some initialization work
    }
}

ただし、前述の 2 つの例の最初の例では、コードを Spring に結合していません。

破棄コールバック

org.springframework.beans.factory.DisposableBean インターフェースを実装すると、Bean を含むコンテナーが破棄されたときに Bean がコールバックを取得できます。DisposableBean インターフェースは単一のメソッドを指定します:

Java
void destroy() throws Exception;
Kotlin
fun destroy()

DisposableBean コールバックインターフェースは、コードを Spring に不必要に結合するため、使用しないことをお勧めします。または、@PreDestroy アノテーションを使用するか、Bean 定義でサポートされている一般的なメソッドを指定することをお勧めします。XML ベースの構成メタデータを使用すると、<bean/> の destroy-method 属性を使用できます。Java 構成では、@Bean の destroyMethod 属性を使用できます。ライフサイクルコールバックの受信を参照してください。次の定義を考慮してください。

<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>
Java
public class ExampleBean {

    public void cleanup() {
        // do some destruction work (like releasing pooled connections)
    }
}
Kotlin
class ExampleBean {

    fun cleanup() {
        // do some destruction work (like releasing pooled connections)
    }
}

上記の定義は、次の定義とほぼ同じ効果があります。

<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
Java
public class AnotherExampleBean implements DisposableBean {

    @Override
    public void destroy() {
        // do some destruction work (like releasing pooled connections)
    }
}
Kotlin
class AnotherExampleBean : DisposableBean {

    override fun destroy() {
        // do some destruction work (like releasing pooled connections)
    }
}

ただし、前述の 2 つの定義の最初のものは、コードを Spring に結合しません。

<bean> 要素の destroy-method 属性に特別な (inferred) 値を割り当てることができます。これにより、特定の Bean クラスでパブリック close または shutdown メソッドを自動的に検出するように Spring に指示します。(したがって、java.lang.AutoCloseable または java.io.Closeable を実装するクラスはすべて一致します) <beans> 要素の default-destroy-method 属性にこの特別な (inferred) 値を設定して、この動作を Bean のセット全体に適用することもできます(デフォルトの初期化および破棄メソッドを参照)。これは Java 構成のデフォルトの動作であることに注意してください。
デフォルトの初期化および破棄メソッド

初期化を記述し、Spring 固有の InitializingBean および DisposableBean コールバックインターフェースを使用しないメソッドコールバックを破棄する場合、通常は init()initialize()dispose() などの名前のメソッドを記述します。理想的には、このようなライフサイクルコールバックメソッドの名前はプロジェクト全体で標準化され、すべての開発者が同じメソッド名を使用して一貫性を確保できるようにします。

Spring コンテナーを設定して、名前付きの初期化を「検索」し、すべての Bean でコールバックメソッド名を破棄できます。つまり、アプリケーション開発者は、各 Bean 定義で init-method="init" 属性を構成することなく、アプリケーションクラスを記述し、init() という初期化コールバックを使用できます。方法 Bean を作成(及び標準ライフサイクルコールバック契約に従っていること Spring IoC コンテナー呼び出し先に記載)。また、この機能は、初期化およびメソッドコールバックの破棄に一貫した命名規則を適用します。

初期化コールバックメソッドの名前が init() で、破棄コールバックメソッドの名前が destroy() であるとします。クラスは、次の例のクラスに似ています。

Java
public class DefaultBlogService implements BlogService {

    private BlogDao blogDao;

    public void setBlogDao(BlogDao blogDao) {
        this.blogDao = blogDao;
    }

    // this is (unsurprisingly) the initialization callback method
    public void init() {
        if (this.blogDao == null) {
            throw new IllegalStateException("The [blogDao] property must be set.");
        }
    }
}
Kotlin
class DefaultBlogService : BlogService {

    private var blogDao: BlogDao? = null

    // this is (unsurprisingly) the initialization callback method
    fun init() {
        if (blogDao == null) {
            throw IllegalStateException("The [blogDao] property must be set.")
        }
    }
}

次に、そのクラスを次のような Bean で使用できます。

<beans default-init-method="init">

    <bean id="blogService" class="com.something.DefaultBlogService">
        <property name="blogDao" ref="blogDao" />
    </bean>

</beans>

最上位の <beans/> 要素属性に default-init-method 属性が存在すると、Spring IoC コンテナーは、Bean クラスの init と呼ばれるメソッドを初期化メソッドコールバックとして認識します。Bean が作成およびアセンブルされるときに、Bean クラスにそのようなメソッドがある場合、適切なタイミングで呼び出されます。

最上位の <beans/> 要素で default-destroy-method 属性を使用することで、同様に(つまり XML で)destroy メソッドコールバックを構成できます。

既存の Bean クラスには、慣例とは異なる名前のコールバックメソッドが既に存在する場合、<bean/> 自体の init-method および destroy-method 属性を使用してメソッド名を(XML で)指定することにより、デフォルトをオーバーライドできます。

Spring コンテナーは、Bean にすべての依存関係が提供された直後に、構成された初期化コールバックが呼び出されることを保証します。初期化コールバックは生の Bean 参照で呼び出されます。これは、AOP インターセプターなどがまだ Bean に適用されていないことを意味します。ターゲット Bean が最初に完全に作成され、次にインターセプターチェーンを備えた AOP プロキシ(たとえば)が適用されます。ターゲット Bean とプロキシが別々に定義されている場合、コードはプロキシをバイパスして生のターゲット Bean と対話することさえできます。インターセプターを init メソッドに適用することは一貫性がありません。これを行うと、ターゲット Bean のライフサイクルがそのプロキシまたはインターセプターに結合され、コードが生のターゲット Bean と直接対話するときに奇妙なセマンティクスが残るためです。

ライフサイクルメカニズムの組み合わせ

Spring 2.5, の時点で、Bean ライフサイクルの動作を制御するための 3 つのオプションがあります。

Bean に対して複数のライフサイクルメカニズムが構成されており、各メカニズムが異なるメソッド名で構成されている場合、構成された各メソッドは、この注記の後にリストされている順序で実行されます。ただし、同じメソッド名(たとえば、初期化メソッドの init())がこれらのライフサイクルメカニズムの複数に対して構成されている場合、前のセクションで説明したように、そのメソッドは 1 回実行されます。

同じ Bean に対して、異なる初期化方法で構成された複数のライフサイクルメカニズムは、次のように呼び出されます。

  1. @PostConstruct アノテーションが付けられたメソッド

  2. InitializingBean コールバックインターフェースによって定義された afterPropertiesSet() 

  3. カスタム構成の init() メソッド

Destroy メソッドは同じ順序で呼び出されます:

  1. @PreDestroy アノテーションが付けられたメソッド

  2. DisposableBean コールバックインターフェースによって定義された destroy() 

  3. カスタム構成の destroy() メソッド

起動とシャットダウンのコールバック

Lifecycle インターフェースは、独自のライフサイクル要件を持つオブジェクト(バックグラウンドプロセスの開始や停止など)に不可欠なメソッドを定義します。

Java
public interface Lifecycle {

    void start();

    void stop();

    boolean isRunning();
}
Kotlin
interface Lifecycle {

    fun start()

    fun stop()

    val isRunning: Boolean
}

Spring で管理されるオブジェクトは、Lifecycle インターフェースを実装できます。次に、ApplicationContext 自体が(たとえば、実行時の停止 / 再起動シナリオのために)開始および停止信号を受信すると、それらの呼び出しをそのコンテキスト内で定義されたすべての Lifecycle 実装にカスケードします。これを行うには、次のリストに示す LifecycleProcessor に委譲します。

Java
public interface LifecycleProcessor extends Lifecycle {

    void onRefresh();

    void onClose();
}
Kotlin
interface LifecycleProcessor : Lifecycle {

    fun onRefresh()

    fun onClose()
}

LifecycleProcessor 自体が Lifecycle インターフェースの拡張であることに注意してください。また、リフレッシュおよび閉じられるコンテキストに反応するための 2 つの他のメソッドを追加します。

通常の org.springframework.context.Lifecycle インターフェースは、明示的な開始および停止通知の単純な契約であり、コンテキストのリフレッシュ時の自動起動を意味しないことに注意してください。特定の Bean の自動起動(起動フェーズを含む)をきめ細かく制御するには、代わりに org.springframework.context.SmartLifecycle の実装を検討してください。

また、停止通知が破棄される前に送信されるとは限りません。通常のシャットダウンでは、すべての Lifecycle Bean が最初に停止通知を受信してから、一般的な破棄コールバックが伝達されます。ただし、コンテキストの有効期間中のホットリフレッシュ時、またはリフレッシュの試行が停止したときは、destroy メソッドのみが呼び出されます。

起動とシャットダウンの呼び出しの順序は重要です。2 つのオブジェクト間に「依存」関連が存在する場合、依存側は依存関連の後に開始し、依存関連の前に停止します。ただし、直接的な依存関連が不明な場合があります。特定のタイプのオブジェクトは、別のタイプのオブジェクトよりも先に開始する必要があることを知っているかもしれません。そのような場合、SmartLifecycle インターフェースは別のオプション、つまりスーパーインターフェース Phased で定義されている getPhase() メソッドを定義します。次のリストは、Phased インターフェースの定義を示しています。

Java
public interface Phased {

    int getPhase();
}
Kotlin
interface Phased {

    val phase: Int
}

次のリストは、SmartLifecycle インターフェースの定義を示しています。

Java
public interface SmartLifecycle extends Lifecycle, Phased {

    boolean isAutoStartup();

    void stop(Runnable callback);
}
Kotlin
interface SmartLifecycle : Lifecycle, Phased {

    val isAutoStartup: Boolean

    fun stop(callback: Runnable)
}

開始時に、最も低いフェーズのオブジェクトが最初に開始されます。停止するときは、逆の順序に従います。SmartLifecycle を実装し、getPhase() メソッドが Integer.MIN_VALUE を返すオブジェクトは、最初に開始し、最後に停止するオブジェクトになります。スペクトルのもう一方の端では、Integer.MAX_VALUE の位相値は、オブジェクトが最後に開始され、最初に停止されることを示します(実行されている他のプロセスに依存するため)。位相値を検討する場合、SmartLifecycle を実装しない「通常の」 Lifecycle オブジェクトのデフォルトの位相が 0 であることを知ることも重要です。負の位相値は、オブジェクトがそれらの標準コンポーネントの前に開始する(およびその後に停止する)ことを示します。正の位相値の場合、逆のことが言えます。

SmartLifecycle によって定義された停止メソッドは、コールバックを受け入れます。実装は、その実装のシャットダウンプロセスが完了した後に、そのコールバックの run() メソッドを呼び出す必要があります。LifecycleProcessor インターフェースのデフォルト実装である DefaultLifecycleProcessor は、各フェーズ内のオブジェクトのグループのタイムアウト値まで待機してコールバックを呼び出すため、必要に応じて非同期シャットダウンが可能になります。デフォルトのフェーズごとのタイムアウトは 30 秒です。コンテキスト内で lifecycleProcessor という名前の Bean を定義することにより、デフォルトのライフサイクルプロセッサインスタンスをオーバーライドできます。タイムアウトのみを変更する場合は、次を定義するだけで十分です。

<bean id="lifecycleProcessor" class="org.springframework.context.support.DefaultLifecycleProcessor">
    <!-- timeout value in milliseconds -->
    <property name="timeoutPerShutdownPhase" value="10000"/>
</bean>

前に記述されていたように、LifecycleProcessor インターフェースは、コンテキストのリフレッシュとクローズのコールバックメソッドも定義します。後者は、stop() が明示的に呼び出されたかのようにシャットダウンプロセスを駆動しますが、コンテキストが閉じるときに発生します。一方、「refresh」コールバックは、SmartLifecycle Bean の別の機能を有効にします。コンテキストがリフレッシュされると(すべてのオブジェクトがインスタンス化および初期化された後)、そのコールバックが呼び出されます。その時点で、デフォルトのライフサイクルプロセッサは、各 SmartLifecycle オブジェクトの isAutoStartup() メソッドによって返されるブール値をチェックします。true の場合、そのオブジェクトはコンテキストまたは独自の start() メソッドの明示的な呼び出しを待つのではなく、その時点で開始されます(コンテキストのリフレッシュとは異なり、コンテキスト開始は標準コンテキスト実装では自動的に行われません)。phase 値と「依存」関連により、前述のように起動順序が決まります。

非 Web アプリケーションで Spring IoC コンテナーを正常にシャットダウンする

このセクションは、非 Web アプリケーションにのみ適用されます。Spring の Web ベースの ApplicationContext 実装には、関連する Web アプリケーションのシャットダウン時に Spring IoC コンテナーを正常にシャットダウンするためのコードが既に用意されています。

Spring の IoC コンテナーを非 Web アプリケーション環境(たとえば、リッチクライアントデスクトップ環境)で使用する場合、シャットダウンフックを JVM に登録します。これにより、正常なシャットダウンが保証され、シングルトン Bean の関連する destroy メソッドが呼び出され、すべてのリソースが解放されます。これらの破棄コールバックを正しく構成および実装する必要があります。

シャットダウンフックを登録するには、次の例に示すように、ConfigurableApplicationContext インターフェースで宣言されている registerShutdownHook() メソッドを呼び出します。

Java
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public final class Boot {

    public static void main(final String[] args) throws Exception {
        ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");

        // add a shutdown hook for the above context...
        ctx.registerShutdownHook();

        // app runs here...

        // main method exits, hook is called prior to the app shutting down...
    }
}
Kotlin
import org.springframework.context.support.ClassPathXmlApplicationContext

fun main() {
    val ctx = ClassPathXmlApplicationContext("beans.xml")

    // add a shutdown hook for the above context...
    ctx.registerShutdownHook()

    // app runs here...

    // main method exits, hook is called prior to the app shutting down...
}

1.6.2. ApplicationContextAware および BeanNameAware

ApplicationContext が org.springframework.context.ApplicationContextAware インターフェースを実装するオブジェクトインスタンスを作成すると、インスタンスにはその ApplicationContext への参照が提供されます。次のリストは、ApplicationContextAware インターフェースの定義を示しています。

Java
public interface ApplicationContextAware {

    void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}
Kotlin
interface ApplicationContextAware {

    @Throws(BeansException::class)
    fun setApplicationContext(applicationContext: ApplicationContext)
}

Bean は、ApplicationContext インターフェースを介して、またはこのインターフェースの既知のサブクラス(追加機能を公開する ConfigurableApplicationContext など)への参照をキャストすることにより、作成した ApplicationContext をプログラムで操作できます。1 つの用途は、他の Bean のプログラムによる取得です。この機能が役立つ場合があります。ただし、コードを Spring に結合し、コラボレーターがプロパティとして Bean に提供される Inversion of Control スタイルには従わないため、通常は回避する必要があります。ApplicationContext の他のメソッドは、ファイルリソースへのアクセス、アプリケーションイベントの公開、および MessageSource へのアクセスを提供します。これらの追加機能については、ApplicationContext の追加機能で説明しています。

オートワイヤーは、ApplicationContext への参照を取得する別の代替手段です。従来の constructor および byType オートワイヤーモード(共同エディターのオートワイヤーで説明)は、それぞれコンストラクター引数または setter メソッドパラメーターに対してタイプ ApplicationContext の依存関係を提供できます。フィールドや複数のパラメーターメソッドをオートワイヤーする機能など、柔軟性を高めるには、アノテーションベースのオートワイヤー機能を使用します。使用すると、ApplicationContext は、問題のフィールド、コンストラクター、またはメソッドが @Autowired アノテーションを保持している場合、ApplicationContext タイプを予期するフィールド、コンストラクター引数、またはメソッドパラメーターに自動接続されます。詳細については、@Autowired の使用を参照してください。

ApplicationContext が org.springframework.beans.factory.BeanNameAware インターフェースを実装するクラスを作成すると、そのクラスには、関連するオブジェクト定義で定義された名前への参照が提供されます。次のリストは、BeanNameAware インターフェースの定義を示しています。

Java
public interface BeanNameAware {

    void setBeanName(String name) throws BeansException;
}
Kotlin
interface BeanNameAware {

    @Throws(BeansException::class)
    fun setBeanName(name: String)
}

コールバックは、通常の Bean プロパティの設定後、InitializingBeanafterPropertiesSet などの初期化コールバック、またはカスタム init-method の前に呼び出されます。

1.6.3. その他の Aware インターフェース

ApplicationContextAware および BeanNameAware (前述)に加えて、Spring は、Bean が特定のインフラストラクチャ依存性を必要とすることをコンテナーに示すことができる、広範囲の Aware コールバックインターフェースを提供します。一般的なルールとして、名前は依存関係のタイプを示します。次の表に、最も重要な Aware インターフェースを要約します。

表 4: 認識インターフェース
名前 注入された依存関係 説明…

ApplicationContextAware

ApplicationContext の宣言。

ApplicationContextAware および BeanNameAware

ApplicationEventPublisherAware

包含 ApplicationContext のイベント発行者。

ApplicationContext の追加機能

BeanClassLoaderAware

Bean クラスをロードするために使用されるクラスローダー。

Bean のインスタンス化

BeanFactoryAware

BeanFactory の宣言。

ApplicationContextAware および BeanNameAware

BeanNameAware

宣言する Bean の名前。

ApplicationContextAware および BeanNameAware

LoadTimeWeaverAware

ロード時にクラス定義を処理するためのウィーバーを定義しました。

Spring Framework での AspectJ によるロード時の織り

MessageSourceAware

メッセージを解決するための構成された戦略(パラメーター化と国際化のサポート付き)。

ApplicationContext の追加機能

NotificationPublisherAware

Spring JMX 通知パブリッシャー。

通知

ResourceLoaderAware

リソースへの低レベルアクセス用に構成されたローダー。

リソース

ServletConfigAware

コンテナーが実行される現在の ServletConfig。Web 対応 Spring ApplicationContext でのみ有効です。

Spring MVC

ServletContextAware

コンテナーが実行される現在の ServletContext。Web 対応 Spring ApplicationContext でのみ有効です。

Spring MVC

これらのインターフェースを使用すると、コードが Spring API に結び付けられ、Inversion of Control スタイルに従っていません。その結果、コンテナーへのプログラムによるアクセスを必要とするインフラストラクチャ Bean にお勧めします。

1.7. Bean 定義の継承

Bean 定義には、コンストラクター引数、プロパティ値、初期化メソッド、静的ファクトリメソッド名などのコンテナー固有の情報など、多くの構成情報を含めることができます。子 Bean 定義は、親定義から構成データを継承します。子定義は、必要に応じて一部の値をオーバーライドしたり、他の値を追加したりできます。親と子の Bean 定義を使用すると、入力を大幅に節約できます。事実上、これはテンプレートの形式です。

ApplicationContext インターフェースをプログラムで操作する場合、子 Bean 定義は ChildBeanDefinition クラスで表されます。ほとんどのユーザーは、このレベルではそれらを操作しません。代わりに、ClassPathXmlApplicationContext などのクラスで Bean 定義を宣言的に構成します。XML ベースの構成メタデータを使用する場合、parent 属性を使用して子 Bean 定義を指定し、この属性の値として親 Bean を指定できます。次の例は、その方法を示しています。

<bean id="inheritedTestBean" abstract="true"
        class="org.springframework.beans.TestBean">
    <property name="name" value="parent"/>
    <property name="age" value="1"/>
</bean>

<bean id="inheritsWithDifferentClass"
        class="org.springframework.beans.DerivedTestBean"
        parent="inheritedTestBean" init-method="initialize">  (1)
    <property name="name" value="override"/>
    <!-- the age property value of 1 will be inherited from parent -->
</bean>
1parent 属性に注意してください。

子 Bean 定義は、指定されていない場合は親定義の Bean クラスを使用しますが、オーバーライドすることもできます。後者の場合、子 Bean クラスは親と互換性がなければなりません(つまり、親のプロパティ値を受け入れる必要があります)。

子 Bean 定義は、新しい値を追加するオプションを使用して、親からスコープ、コンストラクター引数値、プロパティ値、メソッドオーバーライドを継承します。指定したスコープ、初期化メソッド、破棄メソッド、または static ファクトリメソッド設定は、対応する親設定をオーバーライドします。

残りの設定は常に子定義から取得されます:依存、オートワイヤーモード、依存関係チェック、シングルトン、遅延初期化。

前の例では、abstract 属性を使用して、親 Bean 定義を抽象として明示的にマークしています。親定義でクラスが指定されていない場合、次の例に示すように、親 Bean 定義を abstract として明示的にマークする必要があります。

<bean id="inheritedTestBeanWithoutClass" abstract="true">
    <property name="name" value="parent"/>
    <property name="age" value="1"/>
</bean>

<bean id="inheritsWithClass" class="org.springframework.beans.DerivedTestBean"
        parent="inheritedTestBeanWithoutClass" init-method="initialize">
    <property name="name" value="override"/>
    <!-- age will inherit the value of 1 from the parent bean definition-->
</bean>

親 Bean は不完全であるため、単独でインスタンス化することはできません。また、明示的に abstract としてマークされています。定義が abstract の場合、子定義の親定義として機能する純粋なテンプレート Bean 定義としてのみ使用できます。別の Bean の ref プロパティとして参照するか、親 Bean ID で明示的な getBean() 呼び出しを行うことにより、そのような abstract 親 Bean を単独で使用しようとすると、エラーが返されます。同様に、コンテナーの内部 preInstantiateSingletons() メソッドは、抽象として定義されている Bean 定義を無視します。

ApplicationContext は、デフォルトですべてのシングルトンを事前にインスタンス化します。(少なくともシングルトン Bean の場合)テンプレートとしてのみ使用する(親)Bean 定義があり、この定義がクラスを指定する場合、abstract 属性を true に設定する必要があります。そうでない場合、アプリケーションコンテキストは、実際に abstract Bean を事前にインスタンス化(試行)します。

1.8. コンテナー拡張ポイント

通常、アプリケーション開発者は ApplicationContext 実装クラスをサブクラス化する必要はありません。代わりに、Spring IoC コンテナーは、特別な統合インターフェースの実装をプラグインすることにより拡張できます。次のいくつかのセクションでは、これらの統合インターフェースについて説明します。

1.8.1. BeanPostProcessor を使用して Bean をカスタマイズする

BeanPostProcessor インターフェースは、独自の(またはコンテナーのデフォルトをオーバーライドする)インスタンス化ロジック、依存関係解決ロジックなどを提供するために実装できるコールバックメソッドを定義します。Spring コンテナーが Bean のインスタンス化、構成、初期化を完了した後にカスタムロジックを実装する場合は、1 つ以上のカスタム BeanPostProcessor 実装をプラグインできます。

複数の BeanPostProcessor インスタンスを構成でき、order プロパティを設定することにより、これらの BeanPostProcessor インスタンスの実行順序を制御できます。このプロパティを設定できるのは、BeanPostProcessor が Ordered インターフェースを実装している場合のみです。独自の BeanPostProcessor を作成する場合は、Ordered インターフェースの実装も検討する必要があります。詳細については、BeanPostProcessor (Javadoc) および Ordered (Javadoc) インターフェースの javadoc を参照してください。BeanPostProcessor インスタンスのプログラムによる登録に関する注意も参照してください。

BeanPostProcessor インスタンスは、Bean(またはオブジェクト)インスタンスで動作します。つまり、Spring IoC コンテナーが Bean インスタンスをインスタンス化してから、BeanPostProcessor インスタンスが作業を実行します。

BeanPostProcessor インスタンスは、コンテナーごとにスコープされます。これは、コンテナー階層を使用する場合にのみ関係します。1 つのコンテナーで BeanPostProcessor を定義すると、そのコンテナー内の Bean のみが後処理されます。つまり、1 つのコンテナーで定義された Bean は、両方のコンテナーが同じ階層の一部であっても、別のコンテナーで定義された BeanPostProcessor によって後処理されません。

実際の Bean 定義(つまり、Bean を定義する設計図)を変更するには、代わりに BeanFactoryPostProcessor を使用した構成メタデータのカスタマイズで説明されている BeanFactoryPostProcessor を使用する必要があります。

org.springframework.beans.factory.config.BeanPostProcessor インターフェースは、正確に 2 つのコールバックメソッドで構成されています。このようなクラスをポストプロセッサとしてコンテナーに登録すると、コンテナーによって作成される Bean インスタンスごとに、ポストプロセッサはコンテナー初期化メソッド ( InitializingBean.afterPropertiesSet() や公表されている init 法など) が呼び出される前と Bean 初期化コールバックの後の両方で、コンテナーからコールバックを取得します。ポストプロセッサは、コールバックを完全に無視するなど、Bean インスタンスに対して任意のアクションを実行できます。Bean ポストプロセッサは通常、コールバックインターフェースをチェックするか、プロキシで Bean をラップします。一部の Spring AOP インフラストラクチャクラスは、プロキシ折り返しロジックを提供するために Bean ポストプロセッサとして実装されます。

ApplicationContext は、BeanPostProcessor インターフェースを実装する構成メタデータで定義されている Bean を自動的に検出します。ApplicationContext はこれらの Bean をポストプロセッサーとして登録し、Bean の作成時に後で呼び出せるようにします。Bean ポストプロセッサーは、他の Bean と同じ方法でコンテナーにデプロイできます。

構成クラスで @Bean ファクトリメソッドを使用して BeanPostProcessor を宣言する場合、ファクトリメソッドの戻り値の型は、実装クラス自体または少なくとも org.springframework.beans.factory.config.BeanPostProcessor インターフェースである必要があり、その Bean のポストプロセッサの性質を明確に示すことに注意してください。そうしないと、ApplicationContext は完全に作成する前にタイプごとに自動検出できません。コンテキスト内の他の Bean の初期化に適用するには、BeanPostProcessor を早期にインスタンス化する必要があるため、この早期タイプ検出は重要です。

BeanPostProcessor インスタンスをプログラムで登録する
BeanPostProcessor 登録の推奨アプローチは ApplicationContext 自動検出によるものですが(前述)、addBeanPostProcessor メソッドを使用して、ConfigurableBeanFactory に対してプログラムで登録できます。これは、登録前に条件付きロジックを評価する必要がある場合、または階層内のコンテキスト間で Bean ポストプロセッサをコピーする場合にも役立ちます。ただし、プログラムで追加された BeanPostProcessor インスタンスは Ordered インターフェースを考慮しないことに注意してください。ここで、実行の順序を決定するのは登録の順序です。また、プログラムで登録された BeanPostProcessor インスタンスは、明示的な順序に関係なく、自動検出によって登録されたインスタンスの前に常に処理されることに注意してください。
BeanPostProcessor インスタンスと AOP 自動プロキシ

BeanPostProcessor インターフェースを実装するクラスは特別であり、コンテナーによって異なる方法で処理されます。BeanPostProcessor インスタンスとそれらが直接参照する Bean は、ApplicationContext の特別な起動フェーズの一部として、起動時にインスタンス化されます。次に、すべての BeanPostProcessor インスタンスがソートされた方法で登録され、コンテナー内の他のすべての Bean に適用されます。AOP 自動プロキシは BeanPostProcessor 自体として実装されているため、BeanPostProcessor インスタンスも直接参照する Bean も自動プロキシの対象ではなく、それらに織り込まれたアスペクトはありません。

そのような Bean の場合、情報ログメッセージ Bean someBean is not eligible for getting processed by all BeanPostProcessor interfaces (for example: not eligible for auto-proxying) が表示されます。

オートワイヤーまたは @Resource (オートワイヤーにフォールバックする可能性があります)を使用して BeanPostProcessor に Bean を接続している場合、Spring は型一致の依存関係の候補を検索するときに予期しない Bean にアクセスする可能性があるため、自動プロキシまたはその他の種類の資格がありません Bean 後処理。例:フィールドまたは setter 名が Bean の宣言された名前に直接対応せず、name 属性が使用されていない @Resource アノテーションが付けられた依存関係がある場合、Spring は他の Bean にアクセスしてタイプごとに一致させます。

以下の例は、ApplicationContext で BeanPostProcessor インスタンスを作成、登録、使用する方法を示しています。

サンプル : Hello World、BeanPostProcessor -style

この最初の例は、基本的な使用箇所を示しています。この例は、コンテナーによって作成された各 Bean の toString() メソッドを呼び出し、結果の文字列をシステムコンソールに出力するカスタム BeanPostProcessor 実装を示しています。

次のリストは、カスタム BeanPostProcessor 実装クラス定義を示しています。

Java
package scripting;

import org.springframework.beans.factory.config.BeanPostProcessor;

public class InstantiationTracingBeanPostProcessor implements BeanPostProcessor {

    // simply return the instantiated bean as-is
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        return bean; // we could potentially return any object reference here...
    }

    public Object postProcessAfterInitialization(Object bean, String beanName) {
        System.out.println("Bean '" + beanName + "' created : " + bean.toString());
        return bean;
    }
}
Kotlin
import org.springframework.beans.factory.config.BeanPostProcessor

class InstantiationTracingBeanPostProcessor : BeanPostProcessor {

    // simply return the instantiated bean as-is
    override fun postProcessBeforeInitialization(bean: Any, beanName: String): Any? {
        return bean // we could potentially return any object reference here...
    }

    override fun postProcessAfterInitialization(bean: Any, beanName: String): Any? {
        println("Bean '$beanName' created : $bean")
        return bean
    }
}

次の beans 要素は InstantiationTracingBeanPostProcessor を使用します。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:lang="http://www.springframework.org/schema/lang"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/lang
        https://www.springframework.org/schema/lang/spring-lang.xsd">

    <lang:groovy id="messenger"
            script-source="classpath:org/springframework/scripting/groovy/Messenger.groovy">
        <lang:property name="message" value="Fiona Apple Is Just So Dreamy."/>
    </lang:groovy>

    <!--
    when the above bean (messenger) is instantiated, this custom
    BeanPostProcessor implementation will output the fact to the system console
    -->
    <bean class="scripting.InstantiationTracingBeanPostProcessor"/>

</beans>

InstantiationTracingBeanPostProcessor が単に定義されていることに注目してください。名前さえも持たず、Bean であるため、他の Bean と同様に依存関係を注入できます。(上記の構成では、Groovy スクリプトによってサポートされる Bean も定義しています。Spring 動的言語サポートについては、動的言語サポートというタイトルの章で詳しく説明しています。)

次の Java アプリケーションは、前述のコードと構成を実行します。

Java
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.scripting.Messenger;

public final class Boot {

    public static void main(final String[] args) throws Exception {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("scripting/beans.xml");
        Messenger messenger = ctx.getBean("messenger", Messenger.class);
        System.out.println(messenger);
    }

}
Kotlin
import org.springframework.beans.factory.getBean

fun main() {
    val ctx = ClassPathXmlApplicationContext("scripting/beans.xml")
    val messenger = ctx.getBean<Messenger>("messenger")
    println(messenger)
}

上記のアプリケーションの出力は次のようになります。

サンプル : RequiredAnnotationBeanPostProcessor

Spring IoC コンテナーを継承する一般的な方法は、コールバックインターフェースまたはアノテーションをカスタム BeanPostProcessor 実装と組み合わせて使用することです。例は Spring の RequiredAnnotationBeanPostProcessor です。BeanPostProcessor 実装は Spring ディストリビューションに同梱されており、(任意の)アノテーションでマークされた Bean の JavaBean プロパティが実際に(依存するように設定された)値でインジェクトされます。

1.8.2. BeanFactoryPostProcessor を使用した構成メタデータのカスタマイズ

次に見る拡張ポイントは org.springframework.beans.factory.config.BeanFactoryPostProcessor です。このインターフェースのセマンティクスは BeanPostProcessor のセマンティクスと似ていますが、大きな違いが 1 つあります。BeanFactoryPostProcessor は Bean 構成メタデータで動作します。つまり、Spring IoC コンテナーは、BeanFactoryPostProcessor インスタンス以外の Bean をコンテナーがインスタンス化する前にBeanFactoryPostProcessor が構成メタデータを読み取り、潜在的にそれを変更できるようします。

複数の BeanFactoryPostProcessor インスタンスを構成でき、order プロパティを設定することにより、これらの BeanFactoryPostProcessor インスタンスの実行順序を制御できます。ただし、BeanFactoryPostProcessor が Ordered インターフェースを実装している場合にのみ、このプロパティを設定できます。独自の BeanFactoryPostProcessor を作成する場合は、Ordered インターフェースの実装も検討する必要があります。詳細については、BeanFactoryPostProcessor (Javadoc) および Ordered (Javadoc) インターフェースの javadoc を参照してください。

実際の Bean インスタンス(つまり、構成メタデータから作成されたオブジェクト)を変更する場合は、代わりに BeanPostProcessor (前述の BeanPostProcessor を使用して Bean をカスタマイズするで説明)を使用する必要があります。BeanFactoryPostProcessor 内で Bean インスタンスを操作することは技術的には可能ですが(たとえば、BeanFactory.getBean() を使用して)、そうすると、早すぎる Bean インスタンス化が発生し、標準のコンテナーライフサイクルに違反します。これにより、Bean 後処理のバイパスなど、マイナスの副作用が生じる可能性があります。

また、BeanFactoryPostProcessor インスタンスはコンテナーごとにスコープされます。これは、コンテナー階層を使用する場合にのみ関係します。1 つのコンテナーで BeanFactoryPostProcessor を定義すると、そのコンテナーの Bean 定義にのみ適用されます。両方のコンテナーが同じ階層の一部である場合でも、1 つのコンテナー内の Bean 定義は、別のコンテナー内の BeanFactoryPostProcessor インスタンスによって後処理されません。

Bean ファクトリポストプロセッサは、コンテナーを定義する構成メタデータに変更を適用するために、ApplicationContext 内で宣言されたときに自動的に実行されます。Spring には、PropertyOverrideConfigurer や PropertySourcesPlaceholderConfigurer など、事前定義された多数の Bean ファクトリポストプロセッサが含まれています。カスタム BeanFactoryPostProcessor を使用して、たとえば、カスタムプロパティエディターを登録することもできます。

ApplicationContext は、BeanFactoryPostProcessor インターフェースを実装する Bean にデプロイされた Bean を自動的に検出します。適切なタイミングで、これらの Bean を Bean ファクトリポストプロセッサとして使用します。これらのポストプロセッサ Bean は、他の Bean と同様にデプロイできます。

BeanPostProcessor の場合と同様、通常、遅延初期化用に BeanFactoryPostProcessor を構成することは望ましくありません。他の Bean が Bean(Factory)PostProcessor を参照していない場合、そのポストプロセッサーはインスタンス化されません。遅延初期化のマークは無視され、<beans /> 要素の宣言で default-lazy-init 属性を true に設定しても、Bean(Factory)PostProcessor は即座にインスタンス化されます。
サンプル : クラス名の置換 PropertySourcesPlaceholderConfigurer

PropertySourcesPlaceholderConfigurer を使用して、標準の Java Properties 形式を使用することにより、別のファイルの Bean 定義からプロパティ値を外部化できます。そうすることで、アプリケーションをデプロイする人は、複雑な、またはコンテナーの XML 定義ファイルを変更するリスクなしに、データベース URL やパスワードなどの環境固有のプロパティをカスタマイズできます。

プレースホルダー値を持つ DataSource が定義されている次の XML ベースの構成メタデータフラグメントを検討してください。

<bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
    <property name="locations" value="classpath:com/something/jdbc.properties"/>
</bean>

<bean id="dataSource" destroy-method="close"
        class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName" value="${jdbc.driverClassName}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>

この例は、外部 Properties ファイルから構成されたプロパティを示しています。実行時に、DataSource の一部のプロパティを置き換える PropertySourcesPlaceholderConfigurer がメタデータに適用されます。置換する値は、Ant、log4j、JSP EL スタイルに従う ${property-name} 形式のプレースホルダーとして指定されます。

実際の値は、標準 Java Properties 形式の別のファイルから取得されます。

jdbc.driverClassName=org.hsqldb.jdbcDriver
jdbc.url=jdbc:hsqldb:hsql://production:9002
jdbc.username=sa
jdbc.password=root

${jdbc.username} 文字列は実行時に値「sa」に置き換えられ、プロパティファイル内のキーと一致する他のプレースホルダー値にも同じことが当てはまります。PropertySourcesPlaceholderConfigurer は、Bean 定義のほとんどのプロパティと属性のプレースホルダーをチェックします。さらに、プレースホルダーのプレフィックスとサフィックスをカスタマイズできます。

Spring 2.5, で導入された context 名前空間を使用すると、専用の構成要素でプロパティプレースホルダーを構成できます。次の例に示すように、location 属性の 1 つ以上の場所をコンマ区切りリストとして指定できます。

<context:property-placeholder location="classpath:com/something/jdbc.properties"/>

PropertySourcesPlaceholderConfigurer は、指定した Properties ファイルのプロパティを探すだけではありません。デフォルトでは、指定されたプロパティファイルでプロパティが見つからない場合、Spring Environment プロパティおよび通常の Java System プロパティに対してチェックします。

PropertySourcesPlaceholderConfigurer を使用してクラス名を置き換えることができます。これは、実行時に特定の実装クラスを選択する必要がある場合に役立つことがあります。次の例は、その方法を示しています。

<bean class="org.springframework.beans.factory.config.PropertySourcesPlaceholderConfigurer">
    <property name="locations">
        <value>classpath:com/something/strategy.properties</value>
    </property>
    <property name="properties">
        <value>custom.strategy.class=com.something.DefaultStrategy</value>
    </property>
</bean>

<bean id="serviceStrategy" class="${custom.strategy.class}"/>

実行時にクラスを有効なクラスに解決できない場合、Bean の解決は、作成されようとしているときに失敗します。これは、lazy-init 以外の Bean の ApplicationContext の preInstantiateSingletons() フェーズ中です。

サンプル : PropertyOverrideConfigurer

別の Bean ファクトリポストプロセッサである PropertyOverrideConfigurer は PropertySourcesPlaceholderConfigurer に似ていますが、後者とは異なり、元の定義には Bean プロパティのデフォルト値を設定することも、値をまったく設定しないこともできます。オーバーライドする Properties ファイルに特定の Bean プロパティのエントリがない場合、デフォルトのコンテキスト定義が使用されます。

Bean 定義はオーバーライドされることを認識していないため、オーバーライド構成が使用されていることは XML 定義ファイルからすぐにはわかりません。同じ Bean プロパティに異なる値を定義する複数の PropertyOverrideConfigurer インスタンスの場合、オーバーライドメカニズムにより、最後のインスタンスが優先されます。

プロパティファイルの構成行の形式は次のとおりです。

beanName.property=value

次のリストは、フォーマットの例を示しています。

dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql:mydb

このサンプルファイルは、driver および url プロパティを持つ dataSource という Bean を含むコンテナー定義で使用できます。

オーバーライドされる最終プロパティを除くパスのすべてのコンポーネントがすでに null でない(おそらくコンストラクターによって初期化される)限り、複合プロパティ名もサポートされます。次の例では、tom Bean の fred プロパティの bob プロパティの sammy プロパティがスカラー値 123 に設定されます。

tom.fred.bob.sammy=123
指定されたオーバーライド値は常にリテラル値です。それらは Bean 参照に変換されません。この規則は、XML Bean 定義の元の値が Bean 参照を指定している場合にも適用されます。

Spring 2.5, で導入された context 名前空間を使用すると、次の例に示すように、専用の構成要素でオーバーライドするプロパティを構成できます。

<context:property-override location="classpath:override.properties"/>

1.8.3. FactoryBean を使用したインスタンス化ロジックのカスタマイズ

自身がファクトリであるオブジェクトに対して org.springframework.beans.factory.FactoryBean インターフェースを実装できます。

FactoryBean インターフェースは、Spring IoC コンテナーのインスタンス化ロジックへのプラグインのポイントです。(潜在的に)冗長な量の XML ではなく Java でより適切に表現される複雑な初期化コードがある場合、独自の FactoryBean を作成し、そのクラス内に複雑な初期化を記述してから、カスタム FactoryBean をコンテナーにプラグインできます。

FactoryBean インターフェースには 3 つの方法があります。

  • Object getObject(): このファクトリが作成するオブジェクトのインスタンスを返します。このファクトリがシングルトンを返すかプロトタイプを返すかに応じて、インスタンスを共有できます。

  • boolean isSingleton(): この FactoryBean がシングルトンを返す場合は true を返し、そうでない場合は false を返します。

  • Class getObjectType()getObject() メソッドまたは null によって返されたオブジェクトタイプを返します。タイプが事前にわからない場合。

FactoryBean の概念とインターフェースは、Spring Framework 内の多くの場所で使用されています。FactoryBean インターフェースの 50 を超える実装には、Spring 自体が付属しています。

コンテナーが生成する Bean ではなく、実際の FactoryBean インスタンス自体を要求する必要がある場合、ApplicationContext の getBean() メソッドを呼び出すときに、Bean の id の前にアンパサンド記号(&)を付けます。そのため、id が myBean である特定の FactoryBean について、コンテナーで getBean("myBean") を呼び出すと FactoryBean の積が返され、getBean("&myBean") を呼び出すと FactoryBean インスタンス自体が返されます。

1.9. アノテーションベースのコンテナー構成

Spring を構成するためのアノテーションは XML よりも優れていますか?

アノテーションベースの構成の導入により、このアプローチが XML よりも「優れている」かどうかという疑問が生じました。短い答えは「それは依存します」です。長い答えは、各アプローチには長所と短所があり、通常、どの戦略がより適しているかを決定するのは開発者次第です。それらが定義される方法のために、アノテーションは宣言で多くのコンテキストを提供し、より短くより簡潔な構成につながります。ただし、XML は、ソースコードに触れたり、再コンパイルしたりすることなく、コンポーネントの接続に優れています。ソースに近い接続を好む開発者もいれば、アノテーション付きクラスはもはや POJO ではなく、さらに構成が分散化され制御が難しくなると主張する開発者もいます。

どちらを選択しても、Spring は両方のスタイルに対応し、さらには両方を組み合わせることもできます。JavaConfig オプションによって、Spring はターゲットコンポーネントのソースコードに手を触れることなく、非侵襲的な方法でアノテーションを使用できること、ツールに関しては、すべての構成スタイルが Eclipse Pleiades All in One (STS, Lombok 付属) または Eclipse 用 Spring Tools (英語) によってサポートされることを指摘しておく価値があります。

XML セットアップの代替手段は、アノテーションベースの構成によって提供されます。これは、山括弧宣言の代わりにコンポーネントを接続するためのバイトコードメタデータに依存します。XML を使用して Bean ワイヤリングを記述する代わりに、開発者は関連するクラス、メソッド、またはフィールド宣言のアノテーションを使用して、構成をコンポーネントクラス自体に移動します。サンプル : RequiredAnnotationBeanPostProcessor で説明したように、BeanPostProcessor をアノテーションと組み合わせて使用することは、Spring IoC コンテナーを継承する一般的な方法です。例:Spring 2.0 は、@Required アノテーションで必要なプロパティを強制する可能性を導入しました。Spring 2.5 は、Spring の依存性注入を駆動するための同じ一般的なアプローチに従うことを可能にしました。基本的に、@Autowired アノテーションは、共同エディターのオートワイヤーで説明されているものと同じ機能を提供しますが、よりきめ細かい制御と幅広い適用性を備えています。Spring 2.5 は、@PostConstruct や @PreDestroy などの JSR-250 アノテーションのサポートも追加しました。Spring 3.0 は、@Inject や @Named などの javax.inject パッケージに含まれる JSR-330(Dependency Injection for Java)アノテーションのサポートを追加しました。これらのアノテーションの詳細は、関連セクションに記載されています。

アノテーション注入は、XML 注入の前に実行されます。XML 構成は、両方のアプローチで接続されたプロパティのアノテーションをオーバーライドします。

通常どおり、個別の Bean 定義として登録できますが、XML ベースの Spring 構成に次のタグを含めることで暗黙的に登録することもできます(context 名前空間の組み込みに注意してください)。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>

</beans>

<context:annotation-config/> は、定義されているのと同じアプリケーションコンテキストで Bean のアノテーションのみを検索します。つまり、<context:annotation-config/> を DispatcherServlet の WebApplicationContext に配置すると、コントローラーでは @Autowired Bean のみがチェックされ、サービスはチェックされません。詳細については、DispatcherServlet を参照してください。

1.9.1. @Required

@Required アノテーションは、次の例のように、Bean プロパティ setter メソッドに適用されます。

Java
public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Required
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...
}
Kotlin
    class SimpleMovieLister {

    @Required
    lateinit var movieFinder: MovieFinder

    // ...
}

このアノテーションは、影響を受ける Bean プロパティが、構成時に、Bean 定義の明示的なプロパティ値またはオートワイヤーを通じて取り込まれる必要があることを示します。影響を受ける Bean プロパティが設定されていない場合、コンテナーは例外をスローします。これにより、後の NullPointerException インスタンスなどを回避して、積極的かつ明示的な障害を許容します。アサーションを Bean クラス自体に(たとえば、init メソッドに)入れることをお勧めします。そうすることで、コンテナーの外部でクラスを使用する場合でも、これらの必要な参照と値が強制されます。

@Required アノテーションは、必要な設定(または Bean プロパティ setter メソッドと共に InitializingBean.afterPropertiesSet() のカスタム実装)を使用するために、Spring Framework 5.1, の時点で正式に非推奨になりました。

1.9.2. @Autowired を使用する

このセクションに含まれる例では、JSR 330 の @Inject アノテーションを Spring の @Autowired アノテーションの代わりに使用できます。詳細はこちらを参照してください。

次の例に示すように、@Autowired アノテーションをコンストラクターに適用できます。

Java
public class MovieRecommender {

    private final CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
        this.customerPreferenceDao = customerPreferenceDao;
    }

    // ...
}
Kotlin
class MovieRecommender @Autowired constructor(
    private val customerPreferenceDao: CustomerPreferenceDao)

Spring Framework 4.3, 以降、ターゲット Bean が最初に 1 つのコンストラクターのみを定義する場合、そのようなコンストラクターの @Autowired アノテーションは不要になりました。ただし、いくつかのコンストラクターが使用可能で、プライマリ / デフォルトコンストラクターがない場合、どちらを使用するかをコンテナーに指示するには、コンストラクターの少なくとも 1 つに @Autowired アノテーションを付ける必要があります。詳細については、コンストラクターの解決に関する説明を参照してください。

次の例に示すように、@Autowired アノテーションを従来の setter メソッドに適用することもできます。

Java
public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Autowired
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...
}
Kotlin
class SimpleMovieLister {

    @Autowired
    lateinit var movieFinder: MovieFinder

    // ...

}

次の例に示すように、任意の名前と複数の引数を持つメソッドにアノテーションを適用することもできます。

Java
public class MovieRecommender {

    private MovieCatalog movieCatalog;

    private CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    public void prepare(MovieCatalog movieCatalog,
            CustomerPreferenceDao customerPreferenceDao) {
        this.movieCatalog = movieCatalog;
        this.customerPreferenceDao = customerPreferenceDao;
    }

    // ...
}
Kotlin
class MovieRecommender {

    private lateinit var movieCatalog: MovieCatalog

    private lateinit var customerPreferenceDao: CustomerPreferenceDao

    @Autowired
    fun prepare(movieCatalog: MovieCatalog,
                customerPreferenceDao: CustomerPreferenceDao) {
        this.movieCatalog = movieCatalog
        this.customerPreferenceDao = customerPreferenceDao
    }

    // ...
}

次の例に示すように、@Autowired をフィールドにも適用し、コンストラクターと組み合わせることもできます。

Java
public class MovieRecommender {

    private final CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    private MovieCatalog movieCatalog;

    @Autowired
    public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
        this.customerPreferenceDao = customerPreferenceDao;
    }

    // ...
}
Kotlin
class MovieRecommender @Autowired constructor(
    private val customerPreferenceDao: CustomerPreferenceDao) {

    @Autowired
    private lateinit var movieCatalog: MovieCatalog

    // ...
}

ターゲットコンポーネント(MovieCatalog または CustomerPreferenceDao など)が、@Autowired アノテーション付き注入ポイントに使用するタイプによって一貫して宣言されていることを確認してください。そうしないと、実行時に「タイプ一致が見つかりません」エラーが原因で注入が失敗する場合があります。

クラスパススキャンを介して検出された XML 定義の Bean またはコンポーネントクラスの場合、コンテナーは通常、事前に具体的なタイプを認識します。ただし、@Bean ファクトリメソッドの場合、宣言された戻り値の型が十分に表現力があることを確認する必要があります。複数のインターフェースを実装するコンポーネント、または実装タイプによって潜在的に参照されるコンポーネントの場合、ファクトリメソッドで最も具体的な戻り値型を宣言することを検討してください(少なくとも Bean を参照する注入ポイントで必要とされる特定の)。

次の例に示すように、ApplicationContext から特定のタイプのすべての Bean を提供するように Spring に指示して、@Autowired アノテーションをそのタイプの配列を想定するフィールドまたはメソッドに追加することもできます。

Java
public class MovieRecommender {

    @Autowired
    private MovieCatalog[] movieCatalogs;

    // ...
}
Kotlin
class MovieRecommender {

    @Autowired
    private lateinit var movieCatalogs: Array<MovieCatalog>

    // ...
}

次の例に示すように、型付きコレクションにも同じことが当てはまります。

Java
public class MovieRecommender {

    private Set<MovieCatalog> movieCatalogs;

    @Autowired
    public void setMovieCatalogs(Set<MovieCatalog> movieCatalogs) {
        this.movieCatalogs = movieCatalogs;
    }

    // ...
}
Kotlin
class MovieRecommender {

    @Autowired
    lateinit var movieCatalogs: Set<MovieCatalog>

    // ...
}

配列またはリスト内の項目を特定の順序でソートする場合、ターゲット Bean は org.springframework.core.Ordered インターフェースを実装するか、@Order または標準 @Priority アノテーションを使用できます。それ以外の場合、それらの順序は、コンテナー内の対応するターゲット Bean 定義の登録順序に従います。

@Order アノテーションは、ターゲットクラスレベルおよび @Bean メソッドで、個々の Bean 定義に対して宣言できます(同じ Bean クラスを使用する複数の定義の場合)。@Order 値は、注入ポイントの優先順位に影響を与える可能性がありますが、依存関連と @DependsOn 宣言によって決定される直交の懸念であるシングルトンの起動順序には影響しないことに注意してください。

標準の javax.annotation.Priority アノテーションは、メソッドで宣言できないため、@Bean レベルでは使用できないことに注意してください。そのセマンティクスは、各タイプの単一 Bean で @Primary と組み合わせて @Order 値を介してモデル化できます。

予想されるキータイプが String である限り、タイプされた Map インスタンスでさえオートワイヤーできます。次の例に示すように、マップ値には予想されるタイプのすべての Bean が含まれ、キーには対応する Bean 名が含まれます。

Java
public class MovieRecommender {

    private Map<String, MovieCatalog> movieCatalogs;

    @Autowired
    public void setMovieCatalogs(Map<String, MovieCatalog> movieCatalogs) {
        this.movieCatalogs = movieCatalogs;
    }

    // ...
}
Kotlin
class MovieRecommender {

    @Autowired
    lateinit var movieCatalogs: Map<String, MovieCatalog>

    // ...
}

デフォルトでは、特定の注入ポイントに一致する候補 Bean がない場合、オートワイヤーは失敗します。宣言された配列、コレクション、またはマップの場合、少なくとも 1 つの一致する要素が期待されます。

デフォルトの動作では、アノテーション付きのメソッドとフィールドを必要な依存関係を示すものとして扱います。次の例に示すように、この動作を変更して、フレームワークが不必要なものとしてマークすることで不満足な注入ポイントをスキップできるようにします(つまり、@Autowired の required 属性を false に設定することにより)。

Java
public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Autowired(required = false)
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...
}
Kotlin
class SimpleMovieLister {

    @Autowired(required = false)
    var movieFinder: MovieFinder? = null

    // ...
}

依存関係(または、複数の引数の場合はその依存関係の 1 つ)が利用できない場合、非必須メソッドはまったく呼び出されません。このような場合、必須ではないフィールドはまったく入力されず、デフォルト値がそのまま残ります。

@Autowired の required 属性は、複数のコンストラクターを処理する可能性のある Spring のコンストラクター解決アルゴリズムのために、注入されたコンストラクターとファクトリメソッドの引数は特別な場合があります。コンストラクターとファクトリメソッドの引数はデフォルトで効果的に必要ですが、単一のコンストラクターシナリオでは、一致する Bean が利用できない場合に空のインスタンスに解決する複数要素のインジェクションポイント(配列、コレクション、マップ)などのいくつかの特別なルールがありますこれにより、すべての依存関係を一意の複数引数コンストラクターで宣言できる共通の実装パターンが可能になります。たとえば、@Autowired アノテーションなしで単一のパブリックコンストラクターとして宣言できます。

特定の Bean クラスの 1 つのコンストラクターのみが、required 属性を true に設定して @Autowired を宣言できます。これは、Spring Bean として使用される場合にオートワイヤーするコンストラクターを示します。その結果、required 属性がデフォルト値の true のままである場合、@Autowired でアノテーションを付けられるコンストラクターは 1 つだけです。複数のコンストラクターがアノテーションを宣言する場合、それらはすべてオートワイヤーの候補と見なされるために required=false を宣言する必要があります(XML の autowire=constructor に類似)。Spring コンテナー内の Bean を一致させることで満たすことができる依存関係の数が最も多いコンストラクターが選択されます。どの候補も満たすことができない場合は、プライマリ / デフォルトコンストラクター(存在する場合)が使用されます。同様に、クラスが複数のコンストラクターを宣言しているが、それらのいずれにも @Autowired アノテーションが付いていない場合、プライマリ / デフォルトコンストラクター(存在する場合)が使用されます。クラスが最初に単一のコンストラクターのみを宣言する場合、アノテーションが付けられていなくても、常に使用されます。アノテーション付きコンストラクターはパブリックである必要はないことに注意してください。

@Autowired の required 属性は、setter メソッドの非推奨の @Required アノテーションよりも推奨されます。required 属性を false に設定すると、このプロパティはオートワイヤーには不要であり、プロパティをオートワイヤーできない場合は無視されます。一方、@Required は、コンテナーでサポートされている任意の手段によって設定されるプロパティを強制し、値が定義されていない場合、対応する例外が発生するという点でより強力です。

または、次の例に示すように、Java 8 の java.util.Optional を使用して、特定の依存関係の不要な性質を表現できます。

public class SimpleMovieLister {

    @Autowired
    public void setMovieFinder(Optional<MovieFinder> movieFinder) {
        ...
    }
}

Spring Framework 5.0, 以降、@Nullable アノテーション(任意のパッケージ内の任意の種類の、たとえば JSR-305 の javax.annotation.Nullable など)を使用するか、Kotlin 組み込みの null セーフティサポートを利用することもできます。

Java
public class SimpleMovieLister {

    @Autowired
    public void setMovieFinder(@Nullable MovieFinder movieFinder) {
        ...
    }
}
Kotlin
class SimpleMovieLister {

    @Autowired
    var movieFinder: MovieFinder? = null

    // ...
}

よく知られている解決可能な依存関係である BeanFactoryApplicationContextEnvironmentResourceLoaderApplicationEventPublisher および MessageSource のインターフェースにも @Autowired を使用できます。これらのインターフェースと、ConfigurableApplicationContext や ResourcePatternResolver などの拡張インターフェースは自動的に解決され、特別なセットアップは必要ありません。次の例は、ApplicationContext オブジェクトをオートワイヤーします。

Java
public class MovieRecommender {

    @Autowired
    private ApplicationContext context;

    public MovieRecommender() {
    }

    // ...
}
Kotlin
class MovieRecommender {

    @Autowired
    lateinit var context: ApplicationContext

    // ...
}

@Autowired@Inject@Value および @Resource アノテーションは、Spring BeanPostProcessor 実装によって処理されます。これは、独自の BeanPostProcessor または BeanFactoryPostProcessor タイプ(存在する場合)内でこれらのアノテーションを適用できないことを意味します。これらのタイプは、XML または Spring @Bean メソッドを使用して明示的に「接続」する必要があります。

1.9.3. @Primary によるアノテーションベースのオートワイヤーの微調整

タイプによるオートワイヤーは複数の候補につながる可能性があるため、多くの場合、選択プロセスをより詳細に制御する必要があります。これを実現する 1 つの方法は、Spring の @Primary アノテーションを使用することです。@Primary は、複数の Bean が単一値の依存関係にオートワイヤーされる候補である場合、特定の Bean を優先する必要があることを示します。候補の中に 1 つのプライマリ Bean が存在する場合、オートワイヤーされた値になります。

firstMovieCatalog をプライマリ MovieCatalog として定義する次の構成を検討してください。

Java
@Configuration
public class MovieConfiguration {

    @Bean
    @Primary
    public MovieCatalog firstMovieCatalog() { ... }

    @Bean
    public MovieCatalog secondMovieCatalog() { ... }

    // ...
}
Kotlin
@Configuration
class MovieConfiguration {

    @Bean
    @Primary
    fun firstMovieCatalog(): MovieCatalog { ... }

    @Bean
    fun secondMovieCatalog(): MovieCatalog { ... }

    // ...
}

上記の構成では、次の MovieRecommender が firstMovieCatalog と自動接続されます。

Java
public class MovieRecommender {

    @Autowired
    private MovieCatalog movieCatalog;

    // ...
}
Kotlin
class MovieRecommender {

    @Autowired
    private lateinit var movieCatalog: MovieCatalog

    // ...
}

対応する Bean 定義は次のとおりです。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>

    <bean class="example.SimpleMovieCatalog" primary="true">
        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean class="example.SimpleMovieCatalog">
        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean id="movieRecommender" class="example.MovieRecommender"/>

</beans>

1.9.4. 修飾子を使用したアノテーションベースのオートワイヤーの微調整

@Primary は、1 つの 1 次候補を決定できる場合に、複数のインスタンスでタイプごとのオートワイヤーを使用する効果的な方法です。選択プロセスをさらに制御する必要がある場合は、Spring の @Qualifier アノテーションを使用できます。修飾子の値を特定の引数に関連付けて、特定の Bean が各引数に選択されるようにタイプ一致のセットを絞り込みます。最も単純なケースでは、次の例に示すように、これはわかりやすい説明的な値になります。

Java
public class MovieRecommender {

    @Autowired
    @Qualifier("main")
    private MovieCatalog movieCatalog;

    // ...
}
Kotlin
class MovieRecommender {

    @Autowired
    @Qualifier("main")
    private lateinit var movieCatalog: MovieCatalog

    // ...
}

次の例に示すように、個々のコンストラクター引数またはメソッドパラメーターに @Qualifier アノテーションを指定することもできます。

Java
public class MovieRecommender {

    private MovieCatalog movieCatalog;

    private CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    public void prepare(@Qualifier("main") MovieCatalog movieCatalog,
            CustomerPreferenceDao customerPreferenceDao) {
        this.movieCatalog = movieCatalog;
        this.customerPreferenceDao = customerPreferenceDao;
    }

    // ...
}
Kotlin
class MovieRecommender {

    private lateinit var movieCatalog: MovieCatalog

    private lateinit var customerPreferenceDao: CustomerPreferenceDao

    @Autowired
    fun prepare(@Qualifier("main") movieCatalog: MovieCatalog,
                customerPreferenceDao: CustomerPreferenceDao) {
        this.movieCatalog = movieCatalog
        this.customerPreferenceDao = customerPreferenceDao
    }

    // ...
}

次の例は、対応する Bean 定義を示しています。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>

    <bean class="example.SimpleMovieCatalog">
        <qualifier value="main"/> (1)

        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean class="example.SimpleMovieCatalog">
        <qualifier value="action"/> (2)

        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean id="movieRecommender" class="example.MovieRecommender"/>

</beans>
1main 修飾子値を持つ Bean は、同じ値で修飾されたコンストラクター引数に関連付けられます。
2action 修飾子値を持つ Bean は、同じ値で修飾されたコンストラクター引数に関連付けられます。

フォールバック一致の場合、Bean 名はデフォルトの修飾子値と見なされます。ネストされた修飾子要素の代わりに main の id を使用して Bean を定義すると、同じ一致結果が得られます。ただし、この規則を使用して特定の Bean を名前で参照することはできますが、@Autowired は基本的に、オプションのセマンティック修飾子を使用したタイプ駆動型注入に関するものです。これは、Bean 名のフォールバックがある場合でも、修飾子の値は、タイプ一致のセット内で常に狭義のセマンティクスを持つことを意味します。それらは、一意の Bean id への参照を意味的に表現しません。適切な修飾子の値は main または EMEA または persistent で、Bean id から独立した特定のコンポーネントの特性を表します。これは、前述の例のような匿名 Bean 定義の場合に自動生成される場合があります。

前述のように、修飾子は型付きコレクションにも適用されます。たとえば、Set<MovieCatalog> に適用されます。この場合、宣言された修飾子に従って、一致するすべての Bean がコレクションとして注入されます。これは、修飾子が一意である必要がないことを意味します。むしろ、それらはフィルタリング条件を構成します。例:同じ修飾子値「アクション」を持つ複数の MovieCatalog Bean を定義できます。これらはすべて、@Qualifier("action") アノテーションが付けられた Set<MovieCatalog> に注入されます。

タイプ一致候補内のターゲット Bean 名に対して修飾子の値を選択できるようにするには、注入ポイントで @Qualifier アノテーションを必要としません。他の解決インジケータ(修飾子やプライマリマーカーなど)がない場合、一意でない依存関係の状況では、Spring は注入ポイント名(つまり、フィールド名またはパラメーター名)をターゲット Bean 名と照合し、選択します。同じ名前の候補(ある場合)。

ただし、アノテーション駆動型の注入を名前で表現する場合は、タイプ一致候補の中から Bean 名で選択できる場合でも、主に @Autowired を使用しないでください。代わりに、JSR-250 @Resource アノテーションを使用します。これは、一意の名前で特定のターゲットコンポーネントを識別するためにセマンティックに定義されており、宣言されたタイプはマッチングプロセスとは無関係です。@Autowired にはかなり異なるセマンティクスがあります:タイプによって候補 Bean を選択した後、指定された String 修飾子の値は、それらのタイプ選択された候補内でのみ考慮されます(たとえば、同じ修飾子ラベルでマークされた Bean に対して account 修飾子を照合します)。

それ自体がコレクション Map または配列タイプとして定義されている Bean の場合、@Resource は特定のコレクションまたは配列 Bean を一意の名前で参照する優れたソリューションです。つまり、4.3、コレクションの時点では、@Bean の戻り値の型の署名またはコレクションの継承階層に要素型情報が保持されている限り、Spring の @Autowired タイプマッチングアルゴリズムを介して Map および配列タイプをマッチングできます。この場合、前の段落で概説したように、修飾子の値を使用して、同じ型のコレクションから選択できます。

4.3 以降、@Autowired は注入の自己参照(つまり、現在注入されている Bean への参照)も考慮します。自己注入はフォールバックであることに注意してください。他のコンポーネントへの定期的な依存関係は常に優先されます。その意味で、自己参照は通常の候補者選考には参加しないため、特に初心者になることはありません。それどころか、それらは常に最低の優先順位になります。実際には、自己参照は最後の手段としてのみ使用する必要があります(たとえば、Bean のトランザクションプロキシを介して同じインスタンスで他のメソッドを呼び出す場合など)。このようなシナリオでは、影響を受けるメソッドを別のデリゲート Bean に除外することを検討してください。または、@Resource を使用することもできます。これにより、一意の名前で現在の Bean にプロキシを戻すことができます。

同じ構成クラスの @Bean メソッドから結果を注入しようとすることも、事実上自己参照シナリオです。構成クラスのオートワイヤーフィールドとは対照的に、実際に必要なメソッドシグネチャーでそのような参照を遅延解決するか、影響を受ける @Bean メソッドを static として宣言し、含む構成クラスインスタンスとそのライフサイクルから切り離します。それ以外の場合、そのような Bean はフォールバックフェーズでのみ考慮され、他の構成クラスの一致する Bean が代わりにプライマリ候補として選択されます(利用可能な場合)。

@Autowired は、フィールド、コンストラクター、複数引数メソッドに適用され、パラメーターレベルで修飾子のアノテーションを絞り込むことができます。対照的に、@Resource は、単一の引数を持つフィールドおよび Bean プロパティ setter メソッドに対してのみサポートされます。結果として、注入ターゲットがコンストラクターまたは複数引数メソッドである場合、修飾子を使用する必要があります。

独自のカスタム修飾子アノテーションを作成できます。これを行うには、次の例に示すように、アノテーションを定義し、定義内で @Qualifier アノテーションを提供します。

Java
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Genre {

    String value();
}
Kotlin
@Target(AnnotationTarget.FIELD, AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME)
@Qualifier
annotation class Genre(val value: String)

次に、次の例に示すように、オートワイヤーされたフィールドとパラメーターにカスタム修飾子を提供できます。

Java
public class MovieRecommender {

    @Autowired
    @Genre("Action")
    private MovieCatalog actionCatalog;

    private MovieCatalog comedyCatalog;

    @Autowired
    public void setComedyCatalog(@Genre("Comedy") MovieCatalog comedyCatalog) {
        this.comedyCatalog = comedyCatalog;
    }

    // ...
}
Kotlin
class MovieRecommender {

    @Autowired
    @Genre("Action")
    private lateinit var actionCatalog: MovieCatalog

    private lateinit var comedyCatalog: MovieCatalog

    @Autowired
    fun setComedyCatalog(@Genre("Comedy") comedyCatalog: MovieCatalog) {
        this.comedyCatalog = comedyCatalog
    }

    // ...
}

次に、候補 Bean 定義の情報を提供できます。<qualifier/> タグを <bean/> タグのサブエレメントとして追加してから、type および value を指定して、カスタム修飾子アノテーションに一致させることができます。タイプは、アノテーションの完全修飾クラス名と照合されます。または、名前の競合のリスクが存在しない場合の便宜として、短いクラス名を使用できます。次の例は、両方のアプローチを示しています。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>

    <bean class="example.SimpleMovieCatalog">
        <qualifier type="Genre" value="Action"/>
        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean class="example.SimpleMovieCatalog">
        <qualifier type="example.Genre" value="Comedy"/>
        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean id="movieRecommender" class="example.MovieRecommender"/>

</beans>

クラスパススキャンと管理対象コンポーネントでは、XML で修飾子メタデータを提供する代わりのアノテーションベースの代替手段を見ることができます。具体的には、アノテーション付きの修飾子メタデータの提供を参照してください。

場合によっては、値なしでアノテーションを使用するだけで十分な場合があります。これは、アノテーションがより一般的な目的に役立ち、いくつかの異なるタイプの依存関係に適用できる場合に役立ちます。例:インターネットに接続できないときに検索できるオフラインカタログを提供できます。最初に、次の例に示すように、簡単なアノテーションを定義します。

Java
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Offline {

}
Kotlin
@Target(AnnotationTarget.FIELD, AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME)
@Qualifier
annotation class Offline

次に、次の例に示すように、オートワイヤーするフィールドまたはプロパティにアノテーションを追加します。

Java
public class MovieRecommender {

    @Autowired
    @Offline (1)
    private MovieCatalog offlineCatalog;

    // ...
}
1 この行は、@Offline アノテーションを追加します。
Kotlin
class MovieRecommender {

    @Autowired
    @Offline (1)
    private lateinit var offlineCatalog: MovieCatalog

    // ...
}
1 この行は、@Offline アノテーションを追加します。

これで、次の例に示すように、Bean 定義には修飾子 type のみが必要になります。

<bean class="example.SimpleMovieCatalog">
    <qualifier type="Offline"/> (1)
    <!-- inject any dependencies required by this bean -->
</bean>
1 この要素は修飾子を指定します。

単純な value 属性に加えて、またはその代わりに、名前付き属性を受け入れるカスタム修飾子アノテーションを定義することもできます。オートワイヤーされるフィールドまたはパラメーターに複数の属性値が指定されている場合、Bean 定義は、オートワイヤーの候補と見なされるすべての属性値と一致する必要があります。例として、次のアノテーション定義を検討してください。

Java
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface MovieQualifier {

    String genre();

    Format format();
}
Kotlin
@Target(AnnotationTarget.FIELD, AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME)
@Qualifier
annotation class MovieQualifier(val genre: String, val format: Format)

この場合、Format は列挙型で、次のように定義されます。

Java
public enum Format {
    VHS, DVD, BLURAY
}
Kotlin
enum class Format {
    VHS, DVD, BLURAY
}

オートワイヤーされるフィールドには、カスタム修飾子でアノテーションが付けられ、次の例に示すように、両方の属性 genre および format の値が含まれます。

Java
public class MovieRecommender {

    @Autowired
    @MovieQualifier(format=Format.VHS, genre="Action")
    private MovieCatalog actionVhsCatalog;

    @Autowired
    @MovieQualifier(format=Format.VHS, genre="Comedy")
    private MovieCatalog comedyVhsCatalog;

    @Autowired
    @MovieQualifier(format=Format.DVD, genre="Action")
    private MovieCatalog actionDvdCatalog;

    @Autowired
    @MovieQualifier(format=Format.BLURAY, genre="Comedy")
    private MovieCatalog comedyBluRayCatalog;

    // ...
}
Kotlin
class MovieRecommender {

    @Autowired
    @MovieQualifier(format = Format.VHS, genre = "Action")
    private lateinit var actionVhsCatalog: MovieCatalog

    @Autowired
    @MovieQualifier(format = Format.VHS, genre = "Comedy")
    private lateinit var comedyVhsCatalog: MovieCatalog

    @Autowired
    @MovieQualifier(format = Format.DVD, genre = "Action")
    private lateinit var actionDvdCatalog: MovieCatalog

    @Autowired
    @MovieQualifier(format = Format.BLURAY, genre = "Comedy")
    private lateinit var comedyBluRayCatalog: MovieCatalog

    // ...
}

最後に、Bean 定義には一致する修飾子の値が含まれている必要があります。この例は、<qualifier/> 要素の代わりに Bean メタ属性を使用できることも示しています。可能な場合、<qualifier/> 要素とその属性が優先されますが、次の例の最後の 2 つの Bean 定義のように、そのような修飾子が存在しない場合、オートワイヤーメカニズムは <meta/> タグ内で提供される値にフォールバックします。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>

    <bean class="example.SimpleMovieCatalog">
        <qualifier type="MovieQualifier">
            <attribute key="format" value="VHS"/>
            <attribute key="genre" value="Action"/>
        </qualifier>
        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean class="example.SimpleMovieCatalog">
        <qualifier type="MovieQualifier">
            <attribute key="format" value="VHS"/>
            <attribute key="genre" value="Comedy"/>
        </qualifier>
        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean class="example.SimpleMovieCatalog">
        <meta key="format" value="DVD"/>
        <meta key="genre" value="Action"/>
        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean class="example.SimpleMovieCatalog">
        <meta key="format" value="BLURAY"/>
        <meta key="genre" value="Comedy"/>
        <!-- inject any dependencies required by this bean -->
    </bean>

</beans>

1.9.5. オートワイヤー修飾子としてジェネリックを使用する

@Qualifier アノテーションに加えて、Java ジェネリック型を暗黙的な修飾形式として使用できます。例:次の構成があるとします。

Java
@Configuration
public class MyConfiguration {

    @Bean
    public StringStore stringStore() {
        return new StringStore();
    }

    @Bean
    public IntegerStore integerStore() {
        return new IntegerStore();
    }
}
Kotlin
@Configuration
class MyConfiguration {

    @Bean
    fun stringStore() = StringStore()

    @Bean
    fun integerStore() = IntegerStore()
}

前述の Bean が汎用インターフェース(つまり、Store<String> および Store<Integer>)を実装していると仮定すると、次の例に示すように、@Autowire で Store インターフェースを使用でき、汎用は修飾子として使用されます。

Java
@Autowired
private Store<String> s1; // <String> qualifier, injects the stringStore bean

@Autowired
private Store<Integer> s2; // <Integer> qualifier, injects the integerStore bean
Kotlin
@Autowired
private lateinit var s1: Store<String> // <String> qualifier, injects the stringStore bean

@Autowired
private lateinit var s2: Store<Integer> // <Integer> qualifier, injects the integerStore bean

リスト、Map インスタンス、および配列をオートワイヤーする場合にも、一般的な修飾子が適用されます。次の例は、一般的な List をオートワイヤーします。

Java
// Inject all Store beans as long as they have an <Integer> generic
// Store<String> beans will not appear in this list
@Autowired
private List<Store<Integer>> s;
Kotlin
// Inject all Store beans as long as they have an <Integer> generic
// Store<String> beans will not appear in this list
@Autowired
private lateinit var s: List<Store<Integer>>

1.9.6. CustomAutowireConfigurer を使用する

CustomAutowireConfigurer (Javadoc) は、Spring の @Qualifier アノテーションが付けられていない場合でも、独自のカスタム修飾子アノテーション型を登録できる BeanFactoryPostProcessor です。次の例は、CustomAutowireConfigurer の使用方法を示しています。

<bean id="customAutowireConfigurer"
        class="org.springframework.beans.factory.annotation.CustomAutowireConfigurer">
    <property name="customQualifierTypes">
        <set>
            <value>example.CustomQualifier</value>
        </set>
    </property>
</bean>

AutowireCandidateResolver は、次の方法でオートワイヤー候補を決定します。

  • 各 Bean 定義の autowire-candidate 値

  • <beans/> 要素で利用可能な default-autowire-candidates パターン

  • @Qualifier アノテーションと CustomAutowireConfigurer に登録されたカスタムアノテーションの存在

複数の Bean がオートワイヤー候補として適格である場合、「プライマリ」の決定は次のとおりです。候補の中の 1 つの Bean 定義に primary 属性が true に設定されている場合、それが選択されます。

1.9.7. @Resource による注入

Spring は、フィールドまたは Bean プロパティ setter メソッドで JSR-250 @Resource アノテーション(javax.annotation.Resource)を使用した注入もサポートしています。これは、Java EE の一般的なパターンです。たとえば、JSF 管理の Bean および JAX-WS エンドポイントです。Spring は、Spring 管理オブジェクトに対してもこのパターンをサポートしています。

@Resource は名前属性を取ります。デフォルトでは、Spring はその値を、挿入される Bean 名として解釈します。つまり、次の例に示すように、名前によるセマンティクスに従います。

Java
public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Resource(name="myMovieFinder") (1)
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
}
1 この行は @Resource を注入します。
Kotlin
class SimpleMovieLister {

    @Resource(name="myMovieFinder") (1)
    private lateinit var movieFinder:MovieFinder
}
1 この行は @Resource を注入します。

名前が明示的に指定されていない場合、デフォルト名はフィールド名または setter メソッドから派生します。フィールドの場合、フィールド名を取ります。setter メソッドの場合、Bean プロパティ名を取ります。次の例では、movieFinder という名前の Bean を setter メソッドに挿入します。

Java
public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Resource
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
}
Kotlin
class SimpleMovieLister {

    @Resource
    private lateinit var movieFinder: MovieFinder

}
アノテーションで提供される名前は、CommonAnnotationBeanPostProcessor が認識している ApplicationContext によって Bean 名として解決されます。Spring の SimpleJndiBeanFactory (Javadoc) を明示的に構成すると、JNDI を介して名前を解決できます。ただし、デフォルトの動作に依存し、Spring の JNDI ルックアップ機能を使用して間接性のレベルを維持することをお勧めします。

明示的な名前が指定されておらず、@Autowired@Resource と同様の @Resource の使用の排他的なケースでは、特定の名前付き Bean の代わりにプライマリタイプの一致を検出し、既知の解決可能な依存関係、BeanFactoryApplicationContextResourceLoaderApplicationEventPublisher および MessageSource インターフェースを解決します。

次の例では、customerPreferenceDao フィールドは最初に「customerPreferenceDao」という名前の Bean を探し、次にタイプ CustomerPreferenceDao に一致するプライマリタイプにフォールバックします。

Java
public class MovieRecommender {

    @Resource
    private CustomerPreferenceDao customerPreferenceDao;

    @Resource
    private ApplicationContext context; (1)

    public MovieRecommender() {
    }

    // ...
}
1context フィールドは、既知の解決可能な依存関係タイプ ApplicationContext に基づいて挿入されます。
Kotlin
class MovieRecommender {

    @Resource
    private lateinit var customerPreferenceDao: CustomerPreferenceDao


    @Resource
    private lateinit var context: ApplicationContext (1)

    // ...
}
1context フィールドは、既知の解決可能な依存関係タイプ ApplicationContext に基づいて挿入されます。

1.9.8. @Value を使用する

@Value は通常、外部化されたプロパティを注入するために使用されます。

Java
@Component
public class MovieRecommender {

    private final String catalog;

    public MovieRecommender(@Value("${catalog.name}") String catalog) {
        this.catalog = catalog;
    }
}
Kotlin
@Component
class MovieRecommender(@Value("\${catalog.name}") private val catalog: String)

次の構成で:

Java
@Configuration
@PropertySource("classpath:application.properties")
public class AppConfig { }
Kotlin
@Configuration
@PropertySource("classpath:application.properties")
class AppConfig

そして、次の application.properties ファイル:

catalog.name=MovieCatalog

その場合、catalog パラメーターとフィールドは MovieCatalog 値と等しくなります。

デフォルトの寛容な埋め込み値リゾルバーは、Spring によって提供されます。プロパティ値を解決しようとしますが、解決できない場合は、プロパティ名(${catalog.name} など)が値として挿入されます。存在しない値を厳密に制御したい場合は、次の例に示すように、PropertySourcesPlaceholderConfigurer Bean を宣言する必要があります。

Java
@Configuration
public class AppConfig {

     @Bean
     public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
           return new PropertySourcesPlaceholderConfigurer();
     }
}
Kotlin
@Configuration
class AppConfig {

    @Bean
    fun propertyPlaceholderConfigurer() = PropertySourcesPlaceholderConfigurer()
}
JavaConfig を使用して PropertySourcesPlaceholderConfigurer を構成する場合、@Bean メソッドは static でなければなりません。

上記の構成を使用すると、${} プレースホルダーを解決できなかった場合に Spring 初期化の失敗が保証されます。setPlaceholderPrefixsetPlaceholderSuffix や setValueSeparator などのメソッドを使用してプレースホルダーをカスタマイズすることもできます。

Spring Boot は、デフォルトで、application.properties および application.yml ファイルからプロパティを取得する PropertySourcesPlaceholderConfigurer Bean を構成します。

Spring が提供する組み込みコンバーターのサポートにより、単純なタイプ変換(たとえば、Integer または int へ)を自動的に処理できます。複数のコンマ区切り値は、特別な労力をかけることなく、自動的に文字列配列に変換できます。

次のようにデフォルト値を提供することが可能です。

Java
@Component
public class MovieRecommender {

    private final String catalog;

    public MovieRecommender(@Value("${catalog.name:defaultCatalog}") String catalog) {
        this.catalog = catalog;
    }
}
Kotlin
@Component
class MovieRecommender(@Value("\${catalog.name:defaultCatalog}") private val catalog: String)

Spring BeanPostProcessor は、バックグラウンドで ConversionService を使用して、@Value の文字列値をターゲットタイプに変換するプロセスを処理します。独自のカスタムタイプの変換サポートを提供する場合は、次の例に示すように、独自の ConversionService Bean インスタンスを提供できます。

Java
@Configuration
public class AppConfig {

    @Bean
    public ConversionService conversionService() {
        DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();
        conversionService.addConverter(new MyCustomConverter());
        return conversionService;
    }
}
Kotlin
@Configuration
class AppConfig {

    @Bean
    fun conversionService(): ConversionService {
            return DefaultFormattingConversionService().apply {
            addConverter(MyCustomConverter())
        }
    }
}

@Value に SpEL 式が含まれる場合、次の例に示すように、値は実行時に動的に計算されます。

Java
@Component
public class MovieRecommender {

    private final String catalog;

    public MovieRecommender(@Value("#{systemProperties['user.catalog'] + 'Catalog' }") String catalog) {
        this.catalog = catalog;
    }
}
Kotlin
@Component
class MovieRecommender(
    @Value("#{systemProperties['user.catalog'] + 'Catalog' }") private val catalog: String)

SpEL は、より複雑なデータ構造の使用も可能にします。

Java
@Component
public class MovieRecommender {

    private final Map<String, Integer> countOfMoviesPerCatalog;

    public MovieRecommender(
            @Value("#{{'Thriller': 100, 'Comedy': 300}}") Map<String, Integer> countOfMoviesPerCatalog) {
        this.countOfMoviesPerCatalog = countOfMoviesPerCatalog;
    }
}
Kotlin
@Component
class MovieRecommender(
    @Value("#{{'Thriller': 100, 'Comedy': 300}}") private val countOfMoviesPerCatalog: Map<String, Int>)

1.9.9. @PostConstruct および @PreDestroy の使用

CommonAnnotationBeanPostProcessor は、@Resource アノテーションだけでなく、JSR-250 ライフサイクルアノテーション javax.annotation.PostConstruct および javax.annotation.PreDestroy も認識します。Spring 2.5, で導入されたこれらのアノテーションのサポートは、初期化コールバックと破棄コール バックで説明されているライフサイクルコールバックメカニズムの代替手段を提供します。CommonAnnotationBeanPostProcessor が Spring ApplicationContext 内に登録されている場合、これらのアノテーションの 1 つを運ぶメソッドは、対応する Spring ライフサイクルインターフェースメソッドまたは明示的に宣言されたコールバックメソッドと同じライフサイクルのポイントで呼び出されます。次の例では、キャッシュは初期化時に事前入力され、破棄時にクリアされます。

Java
public class CachingMovieLister {

    @PostConstruct
    public void populateMovieCache() {
        // populates the movie cache upon initialization...
    }

    @PreDestroy
    public void clearMovieCache() {
        // clears the movie cache upon destruction...
    }
}
Kotlin
class CachingMovieLister {

    @PostConstruct
    fun populateMovieCache() {
        // populates the movie cache upon initialization...
    }

    @PreDestroy
    fun clearMovieCache() {
        // clears the movie cache upon destruction...
    }
}

さまざまなライフサイクルメカニズムを組み合わせた効果の詳細については、ライフサイクルメカニズムの組み合わせを参照してください。

@Resource と同様に、@PostConstruct および @PreDestroy アノテーション型は JDK 6 〜 8 の標準 Java ライブラリの一部でしたが、javax.annotation パッケージ全体は JDK 9 のコア Java モジュールから分離され、最終的に JDK 11 で削除されました。javax.annotation-api アーティファクトは、Maven セントラルを介して取得する必要があります。他のライブラリと同様に、アプリケーションのクラスパスに追加するだけです。

1.10. クラスパススキャンと管理対象コンポーネント

この章のほとんどの例では、XML を使用して、Spring コンテナー内の各 BeanDefinition を生成する構成メタデータを指定します。前のセクション(アノテーションベースのコンテナー構成)は、ソースレベルのアノテーションを使用して多くの構成メタデータを提供する方法を示しています。ただし、これらの例でも、「ベース」の Bean 定義は XML ファイルで明示的に定義されていますが、アノテーションは依存性注入のみを駆動します。このセクションでは、クラスパスをスキャンして候補コンポーネントを暗黙的に検出するオプションについて説明します。候補コンポーネントは、フィルター条件に一致するクラスであり、対応する Bean 定義がコンテナーに登録されています。これにより、Bean 登録を実行するために XML を使用する必要がなくなります。代わりに、アノテーション(たとえば、@Component)、AspectJ 型式、または独自のカスタムフィルター条件を使用して、コンテナーに登録された Bean 定義を持つクラスを選択できます。

Spring 3.0, 以降、Spring JavaConfig プロジェクトによって提供される多くの機能は、コア Spring Framework の一部です。これにより、従来の XML ファイルではなく Java を使用して Bean を定義できます。これらの新機能の使用例については、@Configuration@Bean@Import および @DependsOn アノテーションを参照してください。

1.10.1. @Component およびその他のステレオタイプアノテーション

@Repository アノテーションは、リポジトリ(データアクセスオブジェクトまたは DAO とも呼ばれる)のロールまたはステレオタイプを満たすクラスのマーカーです。このマーカーの用途には、例外変換に従って、例外の自動変換があります。

Spring は、さらにステレオタイプのアノテーション @Component@Service および @Controller を提供します。@Component は、Spring 管理コンポーネントの一般的なステレオタイプです。@Repository@Service、および @Controller は、より具体的な使用例(それぞれ、永続層、サービス層、プレゼンテーション層)向けの @Component の特殊化です。コンポーネントクラスに @Component でアノテーションを付けることができますが、代わりに @Repository@Service、または @Controller でアノテーションを付けることにより、クラスはツールによる処理やアスペクトとの関連付けにより適しています。例:これらのステレオタイプアノテーションは、ポイントカットの理想的なターゲットになります。@Repository@Service および @Controller は、Spring Framework の将来のリリースで追加のセマンティクスを運ぶこともできます。サービスレイヤーに @Component と @Service のどちらを使用するかを選択する場合は、明らかに @Service の方が適しています。同様に、前述のように、@Repository は、永続層の自動例外変換のマーカーとして既にサポートされています。

1.10.2. メタアノテーションと合成アノテーションの使用

Spring が提供する多くのアノテーションは、独自のコードでメタアノテーションとして使用できます。メタアノテーションは、別のアノテーションに適用できるアノテーションです。例:以前の @Service アノテーションは、次の例に示すように、@Component でメタアノテーションされています。

Java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component (1)
public @interface Service {

    // ...
}
1Component により、@Service は @Component と同じ方法で処理されます。
Kotlin
@Target(AnnotationTarget.TYPE)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
@Component (1)
annotation class Service {

    // ...
}
1Component により、@Service は @Component と同じ方法で処理されます。

メタアノテーションを組み合わせて、「構成されたアノテーション」を作成することもできます。例:Spring MVC からの @RestController アノテーションは、@Controller と @ResponseBody で構成されています。

さらに、構成されたアノテーションは、オプションでメタアノテーションから属性を再宣言してカスタマイズを許可できます。これは、メタアノテーションの属性のサブセットのみを公開する場合に特に役立ちます。例:Spring の @SessionScope アノテーションは、スコープ名を session にハードコードしますが、proxyMode のカスタマイズは引き続き可能です。次のリストは、SessionScope アノテーションの定義を示しています。

Java
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Scope(WebApplicationContext.SCOPE_SESSION)
public @interface SessionScope {

    /**
     * Alias for {@link Scope#proxyMode}.
     * <p>Defaults to {@link ScopedProxyMode#TARGET_CLASS}.
     */
    @AliasFor(annotation = Scope.class)
    ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;

}
Kotlin
@Target(AnnotationTarget.TYPE, AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
@Scope(WebApplicationContext.SCOPE_SESSION)
annotation class SessionScope(
        @get:AliasFor(annotation = Scope::class)
        val proxyMode: ScopedProxyMode = ScopedProxyMode.TARGET_CLASS
)

その後、proxyMode を次のように宣言せずに @SessionScope を使用できます。

Java
@Service
@SessionScope
public class SessionScopedService {
    // ...
}
Kotlin
@Service
@SessionScope
class SessionScopedService {
    // ...
}

次の例に示すように、proxyMode の値をオーバーライドすることもできます。

Java
@Service
@SessionScope(proxyMode = ScopedProxyMode.INTERFACES)
public class SessionScopedUserService implements UserService {
    // ...
}
Kotlin
@Service
@SessionScope(proxyMode = ScopedProxyMode.INTERFACES)
class SessionScopedUserService : UserService {
    // ...
}

詳細については、Spring アノテーションプログラミングモデル : GitHub (英語) wiki ページを参照してください。

1.10.3. クラスの自動検出と Bean 定義の登録

Spring は、ステレオタイプ化されたクラスを自動的に検出し、対応する BeanDefinition インスタンスを ApplicationContext に登録できます。例:次の 2 つのクラスは、このような自動検出の対象です。

Java
@Service
public class SimpleMovieLister {

    private MovieFinder movieFinder;

    public SimpleMovieLister(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
}
Kotlin
@Service
class SimpleMovieLister(private val movieFinder: MovieFinder)
Java
@Repository
public class JpaMovieFinder implements MovieFinder {
    // implementation elided for clarity
}
Kotlin
@Repository
class JpaMovieFinder : MovieFinder {
    // implementation elided for clarity
}

これらのクラスを自動検出して対応する Bean を登録するには、@ComponentScan を @Configuration クラスに追加する必要があります。basePackages 属性は 2 つのクラスの共通の親パッケージです。(または、各クラスの親パッケージを含むコンマ区切り、セミコロン区切り、またはスペース区切りのリストを指定できます。)

Java
@Configuration
@ComponentScan(basePackages = "org.example")
public class AppConfig  {
    // ...
}
Kotlin
@Configuration
@ComponentScan(basePackages = ["org.example"])
class AppConfig  {
    // ...
}
簡潔にするために、前の例ではアノテーションの value 属性(つまり @ComponentScan("org.example"))を使用できます。

次の代替方法では XML を使用します。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="org.example"/>

</beans>
<context:component-scan> を使用すると、<context:annotation-config> の機能が暗黙的に有効になります。<context:component-scan> を使用する場合、通常 <context:annotation-config> 要素を含める必要はありません。

クラスパスパッケージをスキャンするには、対応するディレクトリエントリがクラスパスに存在する必要があります。Ant を使用して JAR をビルドする場合は、JAR タスクのファイルのみのスイッチをアクティブにしないでください。また、一部の環境では、セキュリティポリシーに基づいてクラスパスディレクトリが公開されない場合があります。たとえば、JDK 1.7.0_45 以降のスタンドアロンアプリ(マニフェストで 'Trusted-Library' の設定が必要です。https://stackoverflow.com/questions/19394570/java-jre-7u45-breaks-classloader-getresources (英語) を参照してください)。

JDK 9 のモジュールパス(Jigsaw)では、Spring のクラスパススキャンは通常期待どおりに機能します。ただし、コンポーネントクラスが module-info 記述子でエクスポートされていることを確認してください。Spring がクラスの非パブリックメンバーを呼び出すことが予想される場合は、それらが「開かれている」ことを確認してください(つまり、module-info 記述子で exports 宣言の代わりに opens 宣言を使用する)。

さらに、コンポーネントスキャン要素を使用すると、AutowiredAnnotationBeanPostProcessor と CommonAnnotationBeanPostProcessor の両方が暗黙的に含まれます。つまり、2 つのコンポーネントは自動的に検出され、相互に接続されます。すべて XML で提供される Bean 構成メタデータはありません。

false の値を持つ annotation-config 属性を含めることにより、AutowiredAnnotationBeanPostProcessor および CommonAnnotationBeanPostProcessor の登録を無効にできます。

1.10.4. フィルターを使用してスキャンをカスタマイズする

デフォルトでは、@Component@Repository@Service@Controller@Configuration でアノテーションが付けられたクラス、または @Component でアノテーションが付けられたカスタムアノテーションのみが検出された候補コンポーネントです。ただし、カスタムフィルターを適用することにより、この動作を変更および拡張できます。@ComponentScan アノテーションの includeFilters または excludeFilters 属性として(または XML 構成の <context:component-scan> 要素の <context:include-filter /> または <context:exclude-filter /> 子要素として)追加します。各フィルター要素には、type および expression 属性が必要です。次の表で、フィルタリングオプションについて説明します。

表 5: 型のフィルター
フィルタータイプ 式の例 説明

アノテーション (デフォルト)

org.example.SomeAnnotation

ターゲットコンポーネントの型レベルで存在またはメタ表示するアノテーション。

割り当て可能

org.example.SomeClass

ターゲットコンポーネントが割り当てられる(拡張または実装する)クラス(またはインターフェース)。

アスペクト

org.example..*Service+

ターゲットコンポーネントによって照合される AspectJ 型式。

正規表現

org\.example\.Default.*

ターゲットコンポーネントのクラス名と一致する正規表現。

カスタム

org.example.MyTypeFilter

org.springframework.core.type.TypeFilter インターフェースのカスタム実装。

次の例は、すべての @Repository アノテーションを無視し、代わりに「スタブ」リポジトリを使用する構成を示しています。

Java
@Configuration
@ComponentScan(basePackages = "org.example",
        includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),
        excludeFilters = @Filter(Repository.class))
public class AppConfig {
    ...
}
Kotlin
@Configuration
@ComponentScan(basePackages = "org.example",
        includeFilters = [Filter(type = FilterType.REGEX, pattern = [".*Stub.*Repository"])],
        excludeFilters = [Filter(Repository::class)])
class AppConfig {
    // ...
}

次のリストは、同等の XML を示しています。

<beans>
    <context:component-scan base-package="org.example">
        <context:include-filter type="regex"
                expression=".*Stub.*Repository"/>
        <context:exclude-filter type="annotation"
                expression="org.springframework.stereotype.Repository"/>
    </context:component-scan>
</beans>
アノテーションに useDefaultFilters=false を設定するか、use-default-filters="false" を <component-scan/> エレメントの属性として提供することにより、デフォルトのフィルターを無効にすることもできます。これにより、@Component@Repository@Service@Controller@RestController または @Configuration でアノテーション付けまたはメタアノテーション付けされたクラスの自動検出が事実上無効になります。

1.10.5. コンポーネント内での Bean メタデータの定義

Spring コンポーネントは、Bean 定義メタデータをコンテナーに提供することもできます。これは、@Configuration アノテーション付きクラス内で Bean メタデータを定義するために使用されるのと同じ @Bean アノテーションを使用して行うことができます。次の例は、その方法を示しています。

Java
@Component
public class FactoryMethodComponent {

    @Bean
    @Qualifier("public")
    public TestBean publicInstance() {
        return new TestBean("publicInstance");
    }

    public void doWork() {
        // Component method implementation omitted
    }
}
Kotlin
@Component
class FactoryMethodComponent {

    @Bean
    @Qualifier("public")
    fun publicInstance() = TestBean("publicInstance")

    fun doWork() {
        // Component method implementation omitted
    }
}

上記のクラスは、doWork() メソッドにアプリケーション固有のコードを持つ Spring コンポーネントです。ただし、メソッド publicInstance() を参照するファクトリメソッドを持つ Bean 定義も提供します。@Bean アノテーションは、ファクトリメソッドと、@Qualifier アノテーションを介した修飾子値などの他の Bean 定義プロパティを識別します。指定できる他のメソッドレベルのアノテーションは、@Scope@Lazy およびカスタム修飾子アノテーションです。

コンポーネントの初期化のロールに加えて、@Autowired または @Inject でマークされた注入ポイントに @Lazy アノテーションを配置することもできます。このコンテキストでは、遅延解決プロキシの挿入につながります。

前述のように、@Bean メソッドのオートワイヤーの追加サポートとともに、オートワイヤーフィールドとメソッドがサポートされています。次の例は、その方法を示しています。

Java
@Component
public class FactoryMethodComponent {

    private static int i;

    @Bean
    @Qualifier("public")
    public TestBean publicInstance() {
        return new TestBean("publicInstance");
    }

    // use of a custom qualifier and autowiring of method parameters
    @Bean
    protected TestBean protectedInstance(
            @Qualifier("public") TestBean spouse,
            @Value("#{privateInstance.age}") String country) {
        TestBean tb = new TestBean("protectedInstance", 1);
        tb.setSpouse(spouse);
        tb.setCountry(country);
        return tb;
    }

    @Bean
    private TestBean privateInstance() {
        return new TestBean("privateInstance", i++);
    }

    @Bean
    @RequestScope
    public TestBean requestScopedInstance() {
        return new TestBean("requestScopedInstance", 3);
    }
}
Kotlin
@Component
class FactoryMethodComponent {

    companion object {
        private var i: Int = 0
    }

    @Bean
    @Qualifier("public")
    fun publicInstance() = TestBean("publicInstance")

    // use of a custom qualifier and autowiring of method parameters
    @Bean
    protected fun protectedInstance(
            @Qualifier("public") spouse: TestBean,
            @Value("#{privateInstance.age}") country: String) = TestBean("protectedInstance", 1).apply {
        this.spouse = spouse
        this.country = country
    }

    @Bean
    private fun privateInstance() = TestBean("privateInstance", i++)

    @Bean
    @RequestScope
    fun requestScopedInstance() = TestBean("requestScopedInstance", 3)
}

この例では、String メソッドパラメーター country を、privateInstance という名前の別の Bean の age プロパティの値に自動接続します。Spring Expression Language エレメントは、表記 #{ <expression> } を介してプロパティの値を定義します。@Value アノテーションの場合、式テキストを解決するときに Bean 名を検索するように式リゾルバーが事前構成されています。

Spring Framework 4.3, 以降、タイプ InjectionPoint (またはそのより具象サブクラス: DependencyDescriptor)のファクトリメソッドパラメーターを宣言して、現在の Bean の作成をトリガーするリクエストしている注入ポイントにアクセスすることもできます。これは、Bean インスタンスの実際の作成にのみ適用され、既存のインスタンスの挿入には適用されないことに注意してください。結果として、この機能は、プロトタイプスコープの Bean に最も意味があります。他のスコープの場合、ファクトリメソッドは、指定されたスコープで新しい Bean インスタンスの作成をトリガーしたインジェクションポイントのみを確認します(たとえば、遅延シングルトン Bean の作成をトリガーした依存関係)。このようなシナリオでは、提供されている注入ポイントメタデータをセマンティックケアで使用できます。次の例は、InjectionPoint の使用方法を示しています。

Java
@Component
public class FactoryMethodComponent {

    @Bean @Scope("prototype")
    public TestBean prototypeInstance(InjectionPoint injectionPoint) {
        return new TestBean("prototypeInstance for " + injectionPoint.getMember());
    }
}
Kotlin
@Component
class FactoryMethodComponent {

    @Bean
    @Scope("prototype")
    fun prototypeInstance(injectionPoint: InjectionPoint) =
            TestBean("prototypeInstance for ${injectionPoint.member}")
}

通常の Spring コンポーネントの @Bean メソッドは、Spring @Configuration クラス内の対応する @Bean メソッドとは異なる方法で処理されます。違いは、@Component クラスがメソッドとフィールドの呼び出しをインターセプトするために CGLIB で拡張されていないことです。CGLIB プロキシは、@Configuration クラスの @Bean メソッド内のメソッドまたはフィールドを呼び出すことにより、コラボレーションオブジェクトへの Bean メタデータ参照を作成する手段です。このようなメソッドは、通常の Java セマンティクスで呼び出されるのではなく、@Bean メソッドのプログラム呼び出しで他の Bean を参照する場合でも、Spring Bean の通常のライフサイクル管理とプロキシを提供するためにコンテナーを通過します。対照的に、プレーン @Component クラス内の @Bean メソッドでメソッドまたはフィールドを呼び出すには、標準の Java セマンティクスがあり、特別な CGLIB 処理やその他の制約は適用されません。

@Bean メソッドを static として宣言すると、含む構成クラスをインスタンスとして作成せずに呼び出すことができます。これは、ポストプロセッサ Bean(たとえば、タイプ BeanFactoryPostProcessor または BeanPostProcessor)を定義するときに特に意味があります。そのような Bean は、コンテナーライフサイクルの初期に初期化され、その時点で構成の他の部分をトリガーしないようにする必要があるためです

静的な @Bean メソッドの呼び出しは、技術的な制限のため、コンテナーによって(このセクションで前述したように) @Configuration クラス内でさえもインターセプトされません。CGLIB サブクラス化は、非静的メソッドのみをオーバーライドできます。その結果、別の @Bean メソッドへの直接呼び出しには標準の Java セマンティクスがあり、その結果、独立したインスタンスがファクトリメソッド自体から直接返されます。

@Bean メソッドの Java 言語の可視性は、Spring のコンテナーで生成される Bean 定義に直接的な影響を与えません。@Configuration 以外のクラスに収まると思われるように、またどこにいても静的メソッドに適合するように、ファクトリメソッドを自由に宣言できます。ただし、@Configuration クラスの通常の @Bean メソッドはオーバーライド可能である必要があります。つまり、private または final として宣言してはなりません。

@Bean メソッドは、特定のコンポーネントまたは構成クラスの基本クラス、およびコンポーネントまたは構成クラスによって実装されるインターフェースで宣言された Java 8 デフォルトメソッドでも検出されます。これにより、Spring 4.2. 以降の Java 8 のデフォルトメソッドを介して複数の継承が可能な場合でも、複雑な構成の配置を柔軟に構成できます。

最後に、実行時に利用可能な依存関係に応じて使用する複数のファクトリメソッドの配置として、単一のクラスが同じ Bean に対して複数の @Bean メソッドを保持する場合があります。これは、他の構成シナリオで「最も貪欲な」コンストラクターまたはファクトリメソッドを選択する場合と同じアルゴリズムです。コンテナーが複数の @Autowired コンストラクターを選択する方法に類似して、充足可能な依存関係の数が最も多いバリアントが構築時に選択されます。

1.10.6. 自動検出されたコンポーネントの命名

コンポーネントがスキャンプロセスの一部として自動検出されると、その Bean 名は、そのスキャナーが認識している BeanNameGenerator 戦略によって生成されます。デフォルトでは、名前 value を含む Spring ステレオタイプアノテーション(@Component@Repository@Service および @Controller)は、その名前を対応する Bean 定義に提供します。

そのようなアノテーションに名前 value が含まれていない場合、またはその他の検出されたコンポーネント(カスタムフィルターによって検出されたコンポーネントなど)の場合、デフォルトの Bean 名前ジェネレーターは大文字ではない非修飾クラス名を返します。例:次のコンポーネントクラスが検出された場合、名前は myMovieLister および movieFinderImpl になります。

Java
@Service("myMovieLister")
public class SimpleMovieLister {
    // ...
}
Kotlin
@Service("myMovieLister")
class SimpleMovieLister {
    // ...
}
Java
@Repository
public class MovieFinderImpl implements MovieFinder {
    // ...
}
Kotlin
@Repository
class MovieFinderImpl : MovieFinder {
    // ...
}

デフォルトの Bean 命名戦略に依存したくない場合は、カスタムの Bean 命名戦略を提供できます。まず、BeanNameGenerator (Javadoc) インターフェースを実装し、デフォルトの引数なしのコンストラクターを必ず含めてください。次に、以下のアノテーションの例と Bean 定義が示すように、スキャナーの構成時に完全修飾クラス名を指定します。

修飾されていない同じクラス名を持つ複数の自動検出されたコンポーネント(つまり、同じ名前であるが異なるパッケージにあるクラス)が原因で名前の競合が発生した場合、デフォルトで完全修飾クラス名になる BeanNameGenerator を構成する必要がある場合があります。生成された Bean 名。Spring Framework 5.2.3, 以降、パッケージ org.springframework.context.annotation にある FullyQualifiedAnnotationBeanNameGenerator をそのような目的で使用できます。
Java
@Configuration
@ComponentScan(basePackages = "org.example", nameGenerator = MyNameGenerator.class)
public class AppConfig {
    // ...
}
Kotlin
@Configuration
@ComponentScan(basePackages = ["org.example"], nameGenerator = MyNameGenerator::class)
class AppConfig {
    // ...
}
<beans>
    <context:component-scan base-package="org.example"
        name-generator="org.example.MyNameGenerator" />
</beans>

一般的な規則として、他のコンポーネントが明示的に参照している場合は、アノテーションで名前を指定することを検討してください。一方、コンテナーが接続を担当する場合は、自動生成された名前で十分です。

1.10.7. 自動検出されたコンポーネントのスコープを提供する

一般に Spring 管理コンポーネントと同様に、自動検出されたコンポーネントのデフォルトで最も一般的なスコープは singleton です。ただし、@Scope アノテーションで指定できる別のスコープが必要になる場合があります。次の例に示すように、アノテーション内でスコープの名前を指定できます。

Java
@Scope("prototype")
@Repository
public class MovieFinderImpl implements MovieFinder {
    // ...
}
Kotlin
@Scope("prototype")
@Repository
class MovieFinderImpl : MovieFinder {
    // ...
}
@Scope アノテーションは、具体的な Bean クラス(アノテーション付きコンポーネントの場合)またはファクトリメソッド(@Bean メソッドの場合)でのみ内省されます。XML Bean 定義とは対照的に、Bean 定義の継承という概念はなく、クラスレベルの継承階層はメタデータの目的には関係ありません。

Spring コンテキストでの「リクエスト」や「セッション」などの Web 固有のスコープの詳細については、リクエスト、セッション、アプリケーション、WebSocket スコープを参照してください。これらのスコープの事前作成アノテーションと同様に、Spring のメタアノテーションアプローチを使用して、独自のスコープアノテーションを作成することもできます。たとえば、@Scope("prototype") でメタアノテーションされたカスタムアノテーションは、カスタムスコーププロキシモードを宣言することもできます。

アノテーションベースのアプローチに依存するのではなく、スコープ解決のカスタム戦略を提供するために、ScopeMetadataResolver (Javadoc) インターフェースを実装できます。デフォルトの引数なしのコンストラクターを必ず含めてください。次に、以下のアノテーションと Bean 定義の例に示すように、スキャナーの構成時に完全修飾クラス名を指定できます。
Java
@Configuration
@ComponentScan(basePackages = "org.example", scopeResolver = MyScopeResolver.class)
public class AppConfig {
    // ...
}
Kotlin
@Configuration
@ComponentScan(basePackages = ["org.example"], scopeResolver = MyScopeResolver::class)
class AppConfig {
    // ...
}
<beans>
    <context:component-scan base-package="org.example" scope-resolver="org.example.MyScopeResolver"/>
</beans>

特定のシングルトン以外のスコープを使用する場合、スコープ付きオブジェクトのプロキシを生成する必要がある場合があります。推論は依存関係としてのスコープ Bean で説明されています。このために、component-scan 要素で scoped-proxy 属性を使用できます。可能な 3 つの値は、nointerfaces および targetClass です。例:次の構成により、標準の JDK 動的プロキシが生成されます。

Java
@Configuration
@ComponentScan(basePackages = "org.example", scopedProxy = ScopedProxyMode.INTERFACES)
public class AppConfig {
    // ...
}
Kotlin
@Configuration
@ComponentScan(basePackages = ["org.example"], scopedProxy = ScopedProxyMode.INTERFACES)
class AppConfig {
    // ...
}
<beans>
    <context:component-scan base-package="org.example" scoped-proxy="interfaces"/>
</beans>

1.10.8. アノテーション付きの修飾子メタデータの提供

@Qualifier アノテーションについては、修飾子を使用したアノテーションベースのオートワイヤーの微調整で説明しています。そのセクションの例では、@Qualifier アノテーションとカスタム修飾子アノテーションを使用して、オートワイヤーの候補を解決するときにきめ細かな制御を提供します。これらの例は XML Bean 定義に基づいているため、XML の bean 要素の qualifier または meta 子要素を使用して、候補の Bean 定義に修飾子メタデータが提供されます。コンポーネントの自動検出をクラスパススキャンに依存している場合、候補クラスの型レベルのアノテーションを修飾子メタデータに提供できます。次の 3 つの例は、この手法を示しています。

Java
@Component
@Qualifier("Action")
public class ActionMovieCatalog implements MovieCatalog {
    // ...
}
Kotlin
@Component
@Qualifier("Action")
class ActionMovieCatalog : MovieCatalog
Java
@Component
@Genre("Action")
public class ActionMovieCatalog implements MovieCatalog {
    // ...
}
Kotlin
@Component
@Genre("Action")
class ActionMovieCatalog : MovieCatalog {
    // ...
}
Java
@Component
@Offline
public class CachingMovieCatalog implements MovieCatalog {
    // ...
}
Kotlin
@Component
@Offline
class CachingMovieCatalog : MovieCatalog {
    // ...
}
ほとんどのアノテーションベースの代替方法と同様に、XML を使用すると、同じタイプの複数の Bean が修飾子メタデータのバリエーションを提供できる一方で、アノテーションメタデータはクラス定義自体にバインドされることに留意してください。クラスごとではなくインスタンス。

1.10.9. 候補コンポーネントのインデックスの生成

クラスパススキャンは非常に高速ですが、コンパイル時に候補の静的リストを作成することにより、大規模アプリケーションの起動パフォーマンスを向上させることができます。このモードでは、コンポーネントスキャンの対象となるすべてのモジュールがこのメカニズムを使用する必要があります。

既存の @ComponentScan または <context:component-scan ディレクティブは、特定のパッケージの候補をスキャンするコンテキストをリクエストするために、そのままである必要があります。ApplicationContext は、そのようなインデックスを検出すると、クラスパスをスキャンするのではなく、自動的にインデックスを使用します。

インデックスを生成するには、コンポーネントスキャンディレクティブのターゲットであるコンポーネントを含む各モジュールに追加の依存関係を追加します。次の例は、Maven でこれを行う方法を示しています。

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-indexer</artifactId>
        <version>5.3.0-SNAPSHOT</version>
        <optional>true</optional>
    </dependency>
</dependencies>

Gradle 4.5 以前では、次の例に示すように、compileOnly 構成で依存関係を宣言する必要があります。

dependencies {
    compileOnly "org.springframework:spring-context-indexer:5.3.0-SNAPSHOT"
}

Gradle 4.6 以降では、次の例に示すように、annotationProcessor 構成で依存関係を宣言する必要があります。

dependencies {
    annotationProcessor "org.springframework:spring-context-indexer:{spring-version}"
}

そのプロセスは、jar ファイルに含まれる META-INF/spring.components ファイルを生成します。

IDE でこのモードを使用する場合は、spring-context-indexer をアノテーションプロセッサとして登録して、候補コンポーネントが更新されたときにインデックスが最新であることを確認する必要があります。
META-INF/spring.components がクラスパスで見つかると、インデックスは自動的に有効になります。一部のライブラリ(またはユースケース)でインデックスが部分的に使用可能であるが、アプリケーション全体で構築できなかった場合、spring.index.ignore を true に設定することにより、通常のクラスパス配置にフォールバックできます(インデックスが存在しないかのように)。システムプロパティ、またはクラスパスのルートにある spring.properties ファイル。

1.11. JSR 330 標準アノテーションの使用

Spring 3.0, Spring から、Spring は JSR-330 標準アノテーション(依存性注入)をサポートしています。これらのアノテーションは、Spring アノテーションと同じ方法でスキャンされます。使用するには、クラスパスに関連する jar が必要です。

Maven を使用する場合、javax.inject アーティファクトは標準 Maven リポジトリ(https://repo1.maven.org/maven2/javax/inject/javax.inject/1/ (英語) )で使用可能です。ファイル pom.xml に次の依存関係を追加できます。

<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>

1.11.1. @Inject および @Named による依存性注入

@Autowired の代わりに、次のように @javax.inject.Inject を使用できます。

Java
import javax.inject.Inject;

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Inject
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    public void listMovies() {
        this.movieFinder.findMovies(...);
        // ...
    }
}
Kotlin
import javax.inject.Inject

class SimpleMovieLister {

    @Inject
    lateinit var movieFinder: MovieFinder


    fun listMovies() {
        movieFinder.findMovies(...)
        // ...
    }
}

@Autowired と同様に、フィールドレベル、メソッドレベル、コンストラクター引数レベルで @Inject を使用できます。さらに、インジェクションポイントを Provider として宣言して、Provider.get() 呼び出しを介して、より短いスコープの Bean へのオンデマンドアクセスまたは他の Bean への遅延アクセスを許可することができます。次の例は、前述の例の変形を示しています。

Java
import javax.inject.Inject;
import javax.inject.Provider;

public class SimpleMovieLister {

    private Provider<MovieFinder> movieFinder;

    @Inject
    public void setMovieFinder(Provider<MovieFinder> movieFinder) {
        this.movieFinder = movieFinder;
    }

    public void listMovies() {
        this.movieFinder.get().findMovies(...);
        // ...
    }
}
Kotlin
import javax.inject.Inject

class SimpleMovieLister {

    @Inject
    lateinit var movieFinder: MovieFinder


    fun listMovies() {
        movieFinder.findMovies(...)
        // ...
    }
}

挿入する依存関係に修飾名を使用する場合は、次の例に示すように、@Named アノテーションを使用する必要があります。

Java
import javax.inject.Inject;
import javax.inject.Named;

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Inject
    public void setMovieFinder(@Named("main") MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...
}
Kotlin
import javax.inject.Inject
import javax.inject.Named

class SimpleMovieLister {

    private lateinit var movieFinder: MovieFinder

    @Inject
    fun setMovieFinder(@Named("main") movieFinder: MovieFinder) {
        this.movieFinder = movieFinder
    }

    // ...
}

@Autowired と同様に、@Inject は java.util.Optional または @Nullable でも使用できます。@Inject には required 属性がないため、これはさらに適切です。次の例のペアは、@Inject と @Nullable の使用方法を示しています。

public class SimpleMovieLister {

    @Inject
    public void setMovieFinder(Optional<MovieFinder> movieFinder) {
        // ...
    }
}
Java
public class SimpleMovieLister {

    @Inject
    public void setMovieFinder(@Nullable MovieFinder movieFinder) {
        // ...
    }
}
Kotlin
class SimpleMovieLister {

    @Inject
    var movieFinder: MovieFinder? = null
}

1.11.2. @Named および @ManagedBean@Component アノテーションの標準的な同等物

@Component の代わりに、次の例に示すように、@javax.inject.Named または javax.annotation.ManagedBean を使用できます。

Java
import javax.inject.Inject;
import javax.inject.Named;

@Named("movieListener")  // @ManagedBean("movieListener") could be used as well
public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Inject
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...
}
Kotlin
import javax.inject.Inject
import javax.inject.Named

@Named("movieListener")  // @ManagedBean("movieListener") could be used as well
class SimpleMovieLister {

    @Inject
    lateinit var movieFinder: MovieFinder

    // ...
}

コンポーネントの名前を指定せずに @Component を使用することは非常に一般的です。@Named は、次の例に示すように、同様の方法で使用できます。

Java
import javax.inject.Inject;
import javax.inject.Named;

@Named
public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Inject
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...
}
Kotlin
import javax.inject.Inject
import javax.inject.Named

@Named
class SimpleMovieLister {

    @Inject
    lateinit var movieFinder: MovieFinder

    // ...
}

@Named または @ManagedBean を使用する場合、次の例に示すように、Spring アノテーションを使用する場合とまったく同じ方法でコンポーネントスキャンを使用できます。

Java
@Configuration
@ComponentScan(basePackages = "org.example")
public class AppConfig  {
    // ...
}
Kotlin
@Configuration
@ComponentScan(basePackages = ["org.example"])
class AppConfig  {
    // ...
}
@Component とは対照的に、JSR-330 @Named および JSR-250 ManagedBean アノテーションは作成できません。カスタムコンポーネントアノテーションを作成するには、Spring のステレオタイプモデルを使用する必要があります。

1.11.3. JSR-330 標準アノテーションの制限

標準のアノテーションを使用する場合、次の表に示すように、いくつかの重要な機能が利用できないことを知っておく必要があります。

表 6: Spring コンポーネントモデル要素と JSR-330 バリアント
Springjavax.inject.*javax.inject の制限 / コメント

@Autowired

@Inject

@Inject には 'required' 属性がありません。代わりに Java 8 の Optional で使用できます。

@Component

@Named/@ManagedBean

JSR-330 は構成可能なモデルを提供せず、名前付きコンポーネントを識別する方法のみを提供します。

@Scope("singleton" )

@Singleton

JSR-330 のデフォルトのスコープは Spring の prototype のようなものですただし、Spring の一般的なデフォルトとの一貫性を保つため、Spring コンテナーで宣言された JSR-330 Bean はデフォルトで singleton です。singleton 以外のスコープを使用するには、Spring の @Scope アノテーションを使用する必要があります。javax.inject は、@Scope: Oracle (英語) アノテーションも提供します。それにもかかわらず、これはあなた自身のアノテーションを作成するためにのみ使用されることを意図しています。

@Qualifier

@Qualifier/@Named

javax.inject.Qualifier は、カスタム修飾子を作成するためのメタアノテーションです。具体的な String 修飾子(値を持つ Spring の @Qualifier など)は、javax.inject.Named を介して関連付けることができます。

@Value

-

同等のものはありません

@Required

-

同等のものはありません

@Lazy

-

同等のものはありません

ObjectFactory

プロバイダー

javax.inject.Provider は、Spring の ObjectFactory の直接的な代替手段であり、get() メソッド名が短くなっています。また、Spring の @Autowired と組み合わせて、またはアノテーションなしのコンストラクターと setter メソッドと組み合わせて使用することもできます。

1.12. Java ベースのコンテナー構成

このセクションでは、Java コードでアノテーションを使用して Spring コンテナーを構成する方法について説明します。次のトピックが含まれます。

1.12.1. 基本概念 : @Bean および @Configuration

Spring の新しい Java 構成サポートの中心的な成果物は、@Configuration アノテーション付きクラスと @Bean アノテーション付きメソッドです。

@Bean アノテーションは、メソッドが Spring IoC コンテナーによって管理される新しいオブジェクトをインスタンス化、構成、初期化することを示すために使用されます。Spring の <beans/> XML 構成に精通している人にとっては、@Bean アノテーションは <bean/> 要素と同じロールを果たします。@Bean アノテーション付きメソッドは、任意の Spring @Component で使用できます。ただし、これらはほとんどの場合 @Configuration Bean で使用されます。

クラスに @Configuration のアノテーションを付けると、その主な目的が Bean 定義のソースであることを示します。さらに、@Configuration クラスでは、同じクラス内の他の @Bean メソッドを呼び出すことにより、Bean 間の依存関係を定義できます。最も単純な @Configuration クラスは次のようになります。

Java
@Configuration
public class AppConfig {

    @Bean
    public MyService myService() {
        return new MyServiceImpl();
    }
}
Kotlin
@Configuration
class AppConfig {

    @Bean
    fun myService(): MyService {
        return MyServiceImpl()
    }
}

上記の AppConfig クラスは、次の Spring <beans/> XML と同等です。

<beans>
    <bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>
フル @Configuration vs「ライト」@Bean モード?

@Bean メソッドが @Configuration アノテーションが付けられていないクラス内で宣言されている場合、それらは「ライト」モードで処理されていると呼ばれます。@Component または単純な古いクラスで宣言された Bean メソッドは、「ライト」と見なされ、包含クラスと @Bean メソッドの主な目的は、一種のボーナスです。例:サービスコンポーネントは、適用可能な各コンポーネントクラスで追加の @Bean メソッドを使用して、管理ビューをコンテナーに公開できます。このようなシナリオでは、@Bean メソッドは汎用ファクトリメソッドメカニズムです。

完全な @Configuration とは異なり、ライト @Bean メソッドは Bean 間の依存関係を宣言できません。代わりに、含まれるコンポーネントの内部状態と、オプションで、宣言できる引数を操作します。このような @Bean メソッドは、他の @Bean メソッドを呼び出さないでください。そのような各メソッドは、文字通り特定の Bean 参照のファクトリメソッドであり、特別なランタイムセマンティクスはありません。ここでのプラスの副作用は、実行時に CGLIB サブクラス化を適用する必要がないことです。そのため、クラス設計に関して制限はありません(つまり、包含クラスは final などになります)。

一般的なシナリオでは、@Bean メソッドは @Configuration クラス内で宣言され、「フル」モードが常に使用されるようにし、メソッド間の参照がコンテナーのライフサイクル管理にリダイレクトされるようにします。これにより、同じ @Bean メソッドが通常の Java 呼び出しを介して誤って呼び出されることを防ぎます。これにより、「ライト」モードでの操作時に追跡が困難な微妙なバグを減らすことができます。

@Bean および @Configuration アノテーションについては、次のセクションで詳しく説明します。ただし、最初に、Java ベースの構成を使用して Spring コンテナーを作成するさまざまな方法について説明します。

1.12.2. AnnotationConfigApplicationContext を使用して Spring コンテナーをインスタンス化する

次のセクションでは、Spring 3.0. で導入された Spring の AnnotationConfigApplicationContext について説明します。この用途の広い ApplicationContext 実装は、入力として @Configuration クラスだけでなく、プレーンな @Component クラスおよび JSR-330 メタデータでアノテーションが付けられたクラスも受け入れることができます。

@Configuration クラスが入力として提供される場合、@Configuration クラス自体が Bean 定義として登録され、クラス内で宣言されたすべての @Bean メソッドも Bean 定義として登録されます。

@Component および JSR-330 クラスが提供されると、それらは Bean 定義として登録され、@Autowired または @Inject などの DI メタデータが必要に応じてそれらのクラス内で使用されると想定されます。

シンプルな構造

ClassPathXmlApplicationContext のインスタンス化時に Spring XML ファイルが入力として使用されるのとほぼ同じ方法で、AnnotationConfigApplicationContext のインスタンス化時に @Configuration クラスを入力として使用できます。これにより、次の例に示すように、Spring コンテナーを完全に XML なしで使用できます。

Java
public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
    MyService myService = ctx.getBean(MyService.class);
    myService.doStuff();
}
Kotlin
import org.springframework.beans.factory.getBean

fun main() {
    val ctx = AnnotationConfigApplicationContext(AppConfig::class.java)
    val myService = ctx.getBean<MyService>()
    myService.doStuff()
}

前述のように、AnnotationConfigApplicationContext は @Configuration クラスでのみ機能することに限定されません。次の例に示すように、@Component または JSR-330 アノテーション付きクラスは、コンストラクターへの入力として提供できます。

Java
public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(MyServiceImpl.class, Dependency1.class, Dependency2.class);
    MyService myService = ctx.getBean(MyService.class);
    myService.doStuff();
}
Kotlin
import org.springframework.beans.factory.getBean

fun main() {
    val ctx = AnnotationConfigApplicationContext(MyServiceImpl::class.java, Dependency1::class.java, Dependency2::class.java)
    val myService = ctx.getBean<MyService>()
    myService.doStuff()
}

上記の例では、MyServiceImplDependency1 および Dependency2 が @Autowired などの Spring 依存性注入アノテーションを使用すると想定しています。

register(Class<?>…​) を使用してプログラムでコンテナーを構築する

引数なしのコンストラクターを使用して AnnotationConfigApplicationContext をインスタンス化し、register() メソッドを使用して構成することができます。このアプローチは、AnnotationConfigApplicationContext をプログラムで構築するときに特に役立ちます。次の例は、その方法を示しています。

Java
public static void main(String[] args) {
    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
    ctx.register(AppConfig.class, OtherConfig.class);
    ctx.register(AdditionalConfig.class);
    ctx.refresh();
    MyService myService = ctx.getBean(MyService.class);
    myService.doStuff();
}
Kotlin
import org.springframework.beans.factory.getBean

fun main() {
    val ctx = AnnotationConfigApplicationContext()
    ctx.register(AppConfig::class.java, OtherConfig::class.java)
    ctx.register(AdditionalConfig::class.java)
    ctx.refresh()
    val myService = ctx.getBean<MyService>()
    myService.doStuff()
}
scan(String…​) を使用したコンポーネントスキャンの有効化

コンポーネントスキャンを有効にするには、次のように @Configuration クラスにアノテーションを付けます。

Java
@Configuration
@ComponentScan(basePackages = "com.acme") (1)
public class AppConfig  {
    ...
}
1 このアノテーションにより、コンポーネントのスキャンが可能になります。
Kotlin
@Configuration
@ComponentScan(basePackages = ["com.acme"]) (1)
class AppConfig  {
    // ...
}
1 このアノテーションにより、コンポーネントのスキャンが可能になります。

経験豊富な Spring ユーザーは、次の例に示すように、Spring の context: 名前空間に相当する XML 宣言に精通している可能性があります。

<beans>
    <context:component-scan base-package="com.acme"/>
</beans>

前の例では、com.acme パッケージがスキャンされて @Component アノテーション付きクラスが検索され、それらのクラスはコンテナー内で Spring Bean 定義として登録されます。次の例に示すように、AnnotationConfigApplicationContext は scan(String…​) メソッドを公開して、同じコンポーネントスキャン機能を可能にします。

Java
public static void main(String[] args) {
    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
    ctx.scan("com.acme");
    ctx.refresh();
    MyService myService = ctx.getBean(MyService.class);
}
Kotlin
fun main() {
    val ctx = AnnotationConfigApplicationContext()
    ctx.scan("com.acme")
    ctx.refresh()
    val myService = ctx.getBean<MyService>()
}
@Configuration クラスは @Component でメタアノテーションされているため、コンポーネントスキャンの候補であることに注意してください。上記の例では、AppConfig が com.acme パッケージ(またはその任意のパッケージ)内で宣言されていると仮定すると、scan() の呼び出し中に取得されます。refresh() では、そのすべての @Bean メソッドが処理され、コンテナー内で Bean 定義として登録されます。
AnnotationConfigWebApplicationContext を使用した Web アプリケーションのサポート

AnnotationConfigApplicationContext の WebApplicationContext バリアントは、AnnotationConfigWebApplicationContext で使用可能です。Spring ContextLoaderListener サーブレットリスナー、Spring MVC DispatcherServlet などを構成するときに、この実装を使用できます。次の web.xml スニペットは、典型的な Spring MVC Web アプリケーションを構成します(contextClass context-param および init-param の使用に注意してください)。

<web-app>
    <!-- Configure ContextLoaderListener to use AnnotationConfigWebApplicationContext
        instead of the default XmlWebApplicationContext -->
    <context-param>
        <param-name>contextClass</param-name>
        <param-value>
            org.springframework.web.context.support.AnnotationConfigWebApplicationContext
        </param-value>
    </context-param>

    <!-- Configuration locations must consist of one or more comma- or space-delimited
        fully-qualified @Configuration classes. Fully-qualified packages may also be
        specified for component-scanning -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>com.acme.AppConfig</param-value>
    </context-param>

    <!-- Bootstrap the root application context as usual using ContextLoaderListener -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- Declare a Spring MVC DispatcherServlet as usual -->
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- Configure DispatcherServlet to use AnnotationConfigWebApplicationContext
            instead of the default XmlWebApplicationContext -->
        <init-param>
            <param-name>contextClass</param-name>
            <param-value>
                org.springframework.web.context.support.AnnotationConfigWebApplicationContext
            </param-value>
        </init-param>
        <!-- Again, config locations must consist of one or more comma- or space-delimited
            and fully-qualified @Configuration classes -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>com.acme.web.MvcConfig</param-value>
        </init-param>
    </servlet>

    <!-- map all requests for /app/* to the dispatcher servlet -->
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/app/*</url-pattern>
    </servlet-mapping>
</web-app>

1.12.3. @Bean アノテーションの使用

@Bean はメソッドレベルのアノテーションであり、XML <bean/> 要素に直接的に似ています。アノテーションは、次のような <bean/> が提供する name 属性の一部をサポートしています。* init-method * destroy-method * autowiring *

@Configuration アノテーション付きクラスまたは @Component アノテーション付きクラスで @Bean アノテーションを使用できます。

Bean の宣言

Bean を宣言するために、@Bean アノテーションを使用してメソッドにアノテーションを付けることができます。このメソッドを使用して、メソッドの戻り値として指定されたタイプの ApplicationContext 内に Bean 定義を登録します。デフォルトでは、Bean 名はメソッド名と同じです。次の例は、@Bean メソッドの宣言を示しています。

Java
@Configuration
public class AppConfig {

    @Bean
    public TransferServiceImpl transferService() {
        return new TransferServiceImpl();
    }
}
Kotlin
@Configuration
class AppConfig {

    @Bean
    fun transferService() = TransferServiceImpl()
}

上記の構成は、次の Spring XML とまったく同じです。

<beans>
    <bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>

次のテキストイメージに示すように、両方の宣言により、transferService という名前の Bean が ApplicationContext で使用可能になり、タイプ TransferServiceImpl のオブジェクトインスタンスにバインドされます。

transferService -> com.acme.TransferServiceImpl

次の例に示すように、@Bean メソッドをインターフェース(または基本クラス)戻り値の型で宣言することもできます。

Java
@Configuration
public class AppConfig {

    @Bean
    public TransferService transferService() {
        return new TransferServiceImpl();
    }
}
Kotlin
@Configuration
class AppConfig {

    @Bean
    fun transferService(): TransferService {
        return TransferServiceImpl()
    }
}

ただし、これにより、事前型予測の可視性が指定されたインターフェース型(TransferService)に制限されます。その後、コンテナーに一度だけ認識されるフルタイプ(TransferServiceImpl)で、影響を受けるシングルトン Bean がインスタンス化されました。非遅延シングルトン Bean は宣言順序に従ってインスタンス化されるため、別のコンポーネントが非宣言型(@Autowired TransferServiceImpl など、transferService Bean がインスタンス化されると解決する @Autowired TransferServiceImpl など))。

宣言されたサービスインターフェースで型を一貫して参照する場合、@Bean 戻り型はその設計決定に安全に参加できます。ただし、複数のインターフェースを実装するコンポーネント、または実装タイプで潜在的に参照されるコンポーネントの場合、可能な限り最も具体的な戻り値のタイプを宣言する方が安全です(少なくとも Bean を参照する注入ポイントで要求される特定の)。
Bean の依存関係

@Bean アノテーション付きメソッドには、その Bean のビルドに必要な依存関係を記述する任意の数のパラメーターを含めることができます。たとえば、TransferService が AccountRepository を必要とする場合、次の例に示すように、メソッドパラメーターを使用してその依存関係を具体化できます。

Java
@Configuration
public class AppConfig {

    @Bean
    public TransferService transferService(AccountRepository accountRepository) {
        return new TransferServiceImpl(accountRepository);
    }
}
Kotlin
@Configuration
class AppConfig {

    @Bean
    fun transferService(accountRepository: AccountRepository): TransferService {
        return TransferServiceImpl(accountRepository)
    }
}

解決メカニズムは、コンストラクターベースの依存性注入とほとんど同じです。詳細については、関連するセクションを参照してください。

ライフサイクルコールバックの受信

@Bean アノテーションで定義されたクラスは、通常のライフサイクルコールバックをサポートし、JSR-250 の @PostConstruct および @PreDestroy アノテーションを使用できます。詳細については、JSR-250 アノテーションを参照してください。

通常の Spring ライフサイクルコールバックも完全にサポートされています。Bean が InitializingBeanDisposableBean または Lifecycle を実装している場合、それぞれのメソッドはコンテナーによって呼び出されます。

*Aware インターフェースの標準セット(BeanFactoryAwareBeanNameAwareMessageSourceAwareApplicationContextAware など)も完全にサポートされています。

@Bean アノテーションは、Spring XML の init-method 属性や destroy-method 属性と同様に、任意の初期化および破棄コールバックメソッドを bean 要素に指定することをサポートします。次に例を示します。

Java
public class BeanOne {

    public void init() {
        // initialization logic
    }
}

public class BeanTwo {

    public void cleanup() {
        // destruction logic
    }
}

@Configuration
public class AppConfig {

    @Bean(initMethod = "init")
    public BeanOne beanOne() {
        return new BeanOne();
    }

    @Bean(destroyMethod = "cleanup")
    public BeanTwo beanTwo() {
        return new BeanTwo();
    }
}
Kotlin
class BeanOne {

    fun init() {
        // initialization logic
    }
}

class BeanTwo {

    fun cleanup() {
        // destruction logic
    }
}

@Configuration
class AppConfig {

    @Bean(initMethod = "init")
    fun beanOne() = BeanOne()

    @Bean(destroyMethod = "cleanup")
    fun beanTwo() = BeanTwo()
}

デフォルトでは、パブリック close または shutdown メソッドを持つ Java 構成で定義された Bean は、破棄コールバックに自動的に参加します。パブリック close または shutdown メソッドがあり、コンテナーのシャットダウン時に呼び出されたくない場合は、@Bean(destroyMethod="") を Bean 定義に追加して、デフォルトの (inferred) モードを無効にすることができます。

そのライフサイクルはアプリケーションの外部で管理されるため、JNDI で取得するリソースに対してデフォルトでそれを行うことができます。特に、Java EE アプリケーションサーバーでは問題があることが知られているため、必ず DataSource に対して実行してください。

次の例は、DataSource の自動破棄コールバックを防ぐ方法を示しています。

Java
@Bean(destroyMethod="")
public DataSource dataSource() throws NamingException {
    return (DataSource) jndiTemplate.lookup("MyDS");
}
Kotlin
@Bean(destroyMethod = "")
fun dataSource(): DataSource {
    return jndiTemplate.lookup("MyDS") as DataSource
}

また、@Bean メソッドでは、通常、Spring の JndiTemplate または JndiLocatorDelegate ヘルパーまたはストレート JNDI InitialContext を使用して、JndiObjectFactoryBean バリアント(実際のターゲットの代わりに戻り型を FactoryBean タイプとして宣言することを強制します) ここで提供されたリソースを参照しようとする他の @Bean メソッドで相互参照呼び出しに使用することを困難にします)。

前の例の上の例の BeanOne の場合、次の例に示すように、構築中に init() メソッドを直接呼び出すことも同様に有効です。

Java
@Configuration
public class AppConfig {

    @Bean
    public BeanOne beanOne() {
        BeanOne beanOne = new BeanOne();
        beanOne.init();
        return beanOne;
    }

    // ...
}
Kotlin
@Configuration
class AppConfig {

    @Bean
    fun beanOne() = BeanOne().apply {
        init()
    }

    // ...
}
Java で直接作業する場合、オブジェクトを使用して好きなことを行うことができ、常にコンテナーのライフサイクルに依存する必要はありません。
Bean スコープの指定

Spring には @Scope アノテーションが含まれているため、Bean のスコープを指定できます。

@Scope アノテーションの使用

@Bean アノテーションで定義された Bean に特定のスコープが必要であることを指定できます。Bean スコープセクションで指定された標準スコープのいずれかを使用できます。

デフォルトのスコープは singleton ですが、次の例に示すように、@Scope アノテーションでこれをオーバーライドできます。

Java
@Configuration
public class MyConfiguration {

    @Bean
    @Scope("prototype")
    public Encryptor encryptor() {
        // ...
    }
}
Kotlin
@Configuration
class MyConfiguration {

    @Bean
    @Scope("prototype")
    fun encryptor(): Encryptor {
        // ...
    }
}
@Scope および scoped-proxy

Spring は、スコープ化されたプロキシを介してスコープ化された依存関係を操作する便利な方法を提供します。XML 構成を使用する場合、このようなプロキシを作成する最も簡単な方法は <aop:scoped-proxy/> 要素です。Java で @Scope アノテーションを使用して Bean を構成すると、proxyMode 属性で同等のサポートが提供されます。デフォルトはプロキシなし(ScopedProxyMode.NO)ですが、ScopedProxyMode.TARGET_CLASS または ScopedProxyMode.INTERFACES を指定できます。

Java を使用して、XML リファレンスドキュメント(スコーププロキシを参照)のスコーププロキシの例を @Bean に移植すると、次のようになります。

Java
// an HTTP Session-scoped bean exposed as a proxy
@Bean
@SessionScope
public UserPreferences userPreferences() {
    return new UserPreferences();
}

@Bean
public Service userService() {
    UserService service = new SimpleUserService();
    // a reference to the proxied userPreferences bean
    service.setUserPreferences(userPreferences());
    return service;
}
Kotlin
// an HTTP Session-scoped bean exposed as a proxy
@Bean
@SessionScope
fun userPreferences() = UserPreferences()

@Bean
fun userService(): Service {
    return SimpleUserService().apply {
        // a reference to the proxied userPreferences bean
        setUserPreferences(userPreferences()
    }
}
Bean 命名のカスタマイズ

デフォルトでは、構成クラスは @Bean メソッドの名前を結果の Bean の名前として使用します。ただし、次の例に示すように、この機能は name 属性でオーバーライドできます。

Java
@Configuration
public class AppConfig {

    @Bean(name = "myThing")
    public Thing thing() {
        return new Thing();
    }
}
Kotlin
@Configuration
class AppConfig {

    @Bean("myThing")
    fun thing() = Thing()
}
Bean エイリアス

Bean の命名で説明したように、単一の Bean に複数の名前(別名 Bean エイリアス)を付けることが望ましい場合があります。@Bean アノテーションの name 属性は、この目的のためにストリング配列を受け入れます。次の例は、Bean に複数のエイリアスを設定する方法を示しています。

Java
@Configuration
public class AppConfig {

    @Bean({"dataSource", "subsystemA-dataSource", "subsystemB-dataSource"})
    public DataSource dataSource() {
        // instantiate, configure and return DataSource bean...
    }
}
Kotlin
@Configuration
class AppConfig {

    @Bean("dataSource", "subsystemA-dataSource", "subsystemB-dataSource")
    fun dataSource(): DataSource {
        // instantiate, configure and return DataSource bean...
    }
}
Bean の説明

時々、Bean のより詳細なテキスト記述を提供すると役立つことがあります。これは、監視目的で Bean が(おそらく JMX を介して)公開されている場合に特に役立ちます。

@Bean に説明を追加するには、次の例に示すように、@Description (Javadoc) アノテーションを使用できます。

Java
@Configuration
public class AppConfig {

    @Bean
    @Description("Provides a basic example of a bean")
    public Thing thing() {
        return new Thing();
    }
}
Kotlin
@Configuration
class AppConfig {

    @Bean
    @Description("Provides a basic example of a bean")
    fun thing() = Thing()
}

1.12.4. @Configuration アノテーションを使用する

@Configuration は、オブジェクトが Bean 定義のソースであることを示すクラスレベルのアノテーションです。@Configuration クラスは、パブリック @Bean アノテーション付きメソッドを介して Bean を宣言します。@Configuration クラスの @Bean メソッドの呼び出しは、Bean 間の依存関係の定義にも使用できます。一般的な導入については、基本概念 : @Bean および @Configuration を参照してください。

Bean 間の依存関係の注入

Bean が相互に依存関係を持っている場合、その依存関係の表現は、次の例に示すように、ある Bean メソッドが別の Bean メソッドを呼び出すのと同じくらい簡単です。

Java
@Configuration
public class AppConfig {

    @Bean
    public BeanOne beanOne() {
        return new BeanOne(beanTwo());
    }

    @Bean
    public BeanTwo beanTwo() {
        return new BeanTwo();
    }
}
Kotlin
@Configuration
class AppConfig {

    @Bean
    fun beanOne() = BeanOne(beanTwo())

    @Bean
    fun beanTwo() = BeanTwo()
}

上記の例では、beanOne はコンストラクター注入を通じて beanTwo への参照を受け取ります。

Bean 間依存関係を宣言するこの方法は、@Bean メソッドが @Configuration クラス内で宣言されている場合にのみ機能します。プレーンな @Component クラスを使用して Bean 間の依存関係を宣言することはできません。
ルックアップメソッドインジェクション

前述したように、ルックアップメソッドインジェクションは高度な機能であり、めったに使用しないでください。これは、シングルトンスコープの Bean がプロトタイプスコープの Bean に依存している場合に役立ちます。このタイプの構成に Java を使用すると、このパターンを実装するための自然な手段が提供されます。次の例は、ルックアップメソッドインジェクションの使用方法を示しています。

Java
public abstract class CommandManager {
    public Object process(Object commandState) {
        // grab a new instance of the appropriate Command interface
        Command command = createCommand();
        // set the state on the (hopefully brand new) Command instance
        command.setState(commandState);
        return command.execute();
    }

    // okay... but where is the implementation of this method?
    protected abstract Command createCommand();
}
Kotlin
abstract class CommandManager {
    fun process(commandState: Any): Any {
        // grab a new instance of the appropriate Command interface
        val command = createCommand()
        // set the state on the (hopefully brand new) Command instance
        command.setState(commandState)
        return command.execute()
    }

    // okay... but where is the implementation of this method?
    protected abstract fun createCommand(): Command
}

Java 構成を使用することにより、CommandManager のサブクラスを作成できます。ここで、抽象 createCommand() メソッドは、新しい(プロトタイプ)コマンドオブジェクトを検索するようにオーバーライドされます。次の例は、その方法を示しています。

Java
@Bean
@Scope("prototype")
public AsyncCommand asyncCommand() {
    AsyncCommand command = new AsyncCommand();
    // inject dependencies here as required
    return command;
}

@Bean
public CommandManager commandManager() {
    // return new anonymous implementation of CommandManager with createCommand()
    // overridden to return a new prototype Command object
    return new CommandManager() {
        protected Command createCommand() {
            return asyncCommand();
        }
    }
}
Kotlin
@Bean
@Scope("prototype")
fun asyncCommand(): AsyncCommand {
    val command = AsyncCommand()
    // inject dependencies here as required
    return command
}

@Bean
fun commandManager(): CommandManager {
    // return new anonymous implementation of CommandManager with createCommand()
    // overridden to return a new prototype Command object
    return object : CommandManager() {
        override fun createCommand(): Command {
            return asyncCommand()
        }
    }
}
Java ベースの構成が内部的に機能する方法に関する詳細情報

@Bean アノテーション付きメソッドが 2 回呼び出されることを示す次の例を検討してください。

Java
@Configuration
public class AppConfig {

    @Bean
    public ClientService clientService1() {
        ClientServiceImpl clientService = new ClientServiceImpl();
        clientService.setClientDao(clientDao());
        return clientService;
    }

    @Bean
    public ClientService clientService2() {
        ClientServiceImpl clientService = new ClientServiceImpl();
        clientService.setClientDao(clientDao());
        return clientService;
    }

    @Bean
    public ClientDao clientDao() {
        return new ClientDaoImpl();
    }
}
Kotlin
@Configuration
class AppConfig {

    @Bean
    fun clientService1(): ClientService {
        return ClientServiceImpl().apply {
            clientDao = clientDao()
        }
    }

    @Bean
    fun clientService2(): ClientService {
        return ClientServiceImpl().apply {
            clientDao = clientDao()
        }
    }

    @Bean
    fun clientDao(): ClientDao {
        return ClientDaoImpl()
    }
}

clientDao() は clientService1() で 1 回、clientService2() で 1 回呼び出されています。このメソッドは ClientDaoImpl の新しいインスタンスを作成して返すため、通常は 2 つのインスタンス(各サービスに 1 つ)が必要です。それは間違いなく問題になるでしょう:Spring では、インスタンス化された Bean はデフォルトで singleton スコープを持ちます。これが魔法の出番です。すべての @Configuration クラスは、起動時に CGLIB でサブクラス化されます。サブクラスでは、子メソッドは、親メソッドを呼び出して新しいインスタンスを作成する前に、キャッシュされた(スコープされた)Bean のコンテナーを最初にチェックします。

動作は、Bean の範囲に応じて異なる場合があります。ここでシングルトンについて話しています。

Spring 3.2, 以降、CGLIB クラスは org.springframework.cglib に再パッケージ化され、Spring-core JAR 内に直接含まれているため、CGLIB をクラスパスに追加する必要はなくなりました。

CGLIB は起動時に動的に機能を追加するため、いくつかの制限があります。特に、構成クラスは final であってはなりません。ただし、4.3 の時点で、@Autowired の使用やデフォルトのインジェクション用の単一の非デフォルトコンストラクター宣言など、コンストラクターは構成クラスで許可されます。

CGLIB による制限を回避する場合は、@Configuration 以外のクラス(たとえば、代わりにプレーンな @Component クラス)で @Bean メソッドを宣言することを検討してください。@Bean メソッド間のクロスメソッド呼び出しはインターセプトされないため、コンストラクターまたはメソッドレベルでの依存性注入のみに依存する必要があります。

1.12.5. Java ベースの構成の作成

Spring の Java ベースの構成機能により、アノテーションを作成でき、構成の複雑さを軽減できます。

@Import アノテーションの使用

<import/> 要素が Spring XML ファイル内で構成のモジュール化を支援するために使用されるのと同じように、@Import アノテーションは、次の例が示すように、別の構成クラスから @Bean 定義をロードすることを可能にします。

Java
@Configuration
public class ConfigA {

    @Bean
    public A a() {
        return new A();
    }
}

@Configuration
@Import(ConfigA.class)
public class ConfigB {

    @Bean
    public B b() {
        return new B();
    }
}
Kotlin
@Configuration
class ConfigA {

    @Bean
    fun a() = A()
}

@Configuration
@Import(ConfigA::class)
class ConfigB {

    @Bean
    fun b() = B()
}

次の例に示すように、コンテキストをインスタンス化するときに ConfigA.class と ConfigB.class の両方を指定する必要はなく、ConfigB のみを明示的に指定する必要があります。

Java
public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigB.class);

    // now both beans A and B will be available...
    A a = ctx.getBean(A.class);
    B b = ctx.getBean(B.class);
}
Kotlin
import org.springframework.beans.factory.getBean

fun main() {
    val ctx = AnnotationConfigApplicationContext(ConfigB::class.java)

    // now both beans A and B will be available...
    val a = ctx.getBean<A>()
    val b = ctx.getBean<B>()
}

このアプローチは、構築中に多数の @Configuration クラスを覚えておく必要がなく、1 つのクラスのみを処理する必要があるため、コンテナーのインスタンス化を簡素化します。

Spring Framework 4.2, 以降、@Import は、AnnotationConfigApplicationContext.register メソッドと同様に、通常のコンポーネントクラスへの参照もサポートします。これは、すべてのコンポーネントを明示的に定義するエントリポイントとしていくつかの構成クラスを使用することにより、コンポーネントのスキャンを避けたい場合に特に役立ちます。
インポートされた @Bean 定義への依存関係の注入

上記の例は機能しますが、単純です。最も実用的なシナリオでは、Bean は構成クラス間で相互に依存関係を持っています。XML を使用する場合、これは課題ではありません。コンパイラーが関与していないため、ref="someBean" を宣言し、Spring を信頼してコンテナーの初期化中にそれを解決できるためです。@Configuration クラスを使用する場合、Java コンパイラは構成モデルに制約を課します。そのため、他の Bean への参照は有効な Java 構文でなければなりません。

幸いなことに、この問題を解決するのは簡単です。すでに説明したように、@Bean メソッドには、Bean 依存関係を記述する任意の数のパラメーターを含めることができます。それぞれが他のクラスで宣言された Bean に依存する、いくつかの @Configuration クラスを使用した次のより現実的なシナリオを検討してください。

Java
@Configuration
public class ServiceConfig {

    @Bean
    public TransferService transferService(AccountRepository accountRepository) {
        return new TransferServiceImpl(accountRepository);
    }
}

@Configuration
public class RepositoryConfig {

    @Bean
    public AccountRepository accountRepository(DataSource dataSource) {
        return new JdbcAccountRepository(dataSource);
    }
}

@Configuration
@Import({ServiceConfig.class, RepositoryConfig.class})
public class SystemTestConfig {

    @Bean
    public DataSource dataSource() {
        // return new DataSource
    }
}

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
    // everything wires up across configuration classes...
    TransferService transferService = ctx.getBean(TransferService.class);
    transferService.transfer(100.00, "A123", "C456");
}
Kotlin
import org.springframework.beans.factory.getBean

@Configuration
class ServiceConfig {

    @Bean
    fun transferService(accountRepository: AccountRepository): TransferService {
        return TransferServiceImpl(accountRepository)
    }
}

@Configuration
class RepositoryConfig {

    @Bean
    fun accountRepository(dataSource: DataSource): AccountRepository {
        return JdbcAccountRepository(dataSource)
    }
}

@Configuration
@Import(ServiceConfig::class, RepositoryConfig::class)
class SystemTestConfig {

    @Bean
    fun dataSource(): DataSource {
        // return new DataSource
    }
}


fun main() {
    val ctx = AnnotationConfigApplicationContext(SystemTestConfig::class.java)
    // everything wires up across configuration classes...
    val transferService = ctx.getBean<TransferService>()
    transferService.transfer(100.00, "A123", "C456")
}

同じ結果を達成する別の方法があります。@Configuration クラスは、最終的にはコンテナー内の別の Bean にすぎないことに注意してください。これは、@Autowired および @Value インジェクション、および他の Bean と同じ他の機能を利用できることを意味します。

その方法で注入する依存関係が、最も単純な種類のものであることを確認してください。@Configuration クラスはコンテキストの初期化中に非常に早い段階で処理され、このように依存関係を強制的に注入すると、予期しない初期初期化が発生する可能性があります。前の例のように、可能な場合はいつでもパラメーターベースの注入に頼ってください。

また、@Bean を介した BeanPostProcessor および BeanFactoryPostProcessor の定義には特に注意してください。通常、これらは static @Bean メソッドとして宣言する必要があり、含まれる構成クラスのインスタンス化をトリガーしません。そうでない場合、@Autowired および @Value は、AutowiredAnnotationBeanPostProcessor (Javadoc) よりも前に Bean インスタンスとして作成できるため、構成クラス自体では機能しない場合があります。

次の例は、ある Bean を別の Bean にオートワイヤーする方法を示しています。

Java
@Configuration
public class ServiceConfig {

    @Autowired
    private AccountRepository accountRepository;

    @Bean
    public TransferService transferService() {
        return new TransferServiceImpl(accountRepository);
    }
}

@Configuration
public class RepositoryConfig {

    private final DataSource dataSource;

    public RepositoryConfig(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @Bean
    public AccountRepository accountRepository() {
        return new JdbcAccountRepository(dataSource);
    }
}

@Configuration
@Import({ServiceConfig.class, RepositoryConfig.class})
public class SystemTestConfig {

    @Bean
    public DataSource dataSource() {
        // return new DataSource
    }
}

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
    // everything wires up across configuration classes...
    TransferService transferService = ctx.getBean(TransferService.class);
    transferService.transfer(100.00, "A123", "C456");
}
Kotlin
import org.springframework.beans.factory.getBean

@Configuration
class ServiceConfig {

    @Autowired
    lateinit var accountRepository: AccountRepository

    @Bean
    fun transferService(): TransferService {
        return TransferServiceImpl(accountRepository)
    }
}

@Configuration
class RepositoryConfig(private val dataSource: DataSource) {

    @Bean
    fun accountRepository(): AccountRepository {
        return JdbcAccountRepository(dataSource)
    }
}

@Configuration
@Import(ServiceConfig::class, RepositoryConfig::class)
class SystemTestConfig {

    @Bean
    fun dataSource(): DataSource {
        // return new DataSource
    }
}

fun main() {
    val ctx = AnnotationConfigApplicationContext(SystemTestConfig::class.java)
    // everything wires up across configuration classes...
    val transferService = ctx.getBean<TransferService>()
    transferService.transfer(100.00, "A123", "C456")
}
@Configuration クラスでのコンストラクターの注入は、Spring Framework 4.3. 以降でのみサポートされています。ターゲット Bean がコンストラクターを 1 つだけ定義している場合は、@Autowired を指定する必要がないことにも注意してください。
ナビゲーションを容易にする完全修飾インポートされた Bean

上記のシナリオでは、@Autowired の使用はうまく機能し、望ましいモジュール性を提供しますが、オートワイヤーされた Bean 定義がどこで宣言されているかを正確に判断することは、まだいくらかあいまいです。例: ServiceConfig を見ている開発者として、どのようにして @Autowired AccountRepository Bean が宣言されているかを正確に知ることができますか?コードでは明示的ではありませんが、これで十分な場合があります。Eclipse Pleiades All in One (STS, Lombok 付属) または Eclipse 用 Spring Tools (英語) には、すべての接続方法を示すグラフをレンダリングできるツールが用意されていることに注意してください。また、Java IDE は AccountRepository 型のすべての宣言と使用を簡単に見つけ、その型を返す @Bean メソッドの場所をすばやく表示できます。

このあいまいさが許容されず、IDE 内で @Configuration クラスから別のクラスに直接移動したい場合は、構成クラス自体のオートワイヤーを検討してください。次の例は、その方法を示しています。

Java
@Configuration
public class ServiceConfig {

    @Autowired
    private RepositoryConfig repositoryConfig;

    @Bean
    public TransferService transferService() {
        // navigate 'through' the config class to the @Bean method!
        return new TransferServiceImpl(repositoryConfig.accountRepository());
    }
}
Kotlin
@Configuration
class ServiceConfig {

    @Autowired
    private lateinit var repositoryConfig: RepositoryConfig

    @Bean
    fun transferService(): TransferService {
        // navigate 'through' the config class to the @Bean method!
        return TransferServiceImpl(repositoryConfig.accountRepository())
    }
}

上記の状況では、AccountRepository が定義されている場所は完全に明示的です。ただし、ServiceConfig は RepositoryConfig と密結合しています。それがトレードオフです。この密結合は、インターフェースベースまたは抽象クラスベースの @Configuration クラスを使用することにより、ある程度緩和できます。次の例を考えてみましょう。

Java
@Configuration
public class ServiceConfig {

    @Autowired
    private RepositoryConfig repositoryConfig;

    @Bean
    public TransferService transferService() {
        return new TransferServiceImpl(repositoryConfig.accountRepository());
    }
}

@Configuration
public interface RepositoryConfig {

    @Bean
    AccountRepository accountRepository();
}

@Configuration
public class DefaultRepositoryConfig implements RepositoryConfig {

    @Bean
    public AccountRepository accountRepository() {
        return new JdbcAccountRepository(...);
    }
}

@Configuration
@Import({ServiceConfig.class, DefaultRepositoryConfig.class})  // import the concrete config!
public class SystemTestConfig {

    @Bean
    public DataSource dataSource() {
        // return DataSource
    }

}

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
    TransferService transferService = ctx.getBean(TransferService.class);
    transferService.transfer(100.00, "A123", "C456");
}
Kotlin
import org.springframework.beans.factory.getBean

@Configuration
class ServiceConfig {

    @Autowired
    private lateinit var repositoryConfig: RepositoryConfig

    @Bean
    fun transferService(): TransferService {
        return TransferServiceImpl(repositoryConfig.accountRepository())
    }
}

@Configuration
interface RepositoryConfig {

    @Bean
    fun accountRepository(): AccountRepository
}

@Configuration
class DefaultRepositoryConfig : RepositoryConfig {

    @Bean
    fun accountRepository(): AccountRepository {
        return JdbcAccountRepository(...)
    }
}

@Configuration
@Import(ServiceConfig::class, DefaultRepositoryConfig::class)  // import the concrete config!
class SystemTestConfig {

    @Bean
    fun dataSource(): DataSource {
        // return DataSource
    }

}

fun main() {
    val ctx = AnnotationConfigApplicationContext(SystemTestConfig::class.java)
    val transferService = ctx.getBean<TransferService>()
    transferService.transfer(100.00, "A123", "C456")
}

ServiceConfig は、具体的な DefaultRepositoryConfig に関して疎結合されており、組み込みの IDE ツールは依然として有用です。RepositoryConfig 実装の型階層を簡単に取得できます。このようにして、@Configuration クラスとその依存関係をナビゲートすることは、インターフェースベースのコードをナビゲートする通常のプロセスと変わりません。

特定の Bean の起動時の作成順序に影響を与えたい場合は、それらの一部を @Lazy (起動時ではなく初回アクセス時の作成用)または @DependsOn 特定の他の Bean(特定の他の Bean が現在の Bean の前に作成されていることを確認する)として宣言することを検討してください。後者の直接的な依存関係が意味するものを超えて)。
@Configuration クラスまたは @Bean メソッドを条件付きで含める

任意のシステム状態に基づいて、完全な @Configuration クラスまたは個々の @Bean メソッドを条件付きで有効または無効にすると便利な場合があります。この一般的な例の 1 つは、@Profile アノテーションを使用して、Spring Environment で特定のプロファイルが有効になっている場合にのみ Bean をアクティブ化することです(詳細については Bean 定義プロファイルを参照)。

@Profile アノテーションは、実際には @Conditional (Javadoc) と呼ばれるはるかに柔軟なアノテーションを使用して実装されます。@Conditional アノテーションは、@Bean が登録される前に確認する必要がある特定の org.springframework.context.annotation.Condition 実装を示します。

Condition インターフェースの実装は、true または false を返す matches(…​) メソッドを提供します。例:次のリストは、@Profile に使用される実際の Condition 実装を示しています。

Java
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
    // Read the @Profile annotation attributes
    MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
    if (attrs != null) {
        for (Object value : attrs.get("value")) {
            if (context.getEnvironment().acceptsProfiles(((String[]) value))) {
                return true;
            }
        }
        return false;
    }
    return true;
}
Kotlin
override fun matches(context: ConditionContext, metadata: AnnotatedTypeMetadata): Boolean {
    // Read the @Profile annotation attributes
    val attrs = metadata.getAllAnnotationAttributes(Profile::class.java.name)
    if (attrs != null) {
        for (value in attrs["value"]!!) {
            if (context.environment.acceptsProfiles(Profiles .of(*value as Array<String>))) {
                return true
            }
        }
        return false
    }
    return true
}

詳細については、@Conditional (Javadoc) javadoc を参照してください。

Java と XML 構成の組み合わせ

Spring の @Configuration クラスのサポートは、Spring XML の 100% を完全に置き換えるものではありません。Spring XML 名前空間などの一部の機能は、コンテナーを構成する理想的なメソッドのままです。XML が便利または必要な場合、選択肢があります。たとえば、ClassPathXmlApplicationContext を使用して「XML 中心」のメソッドでコンテナーをインスタンス化するか、AnnotationConfigApplicationContext と必要に応じて XML をインポートする @ImportResource アノテーション。

@Configuration クラスの XML 中心の使用

XML から Spring コンテナーをブートストラップし、@Configuration クラスをアドホックな方法で含めることが望ましい場合があります。例:Spring XML を使用する大規模な既存のコードベースでは、@Configuration クラスを必要に応じて作成し、既存の XML ファイルから含める方が簡単です。このセクションの後半では、この種の「XML 中心」の状況で @Configuration クラスを使用するためのオプションについて説明します。

@Configuration クラスをプレーンな Spring <bean/> 要素として宣言する

@Configuration クラスは、最終的にはコンテナー内の Bean 定義であることを忘れないでください。このシリーズの例では、AppConfig という名前の @Configuration クラスを作成し、<bean/> 定義として system-test-config.xml 内に組み込みます。<context:annotation-config/> がオンになっているため、コンテナーは @Configuration アノテーションを認識し、AppConfig で宣言された @Bean メソッドを適切に処理します。

次の例は、Java の通常の構成クラスを示しています。

Java
@Configuration
public class AppConfig {

    @Autowired
    private DataSource dataSource;

    @Bean
    public AccountRepository accountRepository() {
        return new JdbcAccountRepository(dataSource);
    }

    @Bean
    public TransferService transferService() {
        return new TransferService(accountRepository());
    }
}
Kotlin
@Configuration
class AppConfig {

    @Autowired
    private lateinit var dataSource: DataSource

    @Bean
    fun accountRepository(): AccountRepository {
        return JdbcAccountRepository(dataSource)
    }

    @Bean
    fun transferService() = TransferService(accountRepository())
}

次の例は、サンプル system-test-config.xml ファイルの一部を示しています。

<beans>
    <!-- enable processing of annotations such as @Autowired and @Configuration -->
    <context:annotation-config/>
    <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>

    <bean class="com.acme.AppConfig"/>

    <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
</beans>

次の例は、可能な jdbc.properties ファイルを示しています。

jdbc.url=jdbc:hsqldb:hsql://localhost/xdb
jdbc.username=sa
jdbc.password=
Java
public static void main(String[] args) {
    ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:/com/acme/system-test-config.xml");
    TransferService transferService = ctx.getBean(TransferService.class);
    // ...
}
Kotlin
fun main() {
    val ctx = ClassPathXmlApplicationContext("classpath:/com/acme/system-test-config.xml")
    val transferService = ctx.getBean<TransferService>()
    // ...
}
system-test-config.xml ファイルでは、AppConfig<bean/> は id 要素を宣言しません。そうすることは容認できますが、他の Bean がこれを参照することはなく、名前によってコンテナーから明示的にフェッチされる可能性は低いため、これは不要です。同様に、DataSource Bean はタイプごとにのみオートワイヤーされるため、明示的な Bean id は厳密には必要ありません。
<context:component-scan /> を使用して @Configuration クラスを取得する

@Configuration は @Component でメタアノテーションが付けられているため、@Configuration アノテーションが付けられたクラスは自動的にコンポーネントスキャンの候補になります。前の例で説明したのと同じシナリオを使用して、system-test-config.xml を再定義してコンポーネントスキャンを利用できます。この場合、<context:component-scan/> は同じ機能を有効にするため、明示的に <context:annotation-config/> を宣言する必要はありません。

次の例は、変更された system-test-config.xml ファイルを示しています。

<beans>
    <!-- picks up and registers AppConfig as a bean definition -->
    <context:component-scan base-package="com.acme"/>
    <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>

    <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
</beans>
@Configuration  @ImportResource での XML のクラス中心の使用

@Configuration クラスがコンテナーを構成するための主要なメカニズムであるアプリケーションでは、少なくともいくつかの XML を使用する必要があります。これらのシナリオでは、@ImportResource を使用して、必要なだけ XML を定義できます。そうすることで、コンテナーを構成する「Java 中心」のアプローチを実現し、XML を最小限に抑えます。次の例(構成クラス、Bean を定義する XML ファイル、プロパティファイル、main クラスを含む)は、@ImportResource アノテーションを使用して、必要に応じて XML を使用する「Java 中心」構成を実現する方法を示しています。

Java
@Configuration
@ImportResource("classpath:/com/acme/properties-config.xml")
public class AppConfig {

    @Value("${jdbc.url}")
    private String url;

    @Value("${jdbc.username}")
    private String username;

    @Value("${jdbc.password}")
    private String password;

    @Bean
    public DataSource dataSource() {
        return new DriverManagerDataSource(url, username, password);
    }
}
Kotlin
@Configuration
@ImportResource("classpath:/com/acme/properties-config.xml")
class AppConfig {

    @Value("\${jdbc.url}")
    private lateinit var url: String

    @Value("\${jdbc.username}")
    private lateinit var username: String

    @Value("\${jdbc.password}")
    private lateinit var password: String

    @Bean
    fun dataSource(): DataSource {
        return DriverManagerDataSource(url, username, password)
    }
}
properties-config.xml
<beans>
    <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
</beans>
jdbc.properties
jdbc.url=jdbc:hsqldb:hsql://localhost/xdb
jdbc.username=sa
jdbc.password=
Java
public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
    TransferService transferService = ctx.getBean(TransferService.class);
    // ...
}
Kotlin
import org.springframework.beans.factory.getBean

fun main() {
    val ctx = AnnotationConfigApplicationContext(AppConfig::class.java)
    val transferService = ctx.getBean<TransferService>()
    // ...
}

1.13. 環境の抽象化

Environment (Javadoc) インターフェースは、アプリケーション環境の 2 つの重要な側面(プロファイルプロパティ)をモデル化するコンテナーに統合された抽象化です。

プロファイルは、指定されたプロファイルがアクティブな場合にのみ、コンテナーに登録される Bean 定義の名前付きの論理グループです。Bean は、XML で定義されているかアノテーション付きで定義されているかに関係なく、プロファイルに割り当てることができます。プロファイルに関連する Environment オブジェクトのロールは、現在アクティブなプロファイル(存在する場合)、およびデフォルトでアクティブにするプロファイル(存在する場合)を決定することです。

プロパティは、ほとんどすべてのアプリケーションで重要なロールを果たし、プロパティファイル、JVM システムプロパティ、システム環境変数、JNDI、サーブレットコンテキストパラメーター、アドホック Properties オブジェクト、Map オブジェクトなど、さまざまなソースに由来する場合があります。プロパティに関連する Environment オブジェクトのロールは、プロパティソースを設定し、プロパティソースからプロパティを解決するための便利なサービスインターフェースをユーザーに提供することです。

1.13.1. Bean 定義プロファイル

Bean 定義プロファイルは、さまざまな環境でさまざまな Bean を登録できるコアコンテナーのメカニズムを提供します。「環境」という言葉は、ユーザーごとに異なることを意味し、この機能は次のような多くのユースケースに役立ちます。

  • 開発中のメモリ内データソースに対して作業することと、QA または本番環境で JNDI から同じデータソースを検索すること。

  • アプリケーションをパフォーマンス環境にデプロイするときにのみ、監視インフラストラクチャを登録します。

  • 顧客 A と顧客 B デプロイの Bean のカスタマイズされた実装を登録します。

DataSource を必要とする実用的なアプリケーションでの最初のユースケースを考えてください。テスト環境では、構成は次のようになります。

Java
@Bean
public DataSource dataSource() {
    return new EmbeddedDatabaseBuilder()
        .setType(EmbeddedDatabaseType.HSQL)
        .addScript("my-schema.sql")
        .addScript("my-test-data.sql")
        .build();
}
Kotlin
@Bean
fun dataSource(): DataSource {
    return EmbeddedDatabaseBuilder()
            .setType(EmbeddedDatabaseType.HSQL)
            .addScript("my-schema.sql")
            .addScript("my-test-data.sql")
            .build()
}

次に、アプリケーションのデータソースが本番アプリケーションサーバーの JNDI ディレクトリに登録されていると仮定して、このアプリケーションを QA または本番環境にデプロイする方法を検討します。dataSource Bean は、次のリストのようになりました。

Java
@Bean(destroyMethod="")
public DataSource dataSource() throws Exception {
    Context ctx = new InitialContext();
    return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
}
Kotlin
@Bean(destroyMethod = "")
fun dataSource(): DataSource {
    val ctx = InitialContext()
    return ctx.lookup("java:comp/env/jdbc/datasource") as DataSource
}

問題は、現在の環境に基づいて、これら 2 つのバリエーションの使用を切り替える方法です。時間の経過とともに、Spring ユーザーはこれを実現するためのいくつかの方法を考案しました。通常、システム環境変数と、環境変数の値に応じて正しい構成ファイルパスに解決される ${placeholder} トークンを含む XML <import/> ステートメントの組み合わせに依存しています。Bean 定義プロファイルは、この問題の解決策を提供するコアコンテナー機能です。

上記の環境固有の Bean 定義の例に示されているユースケースを一般化すると、特定のコンテキストでは特定の Bean 定義を登録する必要がありますが、他のコンテキストでは登録しません。状況 A で Bean 定義の特定のプロファイルを登録し、状況 B で別のプロファイルを登録すると言うことができます。このニーズを反映するように構成を更新することから始めます。

@Profile を使用する

@Profile (Javadoc) アノテーションを使用すると、1 つ以上の指定されたプロファイルがアクティブであるときに、コンポーネントが登録に適格であることを示すことができます。前述の例を使用して、dataSource 構成を次のように書き換えることができます。

Java
@Configuration
@Profile("development")
public class StandaloneDataConfig {

    @Bean
    public DataSource dataSource() {
        return new EmbeddedDatabaseBuilder()
            .setType(EmbeddedDatabaseType.HSQL)
            .addScript("classpath:com/bank/config/sql/schema.sql")
            .addScript("classpath:com/bank/config/sql/test-data.sql")
            .build();
    }
}
Kotlin
@Configuration
@Profile("development")
class StandaloneDataConfig {

    @Bean
    fun dataSource(): DataSource {
        return EmbeddedDatabaseBuilder()
                .setType(EmbeddedDatabaseType.HSQL)
                .addScript("classpath:com/bank/config/sql/schema.sql")
                .addScript("classpath:com/bank/config/sql/test-data.sql")
                .build()
    }
}
Java
@Configuration
@Profile("production")
public class JndiDataConfig {

    @Bean(destroyMethod="")
    public DataSource dataSource() throws Exception {
        Context ctx = new InitialContext();
        return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
    }
}
Kotlin
@Configuration
@Profile("production")
class JndiDataConfig {

    @Bean(destroyMethod = "")
    fun dataSource(): DataSource {
        val ctx = InitialContext()
        return ctx.lookup("java:comp/env/jdbc/datasource") as DataSource
    }
}
前述したように、@Bean メソッドでは、通常、Spring の JndiTemplate/JndiLocatorDelegate ヘルパーを使用するか、前述したように単純な JNDI InitialContext の使用方法を使用しますが、JndiObjectFactoryBean バリアントは使用しないため、戻り型を FactoryBean 型として宣言する必要があります。

プロファイル文字列には、単純なプロファイル名(たとえば、production)またはプロファイル式を含めることができます。プロファイル式を使用すると、より複雑なプロファイルロジックを表現できます(たとえば、production & us-east)。プロファイル式では次の演算子がサポートされています。

  • !: プロファイルの論理的な「not」

  • &: プロファイルの論理的な「and」

  • |: プロファイルの論理的な「or」

括弧を使用しないと、& 演算子と | 演算子を混在させることはできません。例: production & us-east | eu-central は有効な式ではありません。production & (us-east | eu-central) として表現する必要があります。

@Profile は、カスタム合成アノテーションを作成するためのメタアノテーションとして使用できます。次の例では、@Profile("production") のドロップイン置換として使用できるカスタム @Production アノテーションを定義しています。

Java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Profile("production")
public @interface Production {
}
Kotlin
@Target(AnnotationTarget.TYPE)
@Retention(AnnotationRetention.RUNTIME)
@Profile("production")
annotation class Production
@Configuration クラスが @Profile でマークされている場合、そのクラスに関連付けられている @Bean メソッドと @Import アノテーションはすべて、指定されたプロファイルの 1 つ以上がアクティブでない限りバイパスされます。@Component または @Configuration クラスが @Profile({"p1", "p2"}) でマークされている場合、そのクラスは、プロファイル「p1」または「p2」がアクティブ化されていない限り、登録または処理されません。特定のプロファイルの前に NOT 演算子(!)が付いている場合、アノテーション付き要素は、プロファイルがアクティブでない場合にのみ登録されます。例: @Profile({"p1", "!p2"}) を指定すると、プロファイル 'p1' がアクティブな場合、またはプロファイル 'p2' がアクティブでない場合に登録が行われます。

次の例に示すように、@Profile はメソッドレベルで宣言して、構成クラスの特定の Bean を 1 つだけ含めることもできます(たとえば、特定の Bean の代替バリアント)。

Java
@Configuration
public class AppConfig {

    @Bean("dataSource")
    @Profile("development") (1)
    public DataSource standaloneDataSource() {
        return new EmbeddedDatabaseBuilder()
            .setType(EmbeddedDatabaseType.HSQL)
            .addScript("classpath:com/bank/config/sql/schema.sql")
            .addScript("classpath:com/bank/config/sql/test-data.sql")
            .build();
    }

    @Bean("dataSource")
    @Profile("production") (2)
    public DataSource jndiDataSource() throws Exception {
        Context ctx = new InitialContext();
        return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
    }
}
1standaloneDataSource メソッドは、development プロファイルでのみ使用可能です。
2jndiDataSource メソッドは、production プロファイルでのみ使用可能です。
Kotlin
@Configuration
class AppConfig {

    @Bean("dataSource")
    @Profile("development") (1)
    fun standaloneDataSource(): DataSource {
        return EmbeddedDatabaseBuilder()
                .setType(EmbeddedDatabaseType.HSQL)
                .addScript("classpath:com/bank/config/sql/schema.sql")
                .addScript("classpath:com/bank/config/sql/test-data.sql")
                .build()
    }

    @Bean("dataSource")
    @Profile("production") (2)
    fun jndiDataSource() =
        InitialContext().lookup("java:comp/env/jdbc/datasource") as DataSource
}
1standaloneDataSource メソッドは、development プロファイルでのみ使用可能です。
2jndiDataSource メソッドは、production プロファイルでのみ使用可能です。

@Bean メソッドの @Profile では、特別なシナリオが適用される場合があります:同じ Java メソッド名のオーバーロードされた @Bean メソッドの場合(コンストラクターのオーバーロードに類似)、@Profile 条件はすべてのオーバーロードされたメソッドで一貫して宣言される必要があります。条件に矛盾がある場合、オーバーロードされたメソッドの最初の宣言の条件のみが重要です。@Profile を使用して、特定の引数シグニチャーが別のオーバーロードされたメソッドを選択することはできません。同じ Bean のすべてのファクトリメソッド間の解決は、作成時に Spring のコンストラクター解決アルゴリズムに従います。

異なるプロファイル条件で代替 Bean を定義する場合は、前の例に示すように、@Bean name 属性を使用して、同じ Bean 名を指す別個の Java メソッド名を使用します。引数のシグネチャーがすべて同じである場合(たとえば、すべてのバリアントに引数なしのファクトリメソッドがある場合)、これが最初に有効な Java クラスでそのような配置を表す唯一の方法です(1 つしか存在できないため)特定の名前と引数署名のメソッド)。

XML Bean 定義プロファイル

対応する XML は、<beans> 要素の profile 属性です。上記のサンプル構成は、次のように 2 つの XML ファイルに書き換えることができます。

<beans profile="development"
    xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:jdbc="http://www.springframework.org/schema/jdbc"
    xsi:schemaLocation="...">

    <jdbc:embedded-database id="dataSource">
        <jdbc:script location="classpath:com/bank/config/sql/schema.sql"/>
        <jdbc:script location="classpath:com/bank/config/sql/test-data.sql"/>
    </jdbc:embedded-database>
</beans>
<beans profile="production"
    xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:jee="http://www.springframework.org/schema/jee"
    xsi:schemaLocation="...">

    <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/>
</beans>

次の例に示すように、同じファイル内で <beans/> 要素を分割およびネストすることを回避することもできます。

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:jdbc="http://www.springframework.org/schema/jdbc"
    xmlns:jee="http://www.springframework.org/schema/jee"
    xsi:schemaLocation="...">

    <!-- other bean definitions -->

    <beans profile="development">
        <jdbc:embedded-database id="dataSource">
            <jdbc:script location="classpath:com/bank/config/sql/schema.sql"/>
            <jdbc:script location="classpath:com/bank/config/sql/test-data.sql"/>
        </jdbc:embedded-database>
    </beans>

    <beans profile="production">
        <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/>
    </beans>
</beans>

spring-bean.xsd は、そのような要素をファイルの最後の要素としてのみ許可するように制限されています。これにより、XML ファイルが乱雑になることなく、柔軟性が得られます。

対応する XML は、前述のプロファイル式をサポートしていません。ただし、! 演算子を使用してプロファイルを無効にすることは可能です。次の例に示すように、プロファイルをネストすることで論理的な「and」を適用することもできます。

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:jdbc="http://www.springframework.org/schema/jdbc"
    xmlns:jee="http://www.springframework.org/schema/jee"
    xsi:schemaLocation="...">

    <!-- other bean definitions -->

    <beans profile="production">
        <beans profile="us-east">
            <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/>
        </beans>
    </beans>
</beans>

上記の例では、production プロファイルと us-east プロファイルの両方がアクティブな場合、dataSource Bean が公開されています。

プロファイルの有効化

構成を更新したため、Spring にアクティブなプロファイルを指示する必要があります。サンプルアプリケーションをすぐに開始すると、コンテナーが dataSource という名前の Spring Bean を見つけられなかったため、NoSuchBeanDefinitionException がスローされます。

プロファイルのアクティブ化はいくつかの方法で実行できますが、最も簡単なのは、ApplicationContext を介して利用可能な Environment API に対してプログラムで実行することです。次の例は、その方法を示しています。

Java
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("development");
ctx.register(SomeConfig.class, StandaloneDataConfig.class, JndiDataConfig.class);
ctx.refresh();
Kotlin
val ctx = AnnotationConfigApplicationContext().apply {
    environment.setActiveProfiles("development")
    register(SomeConfig::class.java, StandaloneDataConfig::class.java, JndiDataConfig::class.java)
    refresh()
}

さらに、spring.profiles.active プロパティを介してプロファイルを宣言的にアクティブ化することもできます。spring.profiles.active プロパティは、システム環境変数、JVM システムプロパティ、web.xml のサーブレットコンテキストパラメーター、または JNDI のエントリとして指定できます(PropertySource の抽象化を参照)。統合テストでは、spring-test モジュールの @ActiveProfiles アノテーションを使用して、アクティブなプロファイルを宣言できます(環境プロファイルを使用したコンテキスト構成を参照)。

プロファイルは「どちらか一方」の命題ではないことに注意してください。複数のプロファイルを一度にアクティブ化できます。プログラムにより、String…​ 可変引数を受け入れる setActiveProfiles() メソッドに複数のプロファイル名を提供できます。次の例では、複数のプロファイルをアクティブにします。

Java
ctx.getEnvironment().setActiveProfiles("profile1", "profile2");
Kotlin
ctx.getEnvironment().setActiveProfiles("profile1", "profile2")

宣言的に、spring.profiles.active は、次の例に示すように、プロファイル名のコンマ区切りリストを受け入れる場合があります。

    -Dspring.profiles.active="profile1,profile2"
デフォルトプロファイル

デフォルトプロファイルは、デフォルトで有効になっているプロファイルを表します。次の例を考えてみましょう。

Java
@Configuration
@Profile("default")
public class DefaultDataConfig {

    @Bean
    public DataSource dataSource() {
        return new EmbeddedDatabaseBuilder()
            .setType(EmbeddedDatabaseType.HSQL)
            .addScript("classpath:com/bank/config/sql/schema.sql")
            .build();
    }
}
Kotlin
@Configuration
@Profile("default")
class DefaultDataConfig {

    @Bean
    fun dataSource(): DataSource {
        return EmbeddedDatabaseBuilder()
                .setType(EmbeddedDatabaseType.HSQL)
                .addScript("classpath:com/bank/config/sql/schema.sql")
                .build()
    }
}

アクティブなプロファイルがない場合、dataSource が作成されます。これは、1 つ以上の Bean のデフォルト定義を提供する方法として見ることができます。プロファイルが有効になっている場合、デフォルトのプロファイルは適用されません。

Environment で setDefaultProfiles() を使用して、または spring.profiles.default プロパティを使用して宣言的にデフォルトプロファイルの名前を変更できます。

1.13.2. PropertySource の抽象化

Spring の Environment 抽象化は、プロパティソースの構成可能な階層に対する検索操作を提供します。次のリストを検討してください。

Java
ApplicationContext ctx = new GenericApplicationContext();
Environment env = ctx.getEnvironment();
boolean containsMyProperty = env.containsProperty("my-property");
System.out.println("Does my environment contain the 'my-property' property? " + containsMyProperty);
Kotlin
val ctx = GenericApplicationContext()
val env = ctx.environment
val containsMyProperty = env.containsProperty("my-property")
println("Does my environment contain the 'my-property' property? $containsMyProperty")

上記のスニペットでは、my-property プロパティが現在の環境に定義されているかどうかを Spring に確認する高レベルの方法を示しています。この質問に答えるために、Environment オブジェクトは PropertySource (Javadoc) オブジェクトのセットに対して検索を実行します。PropertySource はキーと値のペアのソースに対する単純な抽象化であり、Spring の StandardEnvironment (Javadoc) は 2 つの PropertySource オブジェクトで構成されます。1 つは JVM システムプロパティのセット(System.getProperties())を表し、もう 1 つはシステム環境変数のセット(System.getenv())を表する

これらのデフォルトプロパティソースは、スタンドアロンアプリケーションで使用するために StandardEnvironment に存在します。StandardServletEnvironment (Javadoc) には、サーブレット構成およびサーブレットコンテキストパラメーターを含む追加のデフォルトプロパティソースが入力されます。オプションで JndiPropertySource (Javadoc) を有効にできます。詳細については、javadoc を参照してください。

具体的には、StandardEnvironment を使用すると、my-property システムプロパティまたは my-property 環境変数が実行時に存在する場合、env.containsProperty("my-property") の呼び出しは true を返します。

実行される検索は階層的です。デフォルトでは、システムプロパティは環境変数よりも優先されます。そのため、env.getProperty("my-property") の呼び出し中に両方の場所で my-property プロパティが設定された場合、システムプロパティ値が「勝ち」返されます。プロパティ値はマージされず、前のエントリによって完全にオーバーライドされることに注意してください。

一般的な StandardServletEnvironment の場合、完全な階層は次のようになり、最上位のエントリが最上位になります。

  1. ServletConfig パラメーター (該当する場合  —  たとえば、DispatcherServlet コンテキストの場合)

  2. ServletContext パラメーター (web.xml context-param エントリ)

  3. JNDI 環境変数 (java:comp/env/ エントリ)

  4. JVM システムプロパティ (-D コマンドライン引数)

  5. JVM システム環境 (オペレーティングシステムの環境変数)

最も重要なことは、メカニズム全体を構成できることです。おそらく、この検索に統合したいプロパティのカスタムソースがあります。これを行うには、独自の PropertySource を実装およびインスタンス化し、現在の Environment の PropertySources のセットに追加します。次の例は、その方法を示しています。

Java
ConfigurableApplicationContext ctx = new GenericApplicationContext();
MutablePropertySources sources = ctx.getEnvironment().getPropertySources();
sources.addFirst(new MyPropertySource());
Kotlin
val ctx = GenericApplicationContext()
val sources = ctx.environment.propertySources
sources.addFirst(MyPropertySource())

上記のコードでは、MyPropertySource が検索で最高の優先度で追加されています。my-property プロパティが含まれている場合、他の PropertySource の my-property プロパティを優先して、プロパティが検出されて返されます。MutablePropertySources (Javadoc) API は、プロパティソースのセットの正確な操作を可能にする多くのメソッドを公開します。

1.13.3. @PropertySource を使用する

@PropertySource (Javadoc) アノテーションは、PropertySource を Spring の Environment に追加するための便利で宣言的なメカニズムを提供します。

キーと値のペア testbean.name=myTestBean を含む app.properties というファイルがある場合、次の @Configuration クラスは @PropertySource を使用して、testBean.getName() の呼び出しが myTestBean を返すようにします。

Java
@Configuration
@PropertySource("classpath:/com/myco/app.properties")
public class AppConfig {

    @Autowired
    Environment env;

    @Bean
    public TestBean testBean() {
        TestBean testBean = new TestBean();
        testBean.setName(env.getProperty("testbean.name"));
        return testBean;
    }
}
Kotlin
@Configuration
@PropertySource("classpath:/com/myco/app.properties")
class AppConfig {

    @Autowired
    private lateinit var env: Environment

    @Bean
    fun testBean() = TestBean().apply {
        name = env.getProperty("testbean.name")!!
    }
}

次の例に示すように、@PropertySource リソースの場所に存在する ${…​} プレースホルダーは、環境に対して既に登録されているプロパティソースのセットに対して解決されます。

Java
@Configuration
@PropertySource("classpath:/com/${my.placeholder:default/path}/app.properties")
public class AppConfig {

    @Autowired
    Environment env;

    @Bean
    public TestBean testBean() {
        TestBean testBean = new TestBean();
        testBean.setName(env.getProperty("testbean.name"));
        return testBean;
    }
}
Kotlin
@Configuration
@PropertySource("classpath:/com/\${my.placeholder:default/path}/app.properties")
class AppConfig {

    @Autowired
    private lateinit var env: Environment

    @Bean
    fun testBean() = TestBean().apply {
        name = env.getProperty("testbean.name")!!
    }
}

my.placeholder がすでに登録されているプロパティソース(システムプロパティや環境変数など)のいずれかに存在すると仮定すると、プレースホルダーは対応する値に解決されます。そうでない場合は、default/path がデフォルトとして使用されます。デフォルトが指定されておらず、プロパティを解決できない場合、IllegalArgumentException がスローされます。

@PropertySource アノテーションは、Java 8 規則に従って繰り返し可能です。ただし、このような @PropertySource アノテーションはすべて、構成クラス上で直接、または同じカスタムアノテーション内のメタアノテーションとして、同じレベルで宣言する必要があります。直接アノテーションがメタアノテーションを効果的にオーバーライドするため、直接アノテーションとメタアノテーションを混在させることはお勧めしません。

1.13.4. ステートメントのプレースホルダー解決

従来、要素内のプレースホルダーの値は、JVM システムプロパティまたは環境変数に対してのみ解決できました。これはもはや事実ではありません。Environment 抽象化はコンテナー全体に統合されているため、プレースホルダの解決をコンテナー全体に簡単にルーティングできます。これは、任意の方法で解決プロセスを構成できることを意味します。システムプロパティと環境変数を検索する優先順位を変更したり、完全に削除したりできます。必要に応じて、独自のプロパティソースをミックスに追加することもできます。

具体的には、Environment で使用可能な限り、customer プロパティが定義されている場所に関係なく、次のステートメントが機能します。

<beans>
    <import resource="com/bank/service/${customer}-config.xml"/>
</beans>

1.14. LoadTimeWeaver の登録

LoadTimeWeaver は、Spring が Java 仮想マシン(JVM)にロードされるクラスを動的に変換するために使用されます。

ロード時のウィービングを有効にするには、次の例に示すように、@EnableLoadTimeWeaving を @Configuration クラスの 1 つに追加できます。

Java
@Configuration
@EnableLoadTimeWeaving
public class AppConfig {
}
Kotlin
@Configuration
@EnableLoadTimeWeaving
class AppConfig

あるいは、XML 構成の場合、context:load-time-weaver 要素を使用できます。

<beans>
    <context:load-time-weaver/>
</beans>

ApplicationContext 用に構成すると、その ApplicationContext 内の Bean は LoadTimeWeaverAware を実装でき、それによってロード時ウィーバーインスタンスへの参照を受け取ります。これは、JPA クラスの変換にロード時のウィービングが必要な場合がある Spring の JPA サポートとの組み合わせで特に役立ちます。詳細については、LocalContainerEntityManagerFactoryBean (Javadoc) javadoc を参照してください。AspectJ のロード時間ウィービングの詳細については、Spring Framework での AspectJ によるロード時の織りを参照してください。

1.15. ApplicationContext の追加機能

はじめにので説明したように、org.springframework.beans.factory パッケージは、プログラムによる方法を含め、Bean を管理および操作するための基本的な機能を提供します。org.springframework.context パッケージは、org.springframework.beans.factory インターフェースを追加します。ApplicationContext (Javadoc) インターフェースは、BeanFactory インターフェースを継承し、他のインターフェースを継承して、よりアプリケーションフレームワーク指向のスタイルで追加機能を提供します。多くの人は、ApplicationContext を完全に宣言的な方法で使用し、プログラムで作成することさえせず、ContextLoader などのサポートクラスに依存して、Java EE Web アプリケーションの通常の起動プロセスの一部として ApplicationContext を自動的にインスタンス化します。

よりフレームワーク指向のスタイルで BeanFactory 機能を強化するために、コンテキストパッケージは次の機能も提供します。

  • MessageSource インターフェースを介した i18n スタイルのメッセージへのアクセス。

  • ResourceLoader インターフェースを介した URL やファイルなどのリソースへのアクセス。

  • イベントの公開。つまり、ApplicationEventPublisher インターフェースを使用した、ApplicationListener インターフェースを実装する Bean へ。

  • HierarchicalBeanFactory インターフェースを介して、アプリケーションの Web レイヤーなどの 1 つの特定のレイヤーにそれぞれを集中させる、複数の(階層)コンテキストのロード。

1.15.1. MessageSource を使用した国際化

ApplicationContext インターフェースは、MessageSource と呼ばれるインターフェースを継承するため、国際化(「i18n」)機能を提供します。Spring は、メッセージを階層的に解決できる HierarchicalMessageSource インターフェースも提供します。これらのインターフェースはともに、Spring がメッセージ解決に影響を与える基盤を提供します。これらのインターフェースで定義されているメソッドは次のとおりです。

  • String getMessage(String code, Object[] args, String default, Locale loc)MessageSource からメッセージを取得するために使用される基本的な方法。指定されたロケールのメッセージが見つからない場合、デフォルトのメッセージが使用されます。渡された引数は、標準ライブラリが提供する MessageFormat 機能を使用して、置換値になります。

  • String getMessage(String code, Object[] args, Locale loc): 基本的に前の方法と同じですが、1 つの違いがあります : デフォルトのメッセージは指定できません。メッセージが見つからない場合、NoSuchMessageException がスローされます。

  • String getMessage(MessageSourceResolvable resolvable, Locale locale): 上記のメソッドで使用されるすべてのプロパティは、このメソッドで使用できる MessageSourceResolvable という名前のクラスでもラップされます。

ApplicationContext がロードされると、コンテキストで定義された MessageSource Bean を自動的に検索します。Bean の名前は messageSource でなければなりません。そのような Bean が見つかった場合、前述のメソッドへのすべての呼び出しはメッセージソースに委譲されます。メッセージソースが見つからない場合、ApplicationContext は同じ名前の Bean を含む親を見つけようとします。存在する場合、その Bean を MessageSource として使用します。ApplicationContext がメッセージのソースを見つけられない場合、空の DelegatingMessageSource がインスタンス化されて、上記で定義されたメソッドへの呼び出しを受け入れることができます。

Spring は、2 つの MessageSource 実装、ResourceBundleMessageSource および StaticMessageSource を提供します。両方とも、ネストされたメッセージングを行うために HierarchicalMessageSource を実装します。StaticMessageSource はめったに使用されませんが、ソースにメッセージをプログラムで追加する方法を提供します。次の例は、ResourceBundleMessageSource を示しています。

<beans>
    <bean id="messageSource"
            class="org.springframework.context.support.ResourceBundleMessageSource">
        <property name="basenames">
            <list>
                <value>format</value>
                <value>exceptions</value>
                <value>windows</value>
            </list>
        </property>
    </bean>
</beans>

この例では、クラスパスで formatexceptions および windows という 3 つのリソースバンドルが定義されていることを前提としています。メッセージを解決するリクエストは、ResourceBundle オブジェクトを介してメッセージを解決する JDK 標準の方法で処理されます。例の目的上、上記の 2 つのリソースバンドルファイルの内容は次のとおりであると仮定します。

    # in format.properties
    message=Alligators rock!
    # in exceptions.properties
    argument.required=The {0} argument is required.

次の例は、MessageSource 機能を実行するプログラムを示しています。すべての ApplicationContext 実装は MessageSource 実装でもあるため、MessageSource インターフェースにキャストできることに注意してください。

Java
public static void main(String[] args) {
    MessageSource resources = new ClassPathXmlApplicationContext("beans.xml");
    String message = resources.getMessage("message", null, "Default", Locale.ENGLISH);
    System.out.println(message);
}
Kotlin
fun main() {
    val resources = ClassPathXmlApplicationContext("beans.xml")
    val message = resources.getMessage("message", null, "Default", Locale.ENGLISH)
    println(message)
}

上記のプログラムからの出力は次のとおりです。

Alligators rock!

要約すると、MessageSource は、クラスパスのルートに存在する beans.xml というファイルで定義されています。messageSource Bean 定義は、basenames プロパティを介していくつかのリソースバンドルを参照します。リストで basenames プロパティに渡される 3 つのファイルは、クラスパスのルートにファイルとして存在し、それぞれ format.propertiesexceptions.properties および windows.properties と呼ばれます。

次の例は、メッセージ検索に渡される引数を示しています。これらの引数は String オブジェクトに変換され、ルックアップメッセージのプレースホルダーに挿入されます。

<beans>

    <!-- this MessageSource is being used in a web application -->
    <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
        <property name="basename" value="exceptions"/>
    </bean>

    <!-- lets inject the above MessageSource into this POJO -->
    <bean id="example" class="com.something.Example">
        <property name="messages" ref="messageSource"/>
    </bean>

</beans>
Java
public class Example {

    private MessageSource messages;

    public void setMessages(MessageSource messages) {
        this.messages = messages;
    }

    public void execute() {
        String message = this.messages.getMessage("argument.required",
            new Object [] {"userDao"}, "Required", Locale.ENGLISH);
        System.out.println(message);
    }
}
Kotlin
    class Example {

    lateinit var messages: MessageSource

    fun execute() {
        val message = messages.getMessage("argument.required",
                arrayOf("userDao"), "Required", Locale.ENGLISH)
        println(message)
    }
}

execute() メソッドの呼び出しの結果の出力は次のとおりです。

The userDao argument is required.

国際化(「i18n」)に関して、Spring のさまざまな MessageSource 実装は、標準 JDK ResourceBundle と同じロケール解決およびフォールバック規則に従います。要するに、以前に定義したサンプル messageSource を続けて、イギリス(en-GB)ロケールに対してメッセージを解決する場合、それぞれ format_en_GB.propertiesexceptions_en_GB.propertieswindows_en_GB.properties というファイルを作成します。

通常、ロケール解決は、アプリケーションの周囲の環境によって管理されます。次の例では、(イギリスの)メッセージが解決されるロケールは手動で指定されます。

# in exceptions_en_GB.properties
argument.required=Ebagum lad, the ''{0}'' argument is required, I say, required.
Java
public static void main(final String[] args) {
    MessageSource resources = new ClassPathXmlApplicationContext("beans.xml");
    String message = resources.getMessage("argument.required",
        new Object [] {"userDao"}, "Required", Locale.UK);
    System.out.println(message);
}
Kotlin
fun main() {
    val resources = ClassPathXmlApplicationContext("beans.xml")
    val message = resources.getMessage("argument.required",
            arrayOf("userDao"), "Required", Locale.UK)
    println(message)
}

上記のプログラムを実行した結果の出力は次のとおりです。

Ebagum lad, the 'userDao' argument is required, I say, required.

MessageSourceAware インターフェースを使用して、定義済みの MessageSource への参照を取得することもできます。MessageSourceAware インターフェースを実装する ApplicationContext で定義されている Bean には、Bean が作成および構成されたときに、アプリケーションコンテキストの MessageSource が注入されます。

ResourceBundleMessageSource の代替として、Spring は ReloadableResourceBundleMessageSource クラスを提供します。このバリアントは同じバンドルファイル形式をサポートしていますが、標準の JDK ベースの ResourceBundleMessageSource 実装よりも柔軟性があります。特に、(クラスパスからだけでなく)Spring リソースの場所からファイルを読み取ることができ、バンドルプロパティファイルのホットリロードをサポートします(その間に効率的にキャッシュします)。詳細については、ReloadableResourceBundleMessageSource (Javadoc) javadoc を参照してください。

1.15.2. 標準およびカスタムイベント

ApplicationContext のイベント処理は、ApplicationEvent クラスと ApplicationListener インターフェースを介して提供されます。ApplicationListener インターフェースを実装する Bean がコンテキストにデプロイされる場合、ApplicationEvent が ApplicationContext に公開されるたびに、その Bean が通知されます。基本的に、これは標準の Observer デザインパターンです。

Spring 4.2, 以降、イベントインフラストラクチャは大幅に改善され、アノテーションベースのモデルと、任意のイベント(つまり、必ずしも ApplicationEvent から拡張されないオブジェクト)を公開する機能を提供しています。そのようなオブジェクトが公開されると、あなたのためにイベントでそれをラップします。

次の表に、Spring が提供する標準イベントを示します。

表 7: 組み込みイベント
イベント 説明

ContextRefreshedEvent

ApplicationContext が初期化またはリフレッシュされたときに発行されます(たとえば、ConfigurableApplicationContext インターフェースで refresh() メソッドを使用して)。ここで、「初期化」とは、すべての Bean がロードされ、ポストプロセッサ Bean が検出およびアクティブ化され、シングルトンが事前にインスタンス化され、ApplicationContext オブジェクトが使用できる状態であることを意味します。コンテキストが閉じられていない限り、選択された ApplicationContext がそのような「ホット」リフレッシュを実際にサポートしていれば、リフレッシュを複数回トリガーできます。例: XmlWebApplicationContext はホットリフレッシュをサポートしていますが、GenericApplicationContext はサポートしていません。

ContextStartedEvent

ConfigurableApplicationContext インターフェースで start() メソッドを使用して ApplicationContext が開始されたときに公開されます。ここで、「開始」とは、すべての Lifecycle Bean が明示的な開始シグナルを受信することを意味します。通常、この信号は明示的な停止後に Bean を再起動するために使用されますが、自動起動用に設定されていないコンポーネント(たとえば、初期化時にまだ起動されていないコンポーネント)の起動にも使用できます。

ContextStoppedEvent

ConfigurableApplicationContext インターフェースで stop() メソッドを使用して ApplicationContext が停止したときに公開されます。ここで、「停止」とは、すべての Lifecycle Bean が明示的な停止信号を受信することを意味します。停止したコンテキストは、start() 呼び出しを介して再開できます。

ContextClosedEvent

ConfigurableApplicationContext インターフェースで close() メソッドを使用するか、JVM シャットダウンフックを介して ApplicationContext が閉じられるときに公開されます。ここで、「クローズ」とは、すべてのシングルトン Bean が破棄されることを意味します。コンテキストが閉じられると、その寿命が終わり、リフレッシュまたは再起動できなくなります。

RequestHandledEvent

HTTP リクエストが処理されたことをすべての Bean に通知する Web 固有のイベント。このイベントは、リクエストが完了すると公開されます。このイベントは、Spring の DispatcherServlet を使用する Web アプリケーションにのみ適用されます。

ServletRequestHandledEvent

サーブレット固有のコンテキスト情報を追加する RequestHandledEvent のサブクラス。

独自のカスタムイベントを作成して公開することもできます。次の例は、Spring の ApplicationEvent 基本クラスを継承する単純なクラスを示しています。

Java
public class BlockedListEvent extends ApplicationEvent {

    private final String address;
    private final String content;

    public BlockedListEvent(Object source, String address, String content) {
        super(source);
        this.address = address;
        this.content = content;
    }

    // accessor and other methods...
}
Kotlin
class BlockedListEvent(source: Any,
                    val address: String,
                    val content: String) : ApplicationEvent(source)

カスタム ApplicationEvent を公開するには、ApplicationEventPublisher で publishEvent() メソッドを呼び出します。通常、これは、ApplicationEventPublisherAware を実装するクラスを作成し、Spring Bean として登録することにより行われます。次の例は、このようなクラスを示しています。

Java
public class EmailService implements ApplicationEventPublisherAware {

    private List<String> blockedList;
    private ApplicationEventPublisher publisher;

    public void setBlockedList(List<String> blockedList) {
        this.blockedList = blockedList;
    }

    public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
        this.publisher = publisher;
    }

    public void sendEmail(String address, String content) {
        if (blockedList.contains(address)) {
            publisher.publishEvent(new BlockedListEvent(this, address, content));
            return;
        }
        // send email...
    }
}
Kotlin
class EmailService : ApplicationEventPublisherAware {

    private lateinit var blockedList: List<String>
    private lateinit var publisher: ApplicationEventPublisher

    fun setBlockedList(blockedList: List<String>) {
        this.blockedList = blockedList
    }

    override fun setApplicationEventPublisher(publisher: ApplicationEventPublisher) {
        this.publisher = publisher
    }

    fun sendEmail(address: String, content: String) {
        if (blockedList!!.contains(address)) {
            publisher!!.publishEvent(BlockedListEvent(this, address, content))
            return
        }
        // send email...
    }
}

構成時に、Spring コンテナーは、EmailService が ApplicationEventPublisherAware を実装していることを検出し、自動的に setApplicationEventPublisher() を呼び出します。実際には、渡されるパラメーターは Spring コンテナー自体です。ApplicationEventPublisher インターフェースを介してアプリケーションコンテキストと対話しています。

カスタム ApplicationEvent を受信するには、ApplicationListener を実装するクラスを作成し、それを Spring Bean として登録します。次の例は、このようなクラスを示しています。

Java
public class BlockedListNotifier implements ApplicationListener<BlockedListEvent> {

    private String notificationAddress;

    public void setNotificationAddress(String notificationAddress) {
        this.notificationAddress = notificationAddress;
    }

    public void onApplicationEvent(BlockedListEvent event) {
        // notify appropriate parties via notificationAddress...
    }
}
Kotlin
class BlockedListNotifier : ApplicationListener<BlockedListEvent> {

    lateinit var notificationAddres: String

    override fun onApplicationEvent(event: BlockedListEvent) {
        // notify appropriate parties via notificationAddress...
    }
}

ApplicationListener は、カスタムイベントのタイプ(前の例では BlockedListEvent)で一般的にパラメーター化されていることに注意してください。これは、onApplicationEvent() メソッドがタイプセーフのままであり、ダウンキャストの必要性を回避できることを意味します。必要な数のイベントリスナーを登録できますが、デフォルトでは、イベントリスナーはイベントを同期的に受信します。これは、すべてのリスナーがイベントの処理を完了するまで、publishEvent() メソッドがブロックすることを意味します。この同期シングルスレッドアプローチの利点の 1 つは、リスナーがイベントを受信すると、トランザクションコンテキストが利用可能な場合、パブリッシャーのトランザクションコンテキスト内で動作することです。イベントを公開するための別の戦略が必要になった場合は、Spring の ApplicationEventMulticaster (Javadoc) インターフェースの javadoc および構成オプションの SimpleApplicationEventMulticaster (Javadoc) 実装を参照してください。

次の例は、上記の各クラスを登録および構成するために使用される Bean 定義を示しています。

<bean id="emailService" class="example.EmailService">
    <property name="blockedList">
        <list>
            <value>[email protected] (英語)  </value>
            <value>[email protected] (英語)  </value>
            <value>[email protected] (英語)  </value>
        </list>
    </property>
</bean>

<bean id="blockedListNotifier" class="example.BlockedListNotifier">
    <property name="notificationAddress" value="[email protected] (英語)  "/>
</bean>

これらすべてをまとめると、emailService Bean の sendEmail() メソッドが呼び出されたときに、ブロックする必要があるメールメッセージがある場合、BlockedListEvent タイプのカスタムイベントが発行されます。blockedListNotifier Bean は ApplicationListener として登録され、BlockedListEvent を受信します。この時点で適切な関係者に通知できます。

Spring のイベントメカニズムは、同じアプリケーションコンテキスト内で Spring Bean 間の単純な通信用に設計されています。ただし、より高度なエンタープライズ統合のニーズに対応するため、別途保守される Spring Integration プロジェクトは、よく知られている Spring プログラミングモデルに基づいた、軽量でパターン指向 (英語) のイベント駆動型アーキテクチャの構築を完全にサポートします。
アノテーションベースのイベントリスナー

Spring 4.2, 以降、@EventListener アノテーションを使用して、マネージ Bean の任意の public メソッドにイベントリスナーを登録できます。BlockedListNotifier は次のように書き換えることができます。

Java
public class BlockedListNotifier {

    private String notificationAddress;

    public void setNotificationAddress(String notificationAddress) {
        this.notificationAddress = notificationAddress;
    }

    @EventListener
    public void processBlockedListEvent(BlockedListEvent event) {
        // notify appropriate parties via notificationAddress...
    }
}
Kotlin
class BlockedListNotifier {

    lateinit var notificationAddress: String

    @EventListener
    fun processBlockedListEvent(event: BlockedListEvent) {
        // notify appropriate parties via notificationAddress...
    }
}

メソッドシグネチャーは、リッスンするイベントタイプを再度宣言しますが、今回は、柔軟な名前を使用して、特定のリスナーインターフェースを実装しません。実際のイベントタイプが実装階層内のジェネリックパラメーターを解決する限り、ジェネリックを介してイベントタイプを絞り込むこともできます。

メソッドが複数のイベントをリッスンする必要がある場合、またはパラメーターをまったく指定せずに定義する場合、アノテーション自体でイベントタイプを指定することもできます。次の例は、その方法を示しています。

Java
@EventListener({ContextStartedEvent.class, ContextRefreshedEvent.class})
public void handleContextStart() {
    // ...
}
Kotlin
@EventListener(ContextStartedEvent::class, ContextRefreshedEvent::class)
fun handleContextStart() {
    // ...
}

特定のイベントのメソッドを実際に呼び出すために一致する SpEL 式を定義するアノテーションの condition 属性を使用して、追加のランタイムフィルタリングを追加することもできます。

次の例は、イベントの content 属性が my-event と等しい場合にのみ呼び出されるようにノーティファイアを書き換える方法を示しています。

Java
@EventListener(condition = "#blEvent.content == 'my-event'")
public void processBlockedListEvent(BlockedListEvent blockedListEvent) {
    // notify appropriate parties via notificationAddress...
}
Kotlin
@EventListener(condition = "#blEvent.content == 'my-event'")
fun processBlockedListEvent(blockedListEvent: BlockedListEvent) {
    // notify appropriate parties via notificationAddress...
}

各 SpEL 式は、専用のコンテキストに対して評価されます。次の表に、条件付きイベント処理に使用できるように、コンテキストで使用できるようにするアイテムを示します。

表 8: イベント SpEL の利用可能なメタデータ
名前 ロケーション 説明 サンプル

イベント

ルートオブジェクト

実際の ApplicationEvent

#root.event または event

引数配列

ルートオブジェクト

メソッドの呼び出しに使用される引数(オブジェクト配列として)。

#root.args または args; 最初の引数などにアクセスするための args[0] 

引数名

評価コンテキスト

メソッド引数の名前。何らかの理由で名前が利用できない場合(たとえば、コンパイルされたバイトコードにデバッグ情報がないため)、#a<#arg> 構文を使用して、<#arg> が引数インデックス(0 から始まる)を表す個々の引数も利用できます。

#blEvent または #a0 ( #p0 または #p<#arg> パラメーター表記をエイリアスとして使用することもできます)

メソッドシグネチャーが実際に公開された任意のオブジェクトを参照している場合でも、#root.event を使用すると、基になるイベントにアクセスできることに注意してください。

別のイベントを処理した結果としてイベントを発行する必要がある場合、次の例に示すように、発行する必要があるイベントを返すようにメソッドシグネチャーを変更できます。

Java
@EventListener
public ListUpdateEvent handleBlockedListEvent(BlockedListEvent event) {
    // notify appropriate parties via notificationAddress and
    // then publish a ListUpdateEvent...
}
Kotlin
@EventListener
fun handleBlockedListEvent(event: BlockedListEvent): ListUpdateEvent {
    // notify appropriate parties via notificationAddress and
    // then publish a ListUpdateEvent...
}
この機能は、非同期リスナーではサポートされていません。

この新しいメソッドは、上記のメソッドで処理されるすべての BlockedListEvent に対して新しい ListUpdateEvent を公開します。複数のイベントを公開する必要がある場合、代わりにイベントの Collection を返すことができます。

非同期リスナー

特定のリスナーにイベントを非同期的に処理させたい場合は、通常の @Async サポートを再利用できます。次の例は、その方法を示しています。

Java
@EventListener
@Async
public void processBlockedListEvent(BlockedListEvent event) {
    // BlockedListEvent is processed in a separate thread
}
Kotlin
@EventListener
@Async
fun processBlockedListEvent(event: BlockedListEvent) {
    // BlockedListEvent is processed in a separate thread
}

非同期イベントを使用するときは、次の制限に注意してください。

  • 非同期イベントリスナーが Exception をスローした場合、呼び出し元に伝播されません。詳細については、AsyncUncaughtExceptionHandler を参照してください。

  • 非同期イベントリスナーメソッドは、値を返すことで後続のイベントを発行できません。処理の結果として別のイベントを公開する必要がある場合は、ApplicationEventPublisher (Javadoc) を挿入してイベントを手動で公開します。

リスナーの順序付け

あるリスナーを別のリスナーの前に呼び出す必要がある場合は、次の例に示すように、@Order アノテーションをメソッド宣言に追加できます。

Java
@EventListener
@Order(42)
public void processBlockedListEvent(BlockedListEvent event) {
    // notify appropriate parties via notificationAddress...
}
Kotlin
@EventListener
@Order(42)
fun processBlockedListEvent(event: BlockedListEvent) {
    // notify appropriate parties via notificationAddress...
}
一般的なイベント

ジェネリックを使用して、イベントの構造をさらに定義することもできます。T が作成された実際のエンティティのタイプである EntityCreatedEvent<T> の使用を検討してください。例:次のリスナー定義を作成して、Person の EntityCreatedEvent のみを受信できます。

Java
@EventListener
public void onPersonCreated(EntityCreatedEvent<Person> event) {
    // ...
}
Kotlin
@EventListener
fun onPersonCreated(event: EntityCreatedEvent<Person>) {
    // ...
}

型の消去により、これは、発生したイベントがイベントリスナーがフィルター処理するジェネリックパラメーター(つまり、class PersonCreatedEvent extends EntityCreatedEvent<Person> { …​ } のようなもの)を解決する場合にのみ機能します。

特定の状況では、すべてのイベントが同じ構造に従う場合、これは非常に面倒になります(前の例のイベントの場合のように)。そのような場合、ResolvableTypeProvider を実装して、ランタイム環境が提供するものを超えてフレームワークをガイドできます。次のイベントは、その方法を示しています。

Java
public class EntityCreatedEvent<T> extends ApplicationEvent implements ResolvableTypeProvider {

    public EntityCreatedEvent(T entity) {
        super(entity);
    }

    @Override
    public ResolvableType getResolvableType() {
        return ResolvableType.forClassWithGenerics(getClass(), ResolvableType.forInstance(getSource()));
    }
}
Kotlin
class EntityCreatedEvent<T>(entity: T) : ApplicationEvent(entity), ResolvableTypeProvider {

    override fun getResolvableType(): ResolvableType? {
        return ResolvableType.forClassWithGenerics(javaClass, ResolvableType.forInstance(getSource()))
    }
}
これは、ApplicationEvent だけでなく、イベントとして送信する任意のオブジェクトに対しても機能します。

1.15.3. 低レベルのリソースへの便利なアクセス

アプリケーションコンテキストを最適に使用して理解するには、リソースに従って、Spring の Resource の抽象化に慣れる必要があります。

アプリケーションコンテキストは ResourceLoader であり、Resource オブジェクトのロードに使用できます。Resource は、本質的に JDK java.net.URL クラスのより機能が豊富なバージョンです。実際、Resource の実装は、必要に応じて java.net.URL のインスタンスをラップします。Resource は、クラスパス、ファイルシステムの場所、標準 URL で記述可能な場所、およびその他のバリエーションを含む、ほぼすべての場所から透過的に低レベルのリソースを取得できます。リソースの場所の文字列が特別なプレフィックスのない単純なパスである場合、それらのリソースがどこから来るかは、実際のアプリケーションコンテキストタイプに固有で適切です。

アプリケーションコンテキストにデプロイされた Bean を構成して、特殊なコールバックインターフェース ResourceLoaderAware を実装し、初期化時に自動的にコールバックされ、アプリケーションコンテキスト自体が ResourceLoader として渡されます。Resource 型のプロパティを公開して、静的リソースへのアクセスに使用することもできます。それらは他のプロパティと同様にそこに注入されます。これらの Resource プロパティを単純な String パスとして指定し、Bean がデプロイされるときにそれらのテキスト文字列から実際の Resource オブジェクトへの自動変換に依存することができます。

ApplicationContext コンストラクターに提供されるロケーションパスは、実際にはリソース文字列であり、単純な形式では、特定のコンテキスト実装に従って適切に処理されます。たとえば、ClassPathXmlApplicationContext は単純なロケーションパスをクラスパスロケーションとして扱います。また、実際のコンテキストタイプに関係なく、クラスパスまたは URL から定義を強制的にロードするために、特別なプレフィックスを持つロケーションパス(リソース文字列)を使用できます。

1.15.4. アプリケーションのスタートアップの追跡

ApplicationContext は、Spring アプリケーションのライフサイクルを管理し、コンポーネントに関する豊富なプログラミングモデルを提供します。その結果、複雑なアプリケーションは、同様に複雑なコンポーネントグラフと起動フェーズを持つことができます。

特定のメトリックを使用してアプリケーションの起動手順を追跡すると、起動フェーズで時間が費やされている場所を理解できますが、コンテキストライフサイクル全体をよりよく理解する方法としても使用できます。

AbstractApplicationContext (およびそのサブクラス)には、さまざまな起動フェーズに関する StartupStep データを収集する ApplicationStartup が装備されています。

  • アプリケーションコンテキストのライフサイクル (基本パッケージのスキャン、構成クラスの管理)

  • bean のライフサイクル (インスタンス化、スマート初期化、後処理)

  • アプリケーションイベント処理

AnnotationConfigApplicationContext のインストルメンテーションの例を次に示します。

Java
// create a startup step and start recording
StartupStep scanPackages = this.getApplicationStartup().start("spring.context.base-packages.scan");
// add tagging information to the current step
scanPackages.tag("packages", () -> Arrays.toString(basePackages));
// perform the actual phase we're instrumenting
this.scanner.scan(basePackages);
// end the current step
scanPackages.end();
Kotlin
// create a startup step and start recording
val scanPackages = this.getApplicationStartup().start("spring.context.base-packages.scan");
// add tagging information to the current step
scanPackages.tag("packages", () -> Arrays.toString(basePackages));
// perform the actual phase we're instrumenting
this.scanner.scan(basePackages);
// end the current step
scanPackages.end();

アプリケーションコンテキストには、すでに複数のステップが組み込まれています。記録されると、これらの起動手順は、特定のツールを使用して収集、表示、分析できます。既存の起動手順の完全なリストについては、専用の付録セクションを確認してください。

デフォルトの ApplicationStartup 実装は、オーバーヘッドを最小限に抑えるための no-op バリアントです。これは、デフォルトでは、アプリケーションの起動時にメトリックが収集されないことを意味します。Spring Framework には、Java Flight Recorder を使用して起動ステップを追跡するための実装が付属しています: FlightRecorderApplicationStartup。このバリアントを使用するには、作成されたらすぐに、そのインスタンスを ApplicationContext に構成する必要があります。

開発者は、独自の AbstractApplicationContext サブクラスを提供している場合、またはより正確なデータを収集したい場合にも、ApplicationStartup インフラストラクチャを使用できます。

ApplicationStartup は、アプリケーションの起動時およびコアコンテナーにのみ使用することを目的としています。これは、Java プロファイラーや Micrometer (英語) のようなメトリクスライブラリに代わるものではありません。

カスタム StartupStep の収集を開始するには、コンポーネントは、アプリケーションコンテキストから ApplicationStartup インスタンスを直接取得するか、コンポーネントに ApplicationStartupAware を実装させるか、任意のインジェクションポイントで ApplicationStartup タイプを要求します。

開発者は、カスタム起動ステップを作成するときに "spring.*" 名前空間を使用しないでください。この名前空間は、内部 Spring の使用のために予約されており、変更される可能性があります。

1.15.5. Web アプリケーション用の便利な ApplicationContext インスタンス化

ApplicationContext インスタンスを宣言的に作成するには、たとえば ContextLoader を使用します。もちろん、ApplicationContext 実装の 1 つを使用して、ApplicationContext インスタンスをプログラムで作成することもできます。

次の例に示すように、ContextLoaderListener を使用して ApplicationContext を登録できます。

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

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

リスナーは contextConfigLocation パラメーターをインスペクションします。パラメーターが存在しない場合、リスナーは /WEB-INF/applicationContext.xml をデフォルトとして使用します。パラメーターが存在する場合、リスナーは事前定義の区切り文字(コンマ、セミコロン、空白)を使用して String を分離し、値をアプリケーションコンテキストが検索される場所として使用します。Ant スタイルのパスパターンもサポートされています。例は、/WEB-INF/*Context.xml (名前が Context.xml で終わり、WEB-INF ディレクトリにあるすべてのファイル用)および /WEB-INF/**/*Context.xml (WEB-INF のサブディレクトリ内のすべてのそのようなファイル用)です。

1.15.6. Spring ApplicationContext を Java EE RAR ファイルとしてデプロイする

Spring ApplicationContext を RAR ファイルとしてデプロイし、コンテキストとそのすべての必須 Bean クラスおよびライブラリ JAR を Java EE RAR デプロイユニットにカプセル化することができます。これは、Java EE サーバー機能にアクセスできるスタンドアロン ApplicationContext (Java EE 環境でのみホストされている)をブートストラップすることと同等です。RAR デプロイは、ヘッドレス WAR ファイルをデプロイするシナリオのより自然な代替手段です。実際には、Java EE 環境で Spring ApplicationContext をブートストラップするためだけに使用される HTTP エントリポイントのない WAR ファイルです。

RAR デプロイは、HTTP エントリポイントを必要とせず、メッセージエンドポイントとスケジュールされたジョブのみで構成されるアプリケーションコンテキストに最適です。このようなコンテキストの Bean は、JTA トランザクションマネージャー、JNDI バインド JDBC DataSource インスタンス、JMS ConnectionFactory インスタンスなどのアプリケーションサーバーリソースを使用できます。また、プラットフォームの JMX サーバーに登録できます。すべて Spring の標準トランザクション管理と JNDI および JMX サポート機能を使用します。アプリケーションコンポーネントは、Spring の TaskExecutor 抽象化を通じてアプリケーションサーバーの JCA WorkManager と対話することもできます。

RAR デプロイに関連する構成の詳細については、SpringContextResourceAdapter (Javadoc) クラスの javadoc を参照してください。

Java EE RAR ファイルとしての Spring ApplicationContext の単純なデプロイの場合:

  1. すべてのアプリケーションクラスを RAR ファイル(ファイル拡張子が異なる標準 JAR ファイル)にパッケージ化します。必要なすべてのライブラリ JAR を RAR アーカイブのルートに追加します。META-INF/ra.xml デプロイ記述子(SpringContextResourceAdapterjavadoc を参照)および対応する Spring XML Bean 定義ファイル(通常 META-INF/applicationContext.xml)を追加します。

  2. 結果の RAR ファイルをアプリケーションサーバーのデプロイディレクトリにドロップします。

このような RAR デプロイユニットは通常、自己完結型です。コンポーネントを外部に公開することはなく、同じアプリケーションの他のモジュールにも公開しません。RAR ベースの ApplicationContext との相互作用は、通常、他のモジュールと共有する JMS 宛先を介して行われます。また、RAR ベースの ApplicationContext は、たとえば、一部のジョブをスケジュールしたり、ファイルシステム内の新しいファイルに反応したりすることもあります(または同様のもの)。外部からの同期アクセスを許可する必要がある場合、(たとえば)RMI エンドポイントをエクスポートできます。これは、同じマシン上の他のアプリケーションモジュールによって使用される可能性があります。

1.16. BeanFactory

BeanFactory API は、Spring の IoC 機能の基礎を提供します。その特定の契約は、Spring の他の部分および関連するサードパーティフレームワークとの統合で主に使用され、その DefaultListableBeanFactory 実装は、高レベルの GenericApplicationContext コンテナー内の主要なデリゲートです。

BeanFactory および関連インターフェース(BeanFactoryAwareInitializingBeanDisposableBean など)は、他のフレームワークコンポーネントの重要な統合ポイントです。アノテーションやリフレクションさえも必要としないことで、コンテナーとそのコンポーネント間の非常に効率的な相互作用が可能になります。アプリケーションレベルの Bean は同じコールバックインターフェースを使用する場合がありますが、通常はアノテーションまたはプログラムによる構成のいずれかを使用して、代わりに宣言依存性注入を好みます。

コア BeanFactory API レベルとその DefaultListableBeanFactory 実装は、使用される構成形式やコンポーネントアノテーションについて想定していないことに注意してください。これらのフレーバーはすべて、拡張機能(XmlBeanDefinitionReader や AutowiredAnnotationBeanPostProcessor など)を通じて提供され、コアメタデータ表現として共有 BeanDefinition オブジェクトを操作します。これは、Spring のコンテナーを非常に柔軟で拡張可能にするものの本質です。

1.16.1. BeanFactory または ApplicationContext

このセクションでは、BeanFactory コンテナーレベルと ApplicationContext コンテナーレベルの違いとブートストラップへの影響について説明します。

GenericApplicationContext とそのサブクラス AnnotationConfigApplicationContext をカスタムブートストラップの一般的な実装として、そうしない理由がない限り、ApplicationContext を使用する必要があります。これらは、すべての一般的な目的のための Spring のコアコンテナーへの主要なエントリポイントです。構成ファイルの読み込み、クラスパススキャンのトリガー、Bean 定義とアノテーション付きクラスのプログラムによる登録、および(5.0 時点で)関数 Bean 定義の登録

ApplicationContext には BeanFactory のすべての機能が含まれているため、Bean 処理の完全な制御が必要なシナリオを除き、プレーンな BeanFactory よりも一般的に推奨されます。ApplicationContext (GenericApplicationContext 実装など)では、いくつかの種類の Bean が慣例(つまり、Bean 名または Bean タイプ、特にポストプロセッサー)によって検出されますが、プレーン DefaultListableBeanFactory は特別な Bean に依存しません。

アノテーション処理や AOP プロキシなど、多くの拡張コンテナー機能には、BeanPostProcessor 拡張ポイントが不可欠です。プレーンな DefaultListableBeanFactory のみを使用する場合、そのようなポストプロセッサーはデフォルトでは検出およびアクティブ化されません。Bean の設定に実際に問題はないため、この状況は混乱を招く可能性があります。むしろ、このようなシナリオでは、追加のセットアップによりコンテナーを完全にブートストラップする必要があります。

次の表に、BeanFactory および ApplicationContext のインターフェースと実装が提供する機能を示します。

表 9: 機能マトリックス
フィーチャー BeanFactoryApplicationContext

Bean のインスタンス化 / 接続

はい

はい

統合されたライフサイクル管理

いいえ

はい

自動 BeanPostProcessor 登録

いいえ

はい

自動 BeanFactoryPostProcessor 登録

いいえ

はい

便利な MessageSource アクセス (内在化のために)

いいえ

はい

ビルトイン ApplicationEvent 発行メカニズム

いいえ

はい

Bean ポストプロセッサを DefaultListableBeanFactory に明示的に登録するには、次の例に示すように、プログラムで addBeanPostProcessor を呼び出す必要があります。

Java
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
// populate the factory with bean definitions

// now register any needed BeanPostProcessor instances
factory.addBeanPostProcessor(new AutowiredAnnotationBeanPostProcessor());
factory.addBeanPostProcessor(new MyBeanPostProcessor());

// now start using the factory
Kotlin
val factory = DefaultListableBeanFactory()
// populate the factory with bean definitions

// now register any needed BeanPostProcessor instances
factory.addBeanPostProcessor(AutowiredAnnotationBeanPostProcessor())
factory.addBeanPostProcessor(MyBeanPostProcessor())

// now start using the factory

BeanFactoryPostProcessor をプレーンな DefaultListableBeanFactory に適用するには、次の例に示すように、postProcessBeanFactory メソッドを呼び出す必要があります。

Java
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(new FileSystemResource("beans.xml"));

// bring in some property values from a Properties file
PropertySourcesPlaceholderConfigurer cfg = new PropertySourcesPlaceholderConfigurer();
cfg.setLocation(new FileSystemResource("jdbc.properties"));

// now actually do the replacement
cfg.postProcessBeanFactory(factory);
Kotlin
val factory = DefaultListableBeanFactory()
val reader = XmlBeanDefinitionReader(factory)
reader.loadBeanDefinitions(FileSystemResource("beans.xml"))

// bring in some property values from a Properties file
val cfg = PropertySourcesPlaceholderConfigurer()
cfg.setLocation(FileSystemResource("jdbc.properties"))

// now actually do the replacement
cfg.postProcessBeanFactory(factory)

どちらの場合も、明示的な登録手順は不便です。そのため、Spring-Backed アプリケーションでは、特に BeanFactoryPostProcessor および BeanPostProcessor インスタンスに依存して、一般的なエンタープライズセットアップで拡張されたコンテナー機能を使用する場合、さまざまな ApplicationContext バリアントがプレーン DefaultListableBeanFactory よりも優先されます。

AnnotationConfigApplicationContext にはすべての一般的なアノテーションポストプロセッサーが登録されており、@EnableTransactionManagement などの構成アノテーションを通じてカバーに追加のプロセッサーを取り込むことができます。Spring のアノテーションベースの構成モデルの抽象化レベルでは、Bean ポストプロセッサーの概念は単なる内部コンテナーの詳細になります。

2. リソース

この章では、Spring がリソースを処理する方法と、Spring でリソースを操作する方法について説明します。次のトピックが含まれます。

2.1. 導入

残念ながら、Java の標準 java.net.URL クラスおよびさまざまな URL プレフィックスの標準ハンドラーは、低レベルのリソースへのすべてのアクセスに十分ではありません。例:クラスパスまたは ServletContext から取得する必要があるリソースにアクセスするために使用できる標準化された URL 実装はありません。特殊な URL プレフィックスの新しいハンドラーを登録することは可能ですが(http: などのプレフィックスの既存のハンドラーと同様)、これは一般的に非常に複雑であり、URL インターフェースには、指し示されているリソース。

2.2. リソースインターフェース

Spring の Resource インターフェースは、低レベルのリソースへのアクセスを抽象化するためのより高性能なインターフェースとなることを意図しています。以下のリストは、Resource インターフェース定義を示しています。

Java
public interface Resource extends InputStreamSource {

    boolean exists();

    boolean isOpen();

    URL getURL() throws IOException;

    File getFile() throws IOException;

    Resource createRelative(String relativePath) throws IOException;

    String getFilename();

    String getDescription();
}
Kotlin
interface Resource : InputStreamSource {

    fun exists(): Boolean

    val isOpen: Boolean

    val url: URL

    val file: File

    @Throws(IOException::class)
    fun createRelative(relativePath: String): Resource

    val filename: String

    val description: String
}

Resource インターフェースの定義が示すように、InputStreamSource インターフェースを継承します。次のリストは、InputStreamSource インターフェースの定義を示しています。

Java
public interface InputStreamSource {

    InputStream getInputStream() throws IOException;
}
Kotlin
interface InputStreamSource {

    val inputStream: InputStream
}

Resource インターフェースの最も重要なメソッドのいくつかは次のとおりです。

  • getInputStream(): リソースを見つけて開き、リソースから読み取るために InputStream を返します。各呼び出しが新しい InputStream を返すことが期待されます。ストリームを閉じるのは呼び出し側の責任です。

  • exists(): このリソースが実際に物理的な形で存在するかどうかを示す boolean を返します。

  • isOpen(): このリソースがオープンストリームのハンドルを表すかどうかを示す boolean を返します。true の場合、InputStream は複数回読み取ることができず、リソースリークを避けるために一度だけ読み取ってから閉じる必要があります。InputStreamResource を除き、すべての通常のリソース実装に対して false を返します。

  • getDescription(): このリソースの説明を返します。これは、リソースを操作する際のエラー出力に使用されます。多くの場合、これは完全修飾ファイル名またはリソースの実際の URL です。

他のメソッドを使用すると、リソースを表す実際の URL または File オブジェクトを取得できます(基礎となる実装に互換性があり、その機能をサポートしている場合)。

Spring 自体は、リソースが必要な場合に多くのメソッドシグニチャーの引数タイプとして、Resource 抽象を広範囲に使用します。一部の Spring API の他のメソッド (様々な ApplicationContext 実装のコンストラクターなど) は String を使用します。これは修飾されていない、または単純な形式で、そのコンテキスト実装に適した Resource を作成するために使用されます。また、String パス上の特別なプレフィックスを使用して、呼び出し側が特定の Resource 実装を作成して使用する必要があることを指定できます。

Resource インターフェースは Spring および Spring で多く使用されますが、実際には、コードが他のことを知らないか気にしない場合でも、リソースへのアクセスのために、独自のコードで単独で一般的なユーティリティクラスとして使用すると非常に便利です Spring の部品。これにより、コードが Spring に結合されますが、実際には、この小さなユーティリティクラスのセットにのみ結合されます。これは、URL のより高性能な代替として機能し、この目的で使用する他のライブラリと同等と見なすことができます。

Resource 抽象化は機能を置き換えません。可能な場合はラップします。例: UrlResource は URL をラップし、ラップされた URL を使用して作業を行います。

2.3. 組み込みのリソース実装

Spring には、次の Resource 実装が含まれています。

2.3.1. UrlResource

UrlResource は java.net.URL をラップし、ファイル、HTTP ターゲット、FTP ターゲットなど、通常 URL でアクセス可能なオブジェクトにアクセスするために使用できます。すべての URL には標準化された String 表現があり、適切な標準化されたプレフィックスを使用して別の URL タイプを示します。これには、ファイルシステムパスにアクセスするための file:、HTTP プロトコル経由でリソースにアクセスするための http:、FTP 経由でリソースにアクセスするための ftp: などが含まれます。

UrlResource は、明示的に UrlResource コンストラクターを使用して Java コードによって作成されますが、パスを表すための String 引数を取る API メソッドを呼び出すと暗黙的に作成されることがよくあります。後者の場合、JavaBeans PropertyEditor は最終的にどのタイプの Resource を作成するかを決定します。パス文字列に既知の(つまり)プレフィックス(classpath: など)が含まれている場合、そのプレフィックスに適切な専用 Resource が作成されます。ただし、プレフィックスを認識しない場合、文字列は標準の URL 文字列であると想定し、UrlResource を作成します。

2.3.2. ClassPathResource

このクラスは、クラスパスから取得するリソースを表します。スレッドコンテキストクラスローダー、特定のクラスローダー、またはリソースの読み込みに特定のクラスのいずれかを使用します。

この Resource 実装は、クラスパスリソースがファイルシステムに存在するが、jar に存在し、(サーブレットエンジンまたは環境が何であっても)ファイルシステムに展開されていないクラスパスリソースの場合、java.io.File としての解決をサポートします。これに対処するために、さまざまな Resource 実装は常に java.net.URL として解決をサポートします。

ClassPathResource は、明示的に ClassPathResource コンストラクターを使用して Java コードによって作成されますが、パスを表すための String 引数を取る API メソッドを呼び出すと暗黙的に作成されることがよくあります。後者の場合、JavaBeans PropertyEditor はストリングパス上の特別なプレフィックス classpath を認識し、その場合 ClassPathResource を作成します。

2.3.3. FileSystemResource

これは、java.io.File および java.nio.file.Path ハンドルの Resource 実装です。File および URL として解決をサポートします。

2.3.4. ServletContextResource

これは、関連する Web アプリケーションのルートディレクトリ内の相対パスを解釈する ServletContext リソースの Resource 実装です。

常にストリームアクセスと URL アクセスをサポートしますが、Web アプリケーションアーカイブが展開され、リソースが物理的にファイルシステム上にある場合にのみ java.io.File アクセスを許可します。拡張されてファイルシステム上にあるか、JAR またはデータベースのような他の場所(考えられる)から直接アクセスされるかどうかは、実際にはサーブレットコンテナーに依存します。

2.3.5. InputStreamResource

InputStreamResource は、指定された InputStream の Resource 実装です。特定の Resource 実装が適用できない場合にのみ使用してください。特に、可能な場合は、ByteArrayResource またはファイルベースの Resource 実装のいずれかを優先してください。

他の Resource 実装とは対照的に、これは既に開かれているリソースの記述子です。isOpen() から true を返します。リソース記述子をどこかに保持する必要がある場合、またはストリームを複数回読み取る必要がある場合は、使用しないでください。

2.3.6. ByteArrayResource

これは、指定されたバイト配列の Resource 実装です。指定されたバイト配列の ByteArrayInputStream を作成します。

これは、使い捨ての InputStreamResource に頼ることなく、任意のバイト配列からコンテンツをロードできます。

2.4. ResourceLoader

ResourceLoader インターフェースは、Resource インスタンスを返す(ロードする)オブジェクトによって実装されることを意図しています。次のリストは、ResourceLoader インターフェース定義を示しています。

Java
public interface ResourceLoader {

    Resource getResource(String location);
}
Kotlin
interface ResourceLoader {

    fun getResource(location: String): Resource
}

すべてのアプリケーションコンテキストは、ResourceLoader インターフェースを実装します。すべてのアプリケーションコンテキストを使用して Resource インスタンスを取得できます。

特定のアプリケーションコンテキストで getResource() を呼び出し、指定した場所のパスに特定のプレフィックスがない場合、その特定のアプリケーションコンテキストに適した Resource タイプが返されます。例:次のコードスニペットが ClassPathXmlApplicationContext インスタンスに対して実行されたと想定します。

Java
Resource template = ctx.getResource("some/resource/path/myTemplate.txt");
Kotlin
val template = ctx.getResource("some/resource/path/myTemplate.txt")

ClassPathXmlApplicationContext に対して、そのコードは ClassPathResource を返します。同じメソッドが FileSystemXmlApplicationContext インスタンスに対して実行された場合、FileSystemResource が返されます。WebApplicationContext の場合、ServletContextResource を返します。同様に、各コンテキストに適切なオブジェクトを返します。

その結果、特定のアプリケーションコンテキストに適した方法でリソースをロードできます。

一方、次の例に示すように、特殊な classpath: プレフィックスを指定することにより、アプリケーションコンテキストタイプに関係なく、ClassPathResource を強制的に使用することもできます。

Java
Resource template = ctx.getResource("classpath:some/resource/path/myTemplate.txt");
Kotlin
val template = ctx.getResource("classpath:some/resource/path/myTemplate.txt")

同様に、標準の java.net.URL プレフィックスのいずれかを指定することにより、強制的に UrlResource を使用できます。次の例のペアでは、file および http プレフィックスを使用しています。

Java
Resource template = ctx.getResource("file:///some/resource/path/myTemplate.txt");
Kotlin
val template = ctx.getResource("file:///some/resource/path/myTemplate.txt")
Java
Resource template = ctx.getResource("https://myhost.com/resource/path/myTemplate.txt");
Kotlin
val template = ctx.getResource("https://myhost.com/resource/path/myTemplate.txt")

次の表は、String オブジェクトを Resource オブジェクトに変換する戦略をまとめたものです。

表 10: リソース文字列
接頭辞 サンプル 説明

クラスパス:

classpath:com/myapp/config.xml

クラスパスからロードされます。

ファイル :

file:///data/config.xml

ファイルシステムから URL としてロードされます。FileSystemResource 警告も参照してください。

http:

https://myserver/logo.png (英語)

URL としてロードされます。

(なし)

/data/config.xml

基礎となる ApplicationContext に依存します。

2.5. ResourceLoaderAware インターフェース

ResourceLoaderAware インターフェースは、ResourceLoader 参照の提供が期待されるコンポーネントを識別する特別なコールバックインターフェースです。次のリストは、ResourceLoaderAware インターフェースの定義を示しています。

Java
public interface ResourceLoaderAware {

    void setResourceLoader(ResourceLoader resourceLoader);
}
Kotlin
interface ResourceLoaderAware {

    fun setResourceLoader(resourceLoader: ResourceLoader)
}

クラスが ResourceLoaderAware を実装し、アプリケーションコンテキストに(Spring 管理の Bean として)デプロイされると、アプリケーションコンテキストによって ResourceLoaderAware として認識されます。次に、アプリケーションコンテキストは setResourceLoader(ResourceLoader) を呼び出し、自身を引数として提供します(Spring のすべてのアプリケーションコンテキストが ResourceLoader インターフェースを実装することを思い出してください)。

ApplicationContext は ResourceLoader であるため、Bean は ApplicationContextAware インターフェースを実装し、提供されたアプリケーションコンテキストを直接使用してリソースをロードすることもできます。ただし、一般的には、必要な場合は専用の ResourceLoader インターフェースを使用することをお勧めします。コードは、Spring ApplicationContext インターフェース全体ではなく、リソースローディングインターフェース(ユーティリティインターフェースと見なすことができます)にのみ結合されます。

アプリケーションコンポーネントでは、ResourceLoaderAware インターフェースを実装する代わりに、ResourceLoader のオートワイヤーを使用することもできます。「従来の」 constructor および byType オートワイヤーモード(共同エディターのオートワイヤーで説明)は、それぞれコンストラクター引数または setter メソッドパラメーターのいずれかに ResourceLoader を提供できます。柔軟性を高めるために(フィールドと複数のパラメーターメソッドをオートワイヤーする機能を含む)、アノテーションベースのオートワイヤー機能の使用を検討してください。その場合、ResourceLoader は、問題のフィールド、コンストラクター、またはメソッドが @Autowired アノテーションを保持している限り、ResourceLoader タイプを予期するフィールド、コンストラクター引数、またはメソッドパラメーターに自動接続されます。詳細については、@Autowired を使用するを参照してください。

2.6. 依存関係としてのリソース

Bean 自体が何らかの動的プロセスを介してリソースパスを決定して供給する場合、Bean が ResourceLoader インターフェースを使用してリソースをロードすることはおそらく理にかなっています。例:必要な特定のリソースがユーザーのロールに依存する、ある種のテンプレートのロードを検討します。リソースが静的である場合、ResourceLoader インターフェースの使用を完全に排除し、Bean が必要な Resource プロパティを公開し、それらにプロパティが挿入されることを期待するのは理にかなっています。

これらのプロパティを注入するのが簡単なのは、すべてのアプリケーションコンテキストが、String パスを Resource オブジェクトに変換できる特別な JavaBeans PropertyEditor を登録および使用することです。そのため、次の例に示すように、myBean にタイプ Resource のテンプレートプロパティがある場合、そのリソースの単純な文字列で構成できます。

<bean id="myBean" class="...">
    <property name="template" value="some/resource/path/myTemplate.txt"/>
</bean>

リソースパスにはプレフィックスがないことに注意してください。その結果、アプリケーションコンテキスト自体が ResourceLoader として使用されるため、リソース自体は、コンテキストの正確なタイプに応じて ClassPathResourceFileSystemResource、または ServletContextResource を介してロードされます。

特定の Resource タイプを強制的に使用する必要がある場合は、プレフィックスを使用できます。次の 2 つの例は、ClassPathResource および UrlResource を強制する方法を示しています(後者はファイルシステムファイルへのアクセスに使用されます)。

<property name="template" value="classpath:some/resource/path/myTemplate.txt">
<property name="template" value="file:///some/resource/path/myTemplate.txt"/>

2.7. アプリケーションコンテキストとリソースパス

このセクションでは、XML で機能するショートカット、ワイルドカードの使用方法、その他の詳細など、リソースを使用してアプリケーションコンテキストを作成する方法について説明します。

2.7.1. アプリケーションコンテキストの構築

(特定のアプリケーションコンテキストタイプの)アプリケーションコンテキストコンストラクターは、通常、コンテキストの定義を構成する XML ファイルなど、リソースのロケーションパスとして文字列または文字列の配列を取ります。

そのようなロケーションパスにプレフィックスがない場合、そのパスから作成され、Bean 定義のロードに使用される特定の Resource タイプは、特定のアプリケーションコンテキストに依存し、適切です。例: ClassPathXmlApplicationContext を作成する次の例を検討してください。

Java
ApplicationContext ctx = new ClassPathXmlApplicationContext("conf/appContext.xml");
Kotlin
val ctx = ClassPathXmlApplicationContext("conf/appContext.xml")

ClassPathResource が使用されるため、Bean 定義はクラスパスからロードされます。ただし、FileSystemXmlApplicationContext を作成する次の例を検討してください。

Java
ApplicationContext ctx =
    new FileSystemXmlApplicationContext("conf/appContext.xml");
Kotlin
val ctx = FileSystemXmlApplicationContext("conf/appContext.xml")

これで、Bean 定義がファイルシステムの場所からロードされます(この場合、現在の作業ディレクトリに相対的です)。

ロケーションパスで特別なクラスパスプレフィックスまたは標準 URL プレフィックスを使用すると、定義を読み込むために作成された Resource のデフォルトタイプがオーバーライドされることに注意してください。次の例を考えてみましょう。

Java
ApplicationContext ctx =
    new FileSystemXmlApplicationContext("classpath:conf/appContext.xml");
Kotlin
val ctx = FileSystemXmlApplicationContext("classpath:conf/appContext.xml")

FileSystemXmlApplicationContext を使用すると、クラスパスから Bean 定義がロードされます。ただし、まだ FileSystemXmlApplicationContext です。その後 ResourceLoader として使用される場合、接頭辞のないパスはファイルシステムパスとして扱われます。

ClassPathXmlApplicationContext インスタンスの構築  —  ショートカット

ClassPathXmlApplicationContext は、便利なインスタンス化を可能にするいくつかのコンストラクターを公開します。基本的な考え方は、XML ファイル自体のファイル名のみ(先頭のパス情報なし)を含むストリング配列のみを提供し、Class も提供できるということです。ClassPathXmlApplicationContext は、指定されたクラスからパス情報を取得します。

次のディレクトリレイアウトを検討してください。

com/
  foo/
    services.xml
    daos.xml
    MessengerService.class

次の例は、services.xml および daos.xml (クラスパス上にある)という名前のファイルで定義された Bean で構成される ClassPathXmlApplicationContext インスタンスをインスタンス化する方法を示しています。

Java
ApplicationContext ctx = new ClassPathXmlApplicationContext(
    new String[] {"services.xml", "daos.xml"}, MessengerService.class);
Kotlin
val ctx = ClassPathXmlApplicationContext(arrayOf("services.xml", "daos.xml"), MessengerService::class.java)

さまざまなコンストラクターの詳細については、api-spring-framework} /context/support/ClassPathXmlApplicationContext.html [ClassPathXmlApplicationContext] javadoc を参照してください。

2.7.2. アプリケーションコンテキストコンストラクターリソースパスのワイルドカード

アプリケーションコンテキストコンストラクター値のリソースパスは、前述のように、それぞれがターゲット Resource への 1 対 1 のマッピングを持つ単純なパスであるか、特別な「classpath *:」プレフィックスまたは内部 Ant を含む場合があります。スタイルの正規表現(Spring の PathMatcher ユーティリティを使用して一致)。後者は両方とも事実上ワイルドカードです。

このメカニズムの用途の 1 つは、コンポーネントスタイルのアプリケーションアセンブリを行う必要がある場合です。すべてのコンポーネントは、既知の場所のパスにコンテキスト定義フラグメントを「公開」できます。また、classpath*: というプレフィックスが付いた同じパスを使用して最終アプリケーションコンテキストが作成されると、すべてのコンポーネントフラグメントが自動的に取得されます。

このワイルドカードは、アプリケーションコンテキストコンストラクター(または PathMatcher ユーティリティクラス階層を直接使用する場合)でのリソースパスの使用に固有であり、構築時に解決されることに注意してください。Resource タイプ自体とは関係ありません。リソースは一度に 1 つのリソースのみを指すため、classpath*: プレフィックスを使用して実際の Resource を構築することはできません。

Ant スタイルのパターン

次の例に示すように、パスの場所には Ant スタイルのパターンを含めることができます。

/WEB-INF/*-context.xml
com/mycompany/**/applicationContext.xml
file:C:/some/path/*-context.xml
classpath:com/mycompany/**/applicationContext.xml

パスの場所に Ant スタイルのパターンが含まれている場合、リゾルバーはより複雑な手順に従ってワイルドカードの解決を試みます。最後の非ワイルドカードセグメントまでのパスに対して Resource を生成し、そこから URL を取得します。この URL が jar: URL またはコンテナー固有のバリアント(WebLogic の zip:、WebSphere の wsjar など)ではない場合、java.io.File がそこから取得され、ファイルシステムを走査してワイルドカードを解決するために使用されます。jar URL の場合、リゾルバーはそこから java.net.JarURLConnection を取得するか、jar URL を手動で解析してから、jar ファイルの内容を走査してワイルドカードを解決します。

移植性への影響

指定されたパスがすでにファイル URL である場合(ベース ResourceLoader が暗黙的にまたは明示的にファイルシステムであるため)、ワイルドカードは完全に移植可能な方法で機能することが保証されます。

指定されたパスがクラスパスの場所である場合、リゾルバーは Classloader.getResource() 呼び出しを行うことにより、最後の非ワイルドカードパスセグメント URL を取得する必要があります。これはパスのノード(最後のファイルではない)であるため、実際には(ClassLoader javadoc で)未定義であり、この場合にどのような URL が返されるかは正確ではありません。実際には、常にディレクトリを表す java.io.File (クラスパスリソースがファイルシステムの場所に解決される)またはある種の jar URL(クラスパスリソースが jar の場所に解決される)です。それでも、この操作には移植性の問題があります。

jar URL が最後の非ワイルドカードセグメントについて取得された場合、リゾルバーは、jar から java.net.JarURLConnection を取得するか、jar URL を手動で解析して、jar の内容を調べてワイルドカードを解決できる必要があります。これはほとんどの環境で機能しますが、他の環境では失敗します。jar からのリソースのワイルドカード解決は、依存する前に特定の環境で徹底的にテストすることを強くお勧めします。

classpath*: プレフィックス

XML ベースのアプリケーションコンテキストを構築するとき、次の例に示すように、ロケーション文字列は特別な classpath*: プレフィックスを使用する場合があります。

Java
ApplicationContext ctx =
    new ClassPathXmlApplicationContext("classpath*:conf/appContext.xml");
Kotlin
val ctx = ClassPathXmlApplicationContext("classpath*:conf/appContext.xml")

この特別なプレフィックスは、指定された名前に一致するすべてのクラスパスリソースを取得し(内部的には、本質的に ClassLoader.getResources(…​) の呼び出しによって発生)、その後、マージして最終的なアプリケーションコンテキスト定義を形成する必要があることを指定します。

ワイルドカードクラスパスは、基になるクラスローダーの getResources() メソッドに依存しています。現在、ほとんどのアプリケーションサーバーは独自のクラスローダー実装を提供しているため、特に jar ファイルを処理する場合、動作が異なる場合があります。classpath* が機能するかどうかを確認する簡単なテストは、クラスローダーを使用して、クラスパス上の jar からファイルをロードすることです: getClass().getClassLoader().getResources("<someFileInsideTheJar>")。同じ名前のファイルを 2 つの異なる場所に配置して、このテストを試してください。不適切な結果が返された場合は、クラスローダーの動作に影響する可能性のある設定について、アプリケーションサーバーのドキュメントを確認してください。

また、ロケーションパスの残りの部分で、classpath*: プレフィックスと PathMatcher パターンを組み合わせることができます(たとえば、classpath*:META-INF/*-beans.xml)。この場合、解決戦略は非常に簡単です: ClassLoader.getResources() 呼び出しを最後の非ワイルドカードパスセグメントで使用して、クラスローダー階層内のすべての一致するリソースを取得し、次に各リソースから、前述の同じ PathMatcher 解決戦略を取得します。ワイルドカードサブパスに使用されます。

ワイルドカードに関するその他の注意事項

classpath* は、Ant スタイルのパターンと組み合わせると、実際のターゲットファイルがファイルシステムに存在しない限り、パターンが開始する前に少なくとも 1 つのルートディレクトリでのみ確実に動作することに注意してください。つまり、classpath*:*.xml などのパターンは、jar ファイルのルートからではなく、展開されたディレクトリのルートからのみファイルを取得する可能性があります。

Spring のクラスパスエントリを取得する機能は、JDK の ClassLoader.getResources() メソッドに由来します。このメソッドは、空の文字列(検索する潜在的なルートを示す)のファイルシステムの場所のみを返します。Spring は URLClassLoader ランタイム構成と jar ファイル内の java.class.path マニフェストも評価しますが、これは移植性のある動作を保証するものではありません。

クラスパスパッケージをスキャンするには、対応するディレクトリエントリがクラスパスに存在する必要があります。Ant を使用して JAR を構築するときは、JAR タスクのファイルのみのスイッチをアクティブにしないでください。また、一部の環境では、クラスパスディレクトリがセキュリティポリシーに基づいて公開されない場合があります。たとえば、JDK 1.7.0_45 以降のスタンドアロンアプリケーション(マニフェストで 'Trusted-Library' を設定する必要があります。https://stackoverflow.com/questions/19394570/java-jre-7u45-breaks-classloader-getresources (英語) を参照してください)。

JDK 9 のモジュールパス(Jigsaw)では、Spring のクラスパススキャンは通常期待どおりに機能します。ここでもリソースを専用ディレクトリに配置することを強くお勧めします。これにより、前述の jar ファイルのルートレベルの検索に関する移植性の問題を回避できます。

検索するルートパッケージが複数のクラスパスの場所で利用可能な場合、classpath: リソースを使用した Ant スタイルのパターンは、一致するリソースの検出を保証しません。次のリソースの場所の例を考えてください。

com/mycompany/package1/service-context.xml

次に、誰かがそのファイルを見つけるために使用する Ant スタイルのパスを考えてみましょう。

classpath:com/mycompany/**/service-context.xml

このようなリソースは 1 つの場所にしか存在しませんが、前の例のようなパスを使用して解決しようとすると、リゾルバーは getResource("com/mycompany"); によって返された(最初の)URL を処理します。このベースパッケージノードが複数のクラスローダーの場所に存在する場合、実際の最終リソースはそこにない可能性があります。このような場合は、ルートパッケージを含むすべてのクラスパスの場所を検索する同じ Ant スタイルのパターンで classpath*: を使用することをお勧めします。

2.7.3. FileSystemResource 警告

FileSystemApplicationContext に接続されていない FileSystemResource (つまり、FileSystemApplicationContext が実際の ResourceLoader ではない場合)は、予想どおりに絶対パスと相対パスを処理します。相対パスは現在の作業ディレクトリからの相対パスですが、絶対パスはファイルシステムのルートからの相対パスです。

ただし、下位互換性(履歴)の理由により、FileSystemApplicationContext が ResourceLoader の場合、これは変更されます。FileSystemApplicationContext は、接続されているすべての FileSystemResource インスタンスに、先頭のスラッシュで始まるかどうかに関係なく、すべてのロケーションパスを強制的に相対パスとして処理させます。実際には、これは次の例が同等であることを意味します。

Java
ApplicationContext ctx =
    new FileSystemXmlApplicationContext("conf/context.xml");
Kotlin
val ctx = FileSystemXmlApplicationContext("conf/context.xml")
Java
ApplicationContext ctx =
    new FileSystemXmlApplicationContext("/conf/context.xml");
Kotlin
val ctx = FileSystemXmlApplicationContext("/conf/context.xml")

次の例も同等です(1 つのケースは相対的で、もう 1 つのケースは絶対的であるため、それらが異なることは理にかなっていますが)。

Java
FileSystemXmlApplicationContext ctx = ...;
ctx.getResource("some/resource/path/myTemplate.txt");
Kotlin
val ctx: FileSystemXmlApplicationContext = ...
ctx.getResource("some/resource/path/myTemplate.txt")
Java
FileSystemXmlApplicationContext ctx = ...;
ctx.getResource("/some/resource/path/myTemplate.txt");
Kotlin
val ctx: FileSystemXmlApplicationContext = ...
ctx.getResource("/some/resource/path/myTemplate.txt")

実際には、真の絶対ファイルシステムパスが必要な場合は、FileSystemResource または FileSystemXmlApplicationContext での絶対パスの使用を避け、file: URL プレフィックスを使用して UrlResource の使用を強制する必要があります。次の例は、その方法を示しています。

Java
// actual context type doesn't matter, the Resource will always be UrlResource
ctx.getResource("file:///some/resource/path/myTemplate.txt");
Kotlin
// actual context type doesn't matter, the Resource will always be UrlResource
ctx.getResource("file:///some/resource/path/myTemplate.txt")
Java
// force this FileSystemXmlApplicationContext to load its definition via a UrlResource
ApplicationContext ctx =
    new FileSystemXmlApplicationContext("file:///conf/context.xml");
Kotlin
// force this FileSystemXmlApplicationContext to load its definition via a UrlResource
val ctx = FileSystemXmlApplicationContext("file:///conf/context.xml")

3. 検証、データバインディング、型変換

ビジネスロジックとして検証を検討することには長所と短所があり、Spring は検証(およびデータバインディング)の設計を提供します。具体的には、検証は Web 層に結び付けられるべきではなく、ローカライズが容易である必要があり、利用可能な検証ツールをプラグインできる必要があります。これらの懸念を考慮して、Spring は Validator 契約を提供します。これは、基本的であり、アプリケーションのすべてのレイヤーで非常に有用です。

データバインディングは、ユーザー入力をアプリケーションのドメインモデル(またはユーザー入力の処理に使用するオブジェクト)に動的にバインドできます。Spring は、まさにそれを行うために適切な名前の DataBinder を提供します。Validator および DataBinder は validation パッケージを構成し、これは主に Web レイヤーで使用されますが、これに限定されません。

BeanWrapper は Spring Framework の基本概念であり、多くの場所で使用されています。ただし、おそらく BeanWrapper を直接使用する必要はありません。ただし、これはリファレンスドキュメントであるため、何らかの説明が適切であると感じました。BeanWrapper については、この章で説明します。これを使用する場合は、データをオブジェクトにバインドするときに使用する可能性が高いからです。

Spring の DataBinder と下位レベルの BeanWrapper はどちらも PropertyEditorSupport 実装を使用して、プロパティ値を解析およびフォーマットします。PropertyEditor および PropertyEditorSupport タイプは JavaBeans 仕様の一部であり、この章でも説明されています。Spring 3 は、一般的な型変換機能を提供する core.convert パッケージと、UI フィールド値をフォーマットするための高レベルの「フォーマット」パッケージを導入しました。これらのパッケージは、PropertyEditorSupport 実装のより簡単な代替手段として使用できます。これらについても、この章で説明します。

Spring は、セットアップインフラストラクチャと Spring 独自の Validator 契約へのアダプターを通じて Java Bean 検証をサポートします。アプリケーションは、Java Bean 検証に従って、Bean 検証をグローバルに一度有効にして、すべての検証ニーズに対してのみ使用できます。Web レイヤーでは、DataBinder の構成に従って、アプリケーションは DataBinder ごとにコントローラーローカル Spring Validator インスタンスをさらに登録できます。これは、カスタム検証ロジックのプラグインに役立ちます。

3.1. Spring の検証インターフェースを使用した検証

Spring は、オブジェクトの検証に使用できる Validator インターフェースを備えています。Validator インターフェースは Errors オブジェクトを使用して機能するため、検証中にバリデーターは Errors オブジェクトに検証エラーを報告できます。

次の小さなデータオブジェクトの例を考えてみましょう。

Java
public class Person {

    private String name;
    private int age;

    // the usual getters and setters...
}
Kotlin
class Person(val name: String, val age: Int)

次の例では、org.springframework.validation.Validator インターフェースの以下の 2 つのメソッドを実装することにより、Person クラスの検証動作を提供します。

  • supports(Class): この Validator は、提供された Class のインスタンスを検証できますか?

  • validate(Object, org.springframework.validation.Errors): 指定されたオブジェクトを検証し、検証エラーの場合、指定された Errors オブジェクトに登録します。

Spring Framework が提供する ValidationUtils ヘルパークラスを知っている場合は特に、Validator の実装は非常に簡単です。次の例では、Person インスタンスに Validator を実装しています。

Java
public class PersonValidator implements Validator {

    /**
     * This Validator validates only Person instances
     */
    public boolean supports(Class clazz) {
        return Person.class.equals(clazz);
    }

    public void validate(Object obj, Errors e) {
        ValidationUtils.rejectIfEmpty(e, "name", "name.empty");
        Person p = (Person) obj;
        if (p.getAge() < 0) {
            e.rejectValue("age", "negativevalue");
        } else if (p.getAge() > 110) {
            e.rejectValue("age", "too.darn.old");
        }
    }
}
Kotlin
class PersonValidator : Validator {

    /*
     * This Validator validates only Person instances
     */
    override fun supports(clazz: Class<>): Boolean {
        return Person::class.java == clazz
    }

    override fun validate(obj: Any, e: Errors) {
        ValidationUtils.rejectIfEmpty(e, "name", "name.empty")
        val p = obj as Person
        if (p.age < 0) {
            e.rejectValue("age", "negativevalue")
        } else if (p.age > 110) {
            e.rejectValue("age", "too.darn.old")
        }
    }
}

ValidationUtils クラスの staticrejectIfEmpty(..) メソッドは、null または空の文字列の場合、name プロパティを拒否するために使用されます。ValidationUtils (Javadoc) javadoc を見て、前に示した例以外にどの機能が提供されるかを確認してください。

リッチオブジェクトの各ネストされたオブジェクトを検証するために単一の Validator クラスを実装することは確かに可能ですが、独自の Validator 実装でオブジェクトの各ネストされたクラスの検証ロジックをカプセル化する方が良い場合があります。「リッチ」オブジェクトの簡単な例は、2 つの String プロパティ(1 番目と 2 番目の名前)と複雑な Address オブジェクトで構成される Customer です。Address オブジェクトは Customer オブジェクトとは独立して使用できるため、別個の AddressValidator が実装されています。CustomerValidator で AddressValidator クラスに含まれるロジックをコピーアンドペーストに頼らずに再利用したい場合、次の例に示すように、CustomerValidator 内で AddressValidator を依存性注入またはインスタンス化できます。

Java
public class CustomerValidator implements Validator {

    private final Validator addressValidator;

    public CustomerValidator(Validator addressValidator) {
        if (addressValidator == null) {
            throw new IllegalArgumentException("The supplied [Validator] is " +
                "required and must not be null.");
        }
        if (!addressValidator.supports(Address.class)) {
            throw new IllegalArgumentException("The supplied [Validator] must " +
                "support the validation of [Address] instances.");
        }
        this.addressValidator = addressValidator;
    }

    /**
     * This Validator validates Customer instances, and any subclasses of Customer too
     */
    public boolean supports(Class clazz) {
        return Customer.class.isAssignableFrom(clazz);
    }

    public void validate(Object target, Errors errors) {
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName", "field.required");
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "surname", "field.required");
        Customer customer = (Customer) target;
        try {
            errors.pushNestedPath("address");
            ValidationUtils.invokeValidator(this.addressValidator, customer.getAddress(), errors);
        } finally {
            errors.popNestedPath();
        }
    }
}
Kotlin
class CustomerValidator(private val addressValidator: Validator) : Validator {

    init {
        if (addressValidator == null) {
            throw IllegalArgumentException("The supplied [Validator] is required and must not be null.")
        }
        if (!addressValidator.supports(Address::class.java)) {
            throw IllegalArgumentException("The supplied [Validator] must support the validation of [Address] instances.")
        }
    }

    /*
    * This Validator validates Customer instances, and any subclasses of Customer too
    */
    override fun supports(clazz: Class<>): Boolean {
        return Customer::class.java.isAssignableFrom(clazz)
    }

    override fun validate(target: Any, errors: Errors) {
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName", "field.required")
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "surname", "field.required")
        val customer = target as Customer
        try {
            errors.pushNestedPath("address")
            ValidationUtils.invokeValidator(this.addressValidator, customer.address, errors)
        } finally {
            errors.popNestedPath()
        }
    }
}

検証エラーは、バリデーターに渡される Errors オブジェクトに報告されます。Spring Web MVC の場合、<spring:bind/> タグを使用してエラーメッセージをインスペクションできますが、Errors オブジェクトを自分でインスペクションすることもできます。提供するメソッドの詳細については、javadoc を参照してください

3.2. コードをエラーメッセージに解決する

データバインディングと検証について説明しました。このセクションでは、検証エラーに対応するメッセージの出力について説明します。前のセクションで示した例では、name フィールドと age フィールドを拒否しました。MessageSource を使用してエラーメッセージを出力する場合は、フィールドを拒否するときに指定するエラーコードを使用します (この場合の 'name' と 'age' )。Errors インターフェースから ( ValidationUtils クラスなどを使用して、直接または間接的に) rejectValue または他の reject メソッドのいずれかを呼び出すと、基礎となるインプリメンテーションは、渡されたコードを登録するだけでなく、多数の追加エラーコードも登録します。MessageCodesResolver は、Errors インターフェースがどのエラーコードを登録するかを決定します。デフォルトでは、DefaultMessageCodesResolver が使用されます。DefaultMessageCodesResolver (たとえば) は、指定したコードでメッセージを登録するだけでなく、reject メソッドに渡したフィールド名を含むメッセージも登録します。そのため、rejectValue("age", "too.darn.old") を使用してフィールドを拒否した場合、too.darn.old コードとは別に、Spring は too.darn.old.age および too.darn.old.age.int (最初のフィールドにはフィールド名が含まれ、2 番目のフィールドにはフィールドのタイプが含まれます) も登録します。これは、開発者がエラーメッセージをターゲットにする際の便宜を図るために行われます。

MessageCodesResolver およびデフォルト戦略の詳細は、それぞれ MessageCodesResolver (Javadoc) および DefaultMessageCodesResolver (Javadoc) の javadoc にあります。

3.3. Bean 操作と BeanWrapper

org.springframework.beans パッケージは、JavaBeans 標準に準拠しています。JavaBean は、デフォルトの引数なしのコンストラクターを持つクラスであり、命名規則に従います(たとえば、bingoMadness という名前のプロパティには setter メソッド setBingoMadness(..) および getter メソッド getBingoMadness() があります)。JavaBeans と仕様の詳細については、javabeans: Oracle を参照してください。

Bean パッケージの非常に重要なクラスの 1 つは、BeanWrapper インターフェースとそれに対応する実装(BeanWrapperImpl)です。javadoc から引用されているように、BeanWrapper は、プロパティ値の設定と取得(個別または一括)、プロパティ記述子の取得、およびプロパティのクエリを行って読み取り可能または書き込み可能かどうかを判断する機能を提供します。また、BeanWrapper はネストされたプロパティをサポートし、サブプロパティのプロパティを無制限の深さに設定できます。BeanWrapper は、ターゲットクラスのコードをサポートする必要なく、標準 JavaBeans PropertyChangeListeners および VetoableChangeListeners を追加する機能もサポートしています。最後になりましたが、BeanWrapper はインデックス付きプロパティの設定をサポートします。BeanWrapper は通常、アプリケーションコードによって直接使用されるのではなく、DataBinder および BeanFactory によって使用されます。

BeanWrapper の動作方法は、その名前によって部分的に示されます。Bean をラップして、プロパティの設定や取得など、その Bean でアクションを実行します。

3.3.1. 基本およびネストされたプロパティの設定と取得

プロパティの設定と取得は、setPropertyValue および getPropertyValue のオーバーロードされた BeanWrapper のメソッドバリアントを介して行われます。詳細については、Javadoc を参照してください。次の表に、これらの規則の例をいくつか示します。

表 11: プロパティの例
説明

name

getName() または isName() および setName(..) メソッドに対応するプロパティ name を示します。

account.name

(たとえば) getAccount().setName() または getAccount().getName() メソッドに対応するプロパティ account のネストされたプロパティ name を示します。

account[2]

インデックス付きプロパティ account3 番目の要素を示します。インデックス付きプロパティは、タイプ arraylist またはその他の自然に順序付けられたコレクションです。

account[COMPANYNAME]

accountMap プロパティの COMPANYNAME キーでインデックス付けされたマップエントリの値を示します。

BeanWrapper を直接使用する予定がない場合、この次のセクションはそれほど重要ではありません。DataBinder と BeanFactory とそれらのデフォルト実装のみを使用する場合は、PropertyEditorsセクションに進んでください

次の 2 つのサンプルクラスは、BeanWrapper を使用してプロパティを取得および設定します。

Java
public class Company {

    private String name;
    private Employee managingDirector;

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Employee getManagingDirector() {
        return this.managingDirector;
    }

    public void setManagingDirector(Employee managingDirector) {
        this.managingDirector = managingDirector;
    }
}
Kotlin
class Company {
    var name: String? = null
    var managingDirector: Employee? = null
}
Java
public class Employee {

    private String name;

    private float salary;

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public float getSalary() {
        return salary;
    }

    public void setSalary(float salary) {
        this.salary = salary;
    }
}
Kotlin
class Employee {
    var name: String? = null
    var salary: Float? = null
}

次のコードスニペットは、インスタンス化された Companies および Employees のプロパティの一部を取得および操作する方法の例を示しています。

Java
BeanWrapper company = new BeanWrapperImpl(new Company());
// setting the company name..
company.setPropertyValue("name", "Some Company Inc.");
// ... can also be done like this:
PropertyValue value = new PropertyValue("name", "Some Company Inc.");
company.setPropertyValue(value);

// ok, let's create the director and tie it to the company:
BeanWrapper jim = new BeanWrapperImpl(new Employee());
jim.setPropertyValue("name", "Jim Stravinsky");
company.setPropertyValue("managingDirector", jim.getWrappedInstance());

// retrieving the salary of the managingDirector through the company
Float salary = (Float) company.getPropertyValue("managingDirector.salary");
Kotlin
val company = BeanWrapperImpl(Company())
// setting the company name..
company.setPropertyValue("name", "Some Company Inc.")
// ... can also be done like this:
val value = PropertyValue("name", "Some Company Inc.")
company.setPropertyValue(value)

// ok, let's create the director and tie it to the company:
val jim = BeanWrapperImpl(Employee())
jim.setPropertyValue("name", "Jim Stravinsky")
company.setPropertyValue("managingDirector", jim.wrappedInstance)

// retrieving the salary of the managingDirector through the company
val salary = company.getPropertyValue("managingDirector.salary") as Float?

3.3.2. ビルトイン PropertyEditor 実装

Spring は、PropertyEditor の概念を使用して、Object と String の間の変換を行います。オブジェクト自体とは異なる方法でプロパティを表すと便利です。例: Date は人間が読める形式で表現できます(String'2007-14-09' のように)が、人間が読める形式を元の日付に戻すことができます(あるいは、人間が読める形式で入力した日付を戻すことができます) Date オブジェクトへ)。この動作は、タイプ java.beans.PropertyEditor のカスタムエディターを登録することで実現できます。BeanWrapper または特定の IoC コンテナー(前の章で説明)でカスタムエディターを登録すると、プロパティを目的のタイプに変換する方法がわかります。PropertyEditor の詳細については、Oracle の java.beans パッケージの javadoc を参照してください。

Spring でプロパティの編集が使用されるいくつかの例:

  • Bean のプロパティの設定は、PropertyEditor 実装を使用して行われます。XML ファイルで宣言する Bean のプロパティの値として String を使用する場合、Spring(対応するプロパティの setter に Class パラメーターがある場合)は、ClassEditor を使用してパラメーターを Class オブジェクトに解決しようとします。

  • Spring の MVC フレームワークでの HTTP リクエストパラメーターの解析は、CommandController のすべてのサブクラスで手動でバインドできる、あらゆる種類の PropertyEditor 実装を使用して行われます。

Spring には、PropertyEditor が多数実装されており、簡単に作業できます。それらはすべて org.springframework.beans.propertyeditors パッケージにあります。ほとんど(すべてではありませんが、次の表に示すように)は、デフォルトで BeanWrapperImpl によって登録されています。プロパティエディターを何らかの方法で構成できる場合でも、独自のバリアントを登録してデフォルトのバリアントをオーバーライドできます。次の表は、Spring が提供するさまざまな PropertyEditor 実装を示しています。

表 12: ビルトイン PropertyEditor 実装
クラス 説明

ByteArrayPropertyEditor

バイト配列のエディター。文字列を対応するバイト表現に変換します。BeanWrapperImpl によってデフォルトで登録されています。

ClassEditor

クラスを実際のクラスに、またはその逆に表す文字列を解析します。クラスが見つからない場合、IllegalArgumentException がスローされます。デフォルトでは、BeanWrapperImpl によって登録されます。

CustomBooleanEditor

Boolean プロパティ用のカスタマイズ可能なプロパティエディター。デフォルトでは、BeanWrapperImpl によって登録されますが、そのカスタムインスタンスをカスタムエディターとして登録することでオーバーライドできます。

CustomCollectionEditor

ソース Collection を特定のターゲット Collection タイプに変換するコレクションのプロパティエディター。

CustomDateEditor

java.util.Date 用のカスタマイズ可能なプロパティエディター、カスタム DateFormat をサポートします。デフォルトでは登録されていません。必要に応じて適切な形式でユーザー登録する必要があります。

CustomNumberEditor

IntegerLongFloat や Double など、Number サブクラスのカスタマイズ可能なプロパティエディター。デフォルトでは、BeanWrapperImpl によって登録されますが、そのカスタムインスタンスをカスタムエディターとして登録することでオーバーライドできます。

FileEditor

文字列を java.io.File オブジェクトに解決します。デフォルトでは、BeanWrapperImpl によって登録されます。

InputStreamEditor

文字列を取得し、InputStream プロパティを文字列として直接設定できるように、ResourceEditor および Resource を介して InputStream を生成できる一方向プロパティエディター。デフォルトの使用箇所では、InputStream が閉じられないことに注意してください。デフォルトでは、BeanWrapperImpl によって登録されます。

LocaleEditor

文字列を Locale オブジェクトに、またはその逆に解決できます(文字列形式は [country][variant] で、Locale の toString() メソッドと同じです)。デフォルトでは、BeanWrapperImpl によって登録されます。

PatternEditor

文字列を java.util.regex.Pattern オブジェクトに、またはその逆に解決できます。

PropertiesEditor

文字列(java.util.Properties クラスの javadoc で定義された形式でフォーマットされた)を Properties オブジェクトに変換できます。デフォルトでは、BeanWrapperImpl によって登録されます。

StringTrimmerEditor

文字列をトリムするプロパティエディター。オプションで、空の文字列を null 値に変換できます。デフォルトでは登録されていません — ユーザー登録する必要があります。

URLEditor

URL の文字列表現を実際の URL オブジェクトに解決できます。デフォルトでは、BeanWrapperImpl によって登録されます。

Spring は、java.beans.PropertyEditorManager を使用して、必要なプロパティエディターの検索パスを設定します。検索パスには sun.bean.editors も含まれます。これには、FontColor などのタイプの PropertyEditor 実装と、ほとんどのプリミティブタイプが含まれます。また、標準の JavaBeans インフラストラクチャは、PropertyEditor クラスを、それらが処理するクラスと同じパッケージ内にあり、そのクラスと同じ名前で、Editor が追加されている場合、自動的に検出します。例: SomethingEditor クラスを認識し、Something -typed プロパティの PropertyEditor として使用するには、次のクラスとパッケージ構造を使用できます。

com
  chank
    pop
      Something
      SomethingEditor // the PropertyEditor for the Something class

ここでも標準 BeanInfo JavaBeans メカニズムを使用できることに注意してください(ここである程度説明します)。次の例では、BeanInfo メカニズムを使用して、1 つ以上の PropertyEditor インスタンスを関連クラスのプロパティに明示的に登録します。

com
  chank
    pop
      Something
      SomethingBeanInfo // the BeanInfo for the Something class

参照される SomethingBeanInfo クラスの次の Java ソースコードは、CustomNumberEditor を Something クラスの age プロパティに関連付けます。

Java
public class SomethingBeanInfo extends SimpleBeanInfo {

    public PropertyDescriptor[] getPropertyDescriptors() {
        try {
            final PropertyEditor numberPE = new CustomNumberEditor(Integer.class, true);
            PropertyDescriptor ageDescriptor = new PropertyDescriptor("age", Something.class) {
                public PropertyEditor createPropertyEditor(Object bean) {
                    return numberPE;
                };
            };
            return new PropertyDescriptor[] { ageDescriptor };
        }
        catch (IntrospectionException ex) {
            throw new Error(ex.toString());
        }
    }
}
Kotlin
class SomethingBeanInfo : SimpleBeanInfo() {

    override fun getPropertyDescriptors(): Array<PropertyDescriptor> {
        try {
            val numberPE = CustomNumberEditor(Int::class.java, true)
            val ageDescriptor = object : PropertyDescriptor("age", Something::class.java) {
                override fun createPropertyEditor(bean: Any): PropertyEditor {
                    return numberPE
                }
            }
            return arrayOf(ageDescriptor)
        } catch (ex: IntrospectionException) {
            throw Error(ex.toString())
        }

    }
}
追加のカスタム PropertyEditor 実装の登録

Bean プロパティを文字列値として設定する場合、Spring IoC コンテナーは最終的に標準 JavaBeans PropertyEditor 実装を使用して、これらの文字列をプロパティの複雑なタイプに変換します。Spring は、多数のカスタム PropertyEditor 実装を事前登録します(たとえば、ストリングとして表現されたクラス名を Class オブジェクトに変換するため)。さらに、Java の標準 JavaBeans PropertyEditor ルックアップメカニズムにより、クラスの PropertyEditor に適切な名前を付けて、サポートを提供するクラスと同じパッケージに配置して、自動的に検出できるようにします。

他のカスタム PropertyEditors を登録する必要がある場合、いくつかのメカニズムが利用可能です。通常、便利または推奨されない最も手動のアプローチは、BeanFactory 参照があると仮定して、ConfigurableBeanFactory インターフェースの registerCustomEditor() メソッドを使用することです。別の(少し便利な)メカニズムは、CustomEditorConfigurer と呼ばれる特別な Bean ファクトリポストプロセッサを使用することです。Bean ファクトリポストプロセッサは BeanFactory 実装で使用できますが、CustomEditorConfigurer にはネストされたプロパティ設定があるため、ApplicationContext で使用することを強くお勧めします。他の Bean と同様の方法でデプロイできます。自動的に検出および適用されます。

すべての Bean ファクトリおよびアプリケーションコンテキストは、BeanWrapper を使用してプロパティ変換を処理することにより、自動的に多数の組み込みプロパティエディターを使用することに注意してください。BeanWrapper が登録する標準プロパティエディターは、前のセクションにリストされています。さらに、ApplicationContexts は、特定のアプリケーションコンテキストタイプに適した方法でリソースルックアップを処理するためのエディターをオーバーライドまたは追加します。

標準の JavaBeans PropertyEditor インスタンスは、文字列として表現されたプロパティ値をプロパティの実際の複合型に変換するために使用されます。Bean ファクトリポストプロセッサである CustomEditorConfigurer を使用して、ApplicationContext に追加の PropertyEditor インスタンスのサポートを簡単に追加できます。

ExoticType というユーザークラスと、ExoticType をプロパティとして設定する必要がある DependsOnExoticType という別のクラスを定義する次の例を考えてみましょう。

Java
package example;

public class ExoticType {

    private String name;

    public ExoticType(String name) {
        this.name = name;
    }
}

public class DependsOnExoticType {

    private ExoticType type;

    public void setType(ExoticType type) {
        this.type = type;
    }
}
Kotlin
package example

class ExoticType(val name: String)

class DependsOnExoticType {

    var type: ExoticType? = null
}

物事が適切に設定されたら、type プロパティを文字列として割り当てることができます。これは、PropertyEditor が実際の ExoticType インスタンスに変換します。次の Bean 定義は、この関連をセットアップする方法を示しています。

<bean id="sample" class="example.DependsOnExoticType">
    <property name="type" value="aNameForExoticType"/>
</bean>

PropertyEditor の実装は次のようになります。

Java
// converts string representation to ExoticType object
package example;

public class ExoticTypeEditor extends PropertyEditorSupport {

    public void setAsText(String text) {
        setValue(new ExoticType(text.toUpperCase()));
    }
}
Kotlin
// converts string representation to ExoticType object
package example

import java.beans.PropertyEditorSupport

class ExoticTypeEditor : PropertyEditorSupport() {

    override fun setAsText(text: String) {
        value = ExoticType(text.toUpperCase())
    }
}

最後に、次の例は、CustomEditorConfigurer を使用して新しい PropertyEditor を ApplicationContext に登録する方法を示しています。これにより、必要に応じて PropertyEditor を使用できるようになります。

<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
    <property name="customEditors">
        <map>
            <entry key="example.ExoticType" value="example.ExoticTypeEditor"/>
        </map>
    </property>
</bean>
PropertyEditorRegistrar を使用する

プロパティエディターを Spring コンテナーに登録する別のメカニズムは、PropertyEditorRegistrar を作成して使用することです。このインターフェースは、いくつかの異なる状況で同じプロパティエディターのセットを使用する必要がある場合に特に役立ちます。対応するレジストラを作成し、それぞれの場合に再利用できます。PropertyEditorRegistrar インスタンスは、PropertyEditorRegistry と呼ばれるインターフェースと連携して動作します。PropertyEditorRegistry は、Spring BeanWrapper (および DataBinder)によって実装されるインターフェースです。PropertyEditorRegistrar インスタンスは、setPropertyEditorRegistrars(..) というプロパティを公開する CustomEditorConfigurer (ここで説明)と組み合わせて使用すると特に便利です。この方法で CustomEditorConfigurer に追加された PropertyEditorRegistrar インスタンスは、DataBinder および Spring MVC コントローラーと簡単に共有できます。さらに、カスタムエディターでの同期の必要性を回避します。PropertyEditorRegistrar は、各 Bean の作成試行ごとに新しい PropertyEditor インスタンスを作成することが期待されています。

次の例は、独自の PropertyEditorRegistrar 実装を作成する方法を示しています。

Java
package com.foo.editors.spring;

public final class CustomPropertyEditorRegistrar implements PropertyEditorRegistrar {

    public void registerCustomEditors(PropertyEditorRegistry registry) {

        // it is expected that new PropertyEditor instances are created
        registry.registerCustomEditor(ExoticType.class, new ExoticTypeEditor());

        // you could register as many custom property editors as are required here...
    }
}
Kotlin
package com.foo.editors.spring

import org.springframework.beans.PropertyEditorRegistrar
import org.springframework.beans.PropertyEditorRegistry

class CustomPropertyEditorRegistrar : PropertyEditorRegistrar {

    override fun registerCustomEditors(registry: PropertyEditorRegistry) {

        // it is expected that new PropertyEditor instances are created
        registry.registerCustomEditor(ExoticType::class.java, ExoticTypeEditor())

        // you could register as many custom property editors as are required here...
    }
}

PropertyEditorRegistrar の実装例については、org.springframework.beans.support.ResourceEditorRegistrar も参照してください。registerCustomEditors(..) メソッドの実装で、各プロパティエディターの新しいインスタンスがどのように作成されるかに注意してください。

次の例は、CustomEditorConfigurer を構成し、それに CustomPropertyEditorRegistrar のインスタンスを挿入する方法を示しています。

<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
    <property name="propertyEditorRegistrars">
        <list>
            <ref bean="customPropertyEditorRegistrar"/>
        </list>
    </property>
</bean>

<bean id="customPropertyEditorRegistrar"
    class="com.foo.editors.spring.CustomPropertyEditorRegistrar"/>

最後に(そして Spring の MVC Web フレームワークを使用している人のためにこの章の焦点から少し離れて)、PropertyEditorRegistrars をデータバインディング Controllers (SimpleFormController など)と組み合わせて使用すると非常に便利です。次の例では、initBinder(..) メソッドの実装で PropertyEditorRegistrar を使用しています。

Java
public final class RegisterUserController extends SimpleFormController {

    private final PropertyEditorRegistrar customPropertyEditorRegistrar;

    public RegisterUserController(PropertyEditorRegistrar propertyEditorRegistrar) {
        this.customPropertyEditorRegistrar = propertyEditorRegistrar;
    }

    protected void initBinder(HttpServletRequest request,
            ServletRequestDataBinder binder) throws Exception {
        this.customPropertyEditorRegistrar.registerCustomEditors(binder);
    }

    // other methods to do with registering a User
}
Kotlin
class RegisterUserController(
    private val customPropertyEditorRegistrar: PropertyEditorRegistrar) : SimpleFormController() {

    protected fun initBinder(request: HttpServletRequest,
                            binder: ServletRequestDataBinder) {
        this.customPropertyEditorRegistrar.registerCustomEditors(binder)
    }

    // other methods to do with registering a User
}

このスタイルの PropertyEditor 登録は簡潔なコードにつながり(initBinder(..) の実装は 1 行のみです)、共通の PropertyEditor 登録コードをクラスにカプセル化し、必要な数の Controllers 間で共有することができます。

3.4. Spring タイプ変換

Spring 3 は、一般的な型変換システムを提供する core.convert パッケージを導入しました。システムは、型変換ロジックを実装する SPI と、実行時に型変換を実行する API を定義します。Spring コンテナー内では、このシステムを PropertyEditor 実装の代わりとして使用して、外部化された Bean プロパティ値文字列を必要なプロパティタイプに変換できます。型変換が必要なアプリケーションの任意の場所でパブリック API を使用することもできます。

3.4.1. コンバーター SPI

次のインターフェース定義が示すように、型変換ロジックを実装するための SPI は単純で厳密に型指定されています。

Java
package org.springframework.core.convert.converter;

public interface Converter<S, T> {

    T convert(S source);
}
Kotlin
package org.springframework.core.convert.converter

interface Converter<S, T> {

    fun convert(source: S): T
}

独自のコンバーターを作成するには、Converter インターフェースを実装し、変換元のタイプとして S を、変換先のタイプとして T をパラメーター化します。S のコレクションまたは配列を T の配列またはコレクションに変換する必要がある場合、委譲配列またはコレクションコンバーターも登録されている場合(DefaultConversionService はデフォルトで行います)、このようなコンバーターを透過的に適用することもできます。

convert(S) を呼び出すたびに、ソース引数が null でないことが保証されます。変換が失敗した場合、Converter は未チェックの例外をスローする場合があります。具体的には、IllegalArgumentException をスローして、無効なソース値を報告する必要があります。Converter 実装がスレッドセーフであることを確認してください。

core.convert.support パッケージには、便宜上いくつかのコンバーター実装が提供されています。これらには、文字列から数値およびその他の一般的なタイプへのコンバーターが含まれます。次のリストは、典型的な Converter 実装である StringToInteger クラスを示しています。

Java
package org.springframework.core.convert.support;

final class StringToInteger implements Converter<String, Integer> {

    public Integer convert(String source) {
        return Integer.valueOf(source);
    }
}
Kotlin
package org.springframework.core.convert.support

import org.springframework.core.convert.converter.Converter

internal class StringToInteger : Converter<String, Int> {

    override fun convert(source: String): Int? {
        return Integer.valueOf(source)
    }
}

3.4.2. ConverterFactory を使用する

クラス階層全体の変換ロジックを集中化する必要がある場合(たとえば、String から Enum オブジェクトに変換する場合)、次の例に示すように ConverterFactory を実装できます。

Java
package org.springframework.core.convert.converter;

public interface ConverterFactory<S, R> {

    <T extends R> Converter<S, T> getConverter(Class<T> targetType);
}
Kotlin
package org.springframework.core.convert.converter

interface ConverterFactory<S, R> {

    fun <T : R> getConverter(targetType: Class<T>): Converter<S, T>
}

S を変換元の型に、R を変換先のクラスの範囲を定義する基本型にパラメーター化します。次に getConverter(Class<T>) を実装します。ここで、T は R のサブクラスです。

例として StringToEnumConverterFactory を検討してください。

Java
package org.springframework.core.convert.support;

final class StringToEnumConverterFactory implements ConverterFactory<String, Enum> {

    public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) {
        return new StringToEnumConverter(targetType);
    }

    private final class StringToEnumConverter<T extends Enum> implements Converter<String, T> {

        private Class<T> enumType;

        public StringToEnumConverter(Class<T> enumType) {
            this.enumType = enumType;
        }

        public T convert(String source) {
            return (T) Enum.valueOf(this.enumType, source.trim());
        }
    }
}

3.4.3. GenericConverter を使用する

洗練された Converter 実装が必要な場合は、GenericConverter インターフェースの使用を検討してください。Converter よりも柔軟性がありますが、型付けがそれほど強力ではない署名では、GenericConverter は複数のソースタイプとターゲットタイプ間の変換をサポートします。さらに、GenericConverter は、変換ロジックを実装するときに使用できるソースおよびターゲットフィールドコンテキストを使用可能にします。このようなコンテキストにより、フィールドのアノテーションまたはフィールドの署名で宣言された一般的な情報によって型変換を実行できます。次のリストは、GenericConverter のインターフェース定義を示しています。

Java
package org.springframework.core.convert.converter;

public interface GenericConverter {

    public Set<ConvertiblePair> getConvertibleTypes();

    Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
}
Kotlin
package org.springframework.core.convert.converter

interface GenericConverter {

    fun getConvertibleTypes(): Set<ConvertiblePair>?

    fun convert(@Nullable source: Any?, sourceType: TypeDescriptor, targetType: TypeDescriptor): Any?
}

GenericConverter を実装するには、getConvertibleTypes() がサポートされているソース→ターゲットタイプのペアを返すようにします。次に、convert(Object, TypeDescriptor, TypeDescriptor) を実装して、変換ロジックを含めます。ソース TypeDescriptor は、変換される値を保持するソースフィールドへのアクセスを提供します。ターゲット TypeDescriptor は、変換された値が設定されるターゲットフィールドへのアクセスを提供します。

GenericConverter の良い例は、Java 配列とコレクションの間で変換するコンバーターです。このような ArrayToCollectionConverter は、ターゲットコレクションタイプを宣言するフィールドを内省して、コレクションの要素タイプを解決します。これにより、コレクションがターゲットフィールドに設定される前に、ソース配列内の各要素がコレクション要素タイプに変換されます。

GenericConverter はより複雑な SPI インターフェースであるため、必要な場合にのみ使用してください。基本的な型変換のニーズには、Converter または ConverterFactory を優先してください。
ConditionalGenericConverter を使用する

特定の条件が当てはまる場合にのみ Converter を実行したい場合があります。例:特定のアノテーションがターゲットフィールドに存在する場合にのみ Converter を実行したり、特定のメソッド(static valueOf メソッドなど)がターゲットクラスに定義されている場合にのみ Converter を実行したい場合があります。ConditionalGenericConverter は GenericConverter および ConditionalConverter インターフェースの結合であり、このようなカスタム一致条件を定義できます。

Java
public interface ConditionalConverter {

    boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
}

public interface ConditionalGenericConverter extends GenericConverter, ConditionalConverter {
}
Kotlin
interface ConditionalConverter {

    fun matches(sourceType: TypeDescriptor, targetType: TypeDescriptor): Boolean
}

interface ConditionalGenericConverter : GenericConverter, ConditionalConverter

ConditionalGenericConverter の良い例は、永続エンティティ ID とエンティティ参照の間で変換する EntityConverter です。このような EntityConverter は、ターゲットエンティティタイプが静的ファインダーメソッドを宣言している場合にのみ一致する場合があります(例: findAccount(Long))。matches(TypeDescriptor, TypeDescriptor) の実装で、このようなファインダーメソッドチェックを実行できます。

3.4.4. ConversionService API

ConversionService は、実行時に型変換ロジックを実行するための統合 API を定義します。多くの場合、コンバーターは次のファサードインターフェースの背後で実行されます。

Java
package org.springframework.core.convert;

public interface ConversionService {

    boolean canConvert(Class<?> sourceType, Class<?> targetType);

    <T> T convert(Object source, Class<T> targetType);

    boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType);

    Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);

}
Kotlin
package org.springframework.core.convert

interface ConversionService {

    fun canConvert(sourceType: Class<*>, targetType: Class<*>): Boolean

    fun <T> convert(source: Any, targetType: Class<T>): T

    fun canConvert(sourceType: TypeDescriptor, targetType: TypeDescriptor): Boolean

    fun convert(source: Any, sourceType: TypeDescriptor, targetType: TypeDescriptor): Any

}

ほとんどの ConversionService 実装は、コンバーターを登録するための SPI を提供する ConverterRegistry も実装します。内部的に、ConversionService 実装は、登録されたコンバーターに委譲して、型変換ロジックを実行します。

ConversionService 実装は、core.convert.support パッケージで提供されます。GenericConversionService は、ほとんどの環境での使用に適した汎用実装です。ConversionServiceFactory は、一般的な ConversionService 構成を作成するための便利なファクトリを提供します。

3.4.5. ConversionService の構成

ConversionService は、アプリケーションの起動時にインスタンス化され、複数のスレッド間で共有されるように設計されたステートレスオブジェクトです。Spring アプリケーションでは、通常、各 Spring コンテナー(または ApplicationContext)に対して ConversionService インスタンスを構成します。Spring は、その ConversionService を取得し、フレームワークが型変換を実行する必要がある場合にそれを使用します。この ConversionService を任意の Bean に注入して、直接呼び出すこともできます。

ConversionService が Spring に登録されていない場合、元の PropertyEditor ベースのシステムが使用されます。

デフォルト ConversionService を Spring に登録するには、conversionService の id で次の Bean 定義を追加します。

<bean id="conversionService"
    class="org.springframework.context.support.ConversionServiceFactoryBean"/>

デフォルトの ConversionService は、ストリング、数字、列挙、コレクション、マップ、その他の一般的なタイプ間で変換できます。独自のカスタムコンバーターで既定のコンバーターを補足またはオーバーライドするには、converters プロパティを設定します。プロパティ値は、ConverterConverterFactory または GenericConverter インターフェースのいずれかを実装できます。

<bean id="conversionService"
        class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <set>
            <bean class="example.MyCustomConverter"/>
        </set>
    </property>
</bean>

Spring MVC アプリケーション内で ConversionService を使用することも一般的です。Spring MVC の章の変換とフォーマットを参照してください。

特定の状況では、変換中にフォーマットを適用したい場合があります。FormattingConversionServiceFactoryBean の使用の詳細については、FormatterRegistry SPI を参照してください。

3.4.6. ConversionService をプログラムで使用する

ConversionService インスタンスをプログラムで操作するには、他の Bean の場合と同様に、インスタンスへの参照を注入できます。次の例は、その方法を示しています。

Java
@Service
public class MyService {

    public MyService(ConversionService conversionService) {
        this.conversionService = conversionService;
    }

    public void doIt() {
        this.conversionService.convert(...)
    }
}
Kotlin
@Service
class MyService(private val conversionService: ConversionService) {

    fun doIt() {
        conversionService.convert(...)
    }
}

ほとんどのユースケースでは、targetType を指定する convert メソッドを使用できますが、パラメーター化された要素のコレクションなど、より複雑なタイプでは機能しません。例: Integer の List を String の List にプログラムで変換する場合は、ソースタイプとターゲットタイプの正式な定義を提供する必要があります。

幸いなことに、TypeDescriptor には、次の例に示すように、簡単にするためのさまざまなオプションが用意されています。

Java
DefaultConversionService cs = new DefaultConversionService();

List<Integer> input = ...
cs.convert(input,
    TypeDescriptor.forObject(input), // List<Integer> type descriptor
    TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(String.class)));
Kotlin
val cs = DefaultConversionService()

val input: List<Integer> = ...
cs.convert(input,
        TypeDescriptor.forObject(input), // List<Integer> type descriptor
        TypeDescriptor.collection(List::class.java, TypeDescriptor.valueOf(String::class.java)))

DefaultConversionService は、ほとんどの環境に適したコンバーターを自動的に登録することに注意してください。これには、コレクションコンバーター、スカラーコンバーター、基本的な Object から String コンバーターが含まれます。DefaultConversionService クラスの静的 addDefaultConverters メソッドを使用して、同じコンバーターを任意の ConverterRegistry に登録できます。

値型のコンバーターは配列およびコレクションに再利用されるため、標準のコレクション処理が適切であると仮定すると、S の Collection から T の Collection に変換する特定のコンバーターを作成する必要はありません。

3.5. Spring フィールドのフォーマット

前のセクションで説明したように、core.convert は汎用の型変換システムです。統一された ConversionService API と、あるタイプから別のタイプへの変換ロジックを実装するための厳密に型指定された Converter SPI を提供します。Spring コンテナーは、このシステムを使用して Bean プロパティ値をバインドします。さらに、Spring Expression Language(SpEL)と DataBinder の両方がこのシステムを使用してフィールド値をバインドします。例:SpEL が expression.setValue(Object bean, Object value) の試行を完了するために Short を Long に強制する必要がある場合、core.convert システムは強制を実行します。

次に、Web アプリケーションやデスクトップアプリケーションなどの一般的なクライアント環境のタイプ変換要件を検討します。このような環境では、通常、クライアントのポストバックプロセスをサポートするために String から変換し、ビューのレンダリングプロセスをサポートするために String に変換します。さらに、String 値をローカライズする必要があることがよくあります。より一般的な core.convertConverter SPI は、このようなフォーマット要件に直接対応していません。それらに直接対処するために、Spring 3 は、クライアント環境向けの PropertyEditor 実装のシンプルで堅牢な代替手段を提供する便利な Formatter SPI を導入しました。

一般に、java.util.Date と Long の間の変換など、汎用の型変換ロジックを実装する必要がある場合は、Converter SPI を使用できます。クライアント環境(Web アプリケーションなど)で作業していて、ローカライズされたフィールド値を解析および出力する必要がある場合は、Formatter SPI を使用できます。ConversionService は、両方の SPI に統一型変換 API を提供します。

3.5.1. Formatter SPI

フィールドフォーマットロジックを実装する Formatter SPI は単純で、強く型付けされています。以下のリストは、Formatter インターフェース定義を示しています。

Java
package org.springframework.format;

public interface Formatter<T> extends Printer<T>, Parser<T> {
}

Formatter は、Printer および Parser ビルドブロックインターフェースから拡張されています。次のリストは、これら 2 つのインターフェースの定義を示しています。

Java
public interface Printer<T> {

    String print(T fieldValue, Locale locale);
}
Kotlin
interface Printer<T> {

    fun print(fieldValue: T, locale: Locale): String
}
Java
import java.text.ParseException;

public interface Parser<T> {

    T parse(String clientValue, Locale locale) throws ParseException;
}
Kotlin
interface Parser<T> {

    @Throws(ParseException::class)
    fun parse(clientValue: String, locale: Locale): T
}

独自の Formatter を作成するには、前述の Formatter インターフェースを実装します。T をパラメーター化して、フォーマットするオブジェクトのタイプ(たとえば、java.util.Date)にします。print() 操作を実装して、クライアントロケールで表示するために T のインスタンスを出力します。parse() 操作を実装して、クライアントロケールから返されたフォーマットされた表現から T のインスタンスを解析します。解析が失敗した場合、Formatter は ParseException または IllegalArgumentException をスローする必要があります。Formatter 実装がスレッドセーフであることを確認してください。

format サブパッケージは、便宜上、いくつかの Formatter 実装を提供します。number パッケージは、java.text.NumberFormat を使用する Number オブジェクトをフォーマットするための NumberStyleFormatterCurrencyStyleFormatter および PercentStyleFormatter を提供します。datetime パッケージは、java.util.Date オブジェクトを java.text.DateFormat でフォーマットするための DateFormatter を提供します。

次の DateFormatter は、Formatter の実装例です。

Java
package org.springframework.format.datetime;

public final class DateFormatter implements Formatter<Date> {

    private String pattern;

    public DateFormatter(String pattern) {
        this.pattern = pattern;
    }

    public String print(Date date, Locale locale) {
        if (date == null) {
            return "";
        }
        return getDateFormat(locale).format(date);
    }

    public Date parse(String formatted, Locale locale) throws ParseException {
        if (formatted.length() == 0) {
            return null;
        }
        return getDateFormat(locale).parse(formatted);
    }

    protected DateFormat getDateFormat(Locale locale) {
        DateFormat dateFormat = new SimpleDateFormat(this.pattern, locale);
        dateFormat.setLenient(false);
        return dateFormat;
    }
}
Kotlin
class DateFormatter(private val pattern: String) : Formatter<Date> {

    override fun print(date: Date, locale: Locale)
            = getDateFormat(locale).format(date)

    @Throws(ParseException::class)
    override fun parse(formatted: String, locale: Locale)
            = getDateFormat(locale).parse(formatted)

    protected fun getDateFormat(locale: Locale): DateFormat {
        val dateFormat = SimpleDateFormat(this.pattern, locale)
        dateFormat.isLenient = false
        return dateFormat
    }
}

Spring チームは、コミュニティ主導の Formatter の貢献を歓迎します。投稿するには GitHub の課題 (英語) を参照してください。

3.5.2. アノテーション駆動の書式設定

フィールドのフォーマットは、フィールドタイプまたはアノテーションによって構成できます。Formatter にアノテーションをバインドするには、AnnotationFormatterFactory を実装します。次のリストは、AnnotationFormatterFactory インターフェースの定義を示しています。

Java
package org.springframework.format;

public interface AnnotationFormatterFactory<A extends Annotation> {

    Set<Class<?>> getFieldTypes();

    Printer<?> getPrinter(A annotation, Class<?> fieldType);

    Parser<?> getParser(A annotation, Class<?> fieldType);
}
Kotlin
package org.springframework.format

interface AnnotationFormatterFactory<A : Annotation> {

    val fieldTypes: Set<Class<*>>

    fun getPrinter(annotation: A, fieldType: Class<*>): Printer<*>

    fun getParser(annotation: A, fieldType: Class<*>): Parser<*>
}

実装を作成するには :。A をフォーマットロジックに関連付けるフィールド annotationType にパラメーター化します(例: org.springframework.format.annotation.DateTimeFormat)。getFieldTypes() がアノテーションを使用できるフィールドのタイプを返すようにします。getPrinter() に Printer を返させて、アノテーション付きフィールドの値を出力させます。getParser() に Parser を返させて、アノテーションフィールドの clientValue を解析させます。

次の例の AnnotationFormatterFactory 実装は、@NumberFormat アノテーションをフォーマッターにバインドして、数値スタイルまたはパターンを指定できるようにします。

Java
public final class NumberFormatAnnotationFormatterFactory
        implements AnnotationFormatterFactory<NumberFormat> {

    public Set<Class<?>> getFieldTypes() {
        return new HashSet<Class<?>>(asList(new Class<?>[] {
            Short.class, Integer.class, Long.class, Float.class,
            Double.class, BigDecimal.class, BigInteger.class }));
    }

    public Printer<Number> getPrinter(NumberFormat annotation, Class<?> fieldType) {
        return configureFormatterFrom(annotation, fieldType);
    }

    public Parser<Number> getParser(NumberFormat annotation, Class<?> fieldType) {
        return configureFormatterFrom(annotation, fieldType);
    }

    private Formatter<Number> configureFormatterFrom(NumberFormat annotation, Class<?> fieldType) {
        if (!annotation.pattern().isEmpty()) {
            return new NumberStyleFormatter(annotation.pattern());
        } else {
            Style style = annotation.style();
            if (style == Style.PERCENT) {
                return new PercentStyleFormatter();
            } else if (style == Style.CURRENCY) {
                return new CurrencyStyleFormatter();
            } else {
                return new NumberStyleFormatter();
            }
        }
    }
}
Kotlin
class NumberFormatAnnotationFormatterFactory : AnnotationFormatterFactory<NumberFormat> {

    override fun getFieldTypes(): Set<Class<*>> {
        return setOf(Short::class.java, Int::class.java, Long::class.java, Float::class.java, Double::class.java, BigDecimal::class.java, BigInteger::class.java)
    }

    override fun getPrinter(annotation: NumberFormat, fieldType: Class<*>): Printer<Number> {
        return configureFormatterFrom(annotation, fieldType)
    }

    override fun getParser(annotation: NumberFormat, fieldType: Class<*>): Parser<Number> {
        return configureFormatterFrom(annotation, fieldType)
    }

    private fun configureFormatterFrom(annotation: NumberFormat, fieldType: Class<*>): Formatter<Number> {
        return if (annotation.pattern.isNotEmpty()) {
            NumberStyleFormatter(annotation.pattern)
        } else {
            val style = annotation.style
            when {
                style === NumberFormat.Style.PERCENT -> PercentStyleFormatter()
                style === NumberFormat.Style.CURRENCY -> CurrencyStyleFormatter()
                else -> NumberStyleFormatter()
            }
        }
    }
}

次の例に示すように、フォーマットをトリガーするために、@NumberFormat でフィールドにアノテーションを付けることができます。

Java
public class MyModel {

    @NumberFormat(style=Style.CURRENCY)
    private BigDecimal decimal;
}
Kotlin
class MyModel(
    @field:NumberFormat(style = Style.CURRENCY) private val decimal: BigDecimal
)
Format Annotation API

ポータブルフォーマットアノテーション API は org.springframework.format.annotation パッケージに含まれています。@NumberFormat を使用して Double や Long などの Number フィールドをフォーマットし、@DateTimeFormat を使用して java.util.Datejava.util.CalendarLong (ミリ秒のタイムスタンプ用)および JSR-310 java.time をフォーマットできます。

次の例では、@DateTimeFormat を使用して、java.util.Date を ISO 日付(yyyy-MM-dd)としてフォーマットします。

Java
public class MyModel {

    @DateTimeFormat(iso=ISO.DATE)
    private Date date;
}
Kotlin
class MyModel(
    @DateTimeFormat(iso= ISO.DATE) private val date: Date
)

3.5.3. FormatterRegistry SPI

FormatterRegistry は、フォーマッタとコンバーターを登録するための SPI です。FormattingConversionService は、ほとんどの環境に適した FormatterRegistry の実装です。プログラムまたは宣言的にこのバリアントを Spring Bean として構成できます。FormattingConversionServiceFactoryBean を使用します。この実装では ConversionService も実装しているため、Spring の DataBinder および Spring Expression Language(SpEL)で使用するように直接構成できます。

次のリストは、FormatterRegistry SPI を示しています。

Java
package org.springframework.format;

public interface FormatterRegistry extends ConverterRegistry {

    void addFormatterForFieldType(Class<?> fieldType, Printer<?> printer, Parser<?> parser);

    void addFormatterForFieldType(Class<?> fieldType, Formatter<?> formatter);

    void addFormatterForFieldType(Formatter<?> formatter);

    void addFormatterForAnnotation(AnnotationFormatterFactory<?> factory);
}
Kotlin
package org.springframework.format

interface FormatterRegistry : ConverterRegistry {

    fun addFormatterForFieldType(fieldType: Class<*>, printer: Printer<*>, parser: Parser<*>)

    fun addFormatterForFieldType(fieldType: Class<*>, formatter: Formatter<*>)

    fun addFormatterForFieldType(formatter: Formatter<*>)

    fun addFormatterForAnnotation(factory: AnnotationFormatterFactory<*>)
}

前のリストに示すように、フィールドタイプまたはアノテーションによってフォーマッタを登録できます。

FormatterRegistry SPI を使用すると、コントローラー全体でこのような構成を複製する代わりに、フォーマット規則を中央で構成できます。例:すべての日付フィールドを特定の方法でフォーマットするか、特定のアノテーションを持つフィールドを特定の方法でフォーマットすることを強制することができます。共有 FormatterRegistry を使用すると、これらのルールを一度定義すると、フォーマットが必要になるたびに適用されます。

3.5.4. FormatterRegistrar SPI

FormatterRegistrar は、FormatterRegistry を介してフォーマッターとコンバーターを登録するための SPI です。次のリストは、そのインターフェース定義を示しています。

Java
package org.springframework.format;

public interface FormatterRegistrar {

    void registerFormatters(FormatterRegistry registry);
}
Kotlin
package org.springframework.format

interface FormatterRegistrar {

    fun registerFormatters(registry: FormatterRegistry)
}

FormatterRegistrar は、日付の書式設定など、特定の書式設定カテゴリに関連する複数のコンバーターおよびフォーマッターを登録するときに役立ちます。宣言的な登録が不十分な場合にも役立ちます。たとえば、独自の <T> とは異なる特定のフィールドタイプでフォーマッタのインデックスを作成する必要がある場合や、Printer/Parser ペアを登録する場合などです。次のセクションでは、コンバーターとフォーマッターの登録について詳しく説明します。

3.5.5. Spring MVC でのフォーマットの構成

Spring MVC の章の変換とフォーマットを参照してください。

3.6. グローバルな日付と時刻の形式の構成

デフォルトでは、@DateTimeFormat のアノテーションが付いていない日付と時刻のフィールドは、DateFormat.SHORT スタイルを使用して文字列から変換されます。必要に応じて、独自のグローバル形式を定義してこれを変更できます。

そのためには、Spring がデフォルトのフォーマッターを登録しないようにしてください。代わりに、次の助けを借りてフォーマッターを手動で登録します。

  • org.springframework.format.datetime.standard.DateTimeFormatterRegistrar

  • org.springframework.format.datetime.DateFormatterRegistrar

例:次の Java 構成は、グローバル yyyyMMdd 形式を登録します。

Java
@Configuration
public class AppConfig {

    @Bean
    public FormattingConversionService conversionService() {

        // Use the DefaultFormattingConversionService but do not register defaults
        DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService(false);

        // Ensure @NumberFormat is still supported
        conversionService.addFormatterForFieldAnnotation(new NumberFormatAnnotationFormatterFactory());

        // Register JSR-310 date conversion with a specific global format
        DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
        registrar.setDateFormatter(DateTimeFormatter.ofPattern("yyyyMMdd"));
        registrar.registerFormatters(conversionService);

        // Register date conversion with a specific global format
        DateFormatterRegistrar registrar = new DateFormatterRegistrar();
        registrar.setFormatter(new DateFormatter("yyyyMMdd"));
        registrar.registerFormatters(conversionService);

        return conversionService;
    }
}
Kotlin
@Configuration
class AppConfig {

    @Bean
    fun conversionService(): FormattingConversionService {
        // Use the DefaultFormattingConversionService but do not register defaults
        return DefaultFormattingConversionService(false).apply {

            // Ensure @NumberFormat is still supported
            addFormatterForFieldAnnotation(NumberFormatAnnotationFormatterFactory())

            // Register JSR-310 date conversion with a specific global format
            val registrar = DateTimeFormatterRegistrar()
            registrar.setDateFormatter(DateTimeFormatter.ofPattern("yyyyMMdd"))
            registrar.registerFormatters(this)

            // Register date conversion with a specific global format
            val registrar = DateFormatterRegistrar()
            registrar.setFormatter(DateFormatter("yyyyMMdd"))
            registrar.registerFormatters(this)
        }
    }
}

XML ベースの構成が必要な場合は、FormattingConversionServiceFactoryBean を使用できます。次の例は、その方法を示しています。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd>

    <bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
        <property name="registerDefaultFormatters" value="false" />
        <property name="formatters">
            <set>
                <bean class="org.springframework.format.number.NumberFormatAnnotationFormatterFactory" />
            </set>
        </property>
        <property name="formatterRegistrars">
            <set>
                <bean class="org.springframework.format.datetime.standard.DateTimeFormatterRegistrar">
                    <property name="dateFormatter">
                        <bean class="org.springframework.format.datetime.standard.DateTimeFormatterFactoryBean">
                            <property name="pattern" value="yyyyMMdd"/>
                        </bean>
                    </property>
                </bean>
            </set>
        </property>
    </bean>
</beans>

Web アプリケーションで日付と時刻の形式を構成する際には、追加の考慮事項があることに注意してください。WebMVC 変換およびフォーマットまたは WebFlux の変換とフォーマットを参照してください。

3.7. Java Bean 検証

Spring Framework は、Java Bean 検証 (英語) API のサポートを提供します。

3.7.1. Bean 検証の概要

Bean 検証は、Java アプリケーションの制約宣言とメタデータを介した検証の一般的なメソッドを提供します。これを使用するには、宣言型の検証制約を使用してドメインモデルプロパティにアノテーションを付けてから、ランタイムによって強制されます。組み込みの制約があり、独自のカスタム制約を定義することもできます。

2 つのプロパティを持つ単純な PersonForm モデルを示す次の例を検討してください。

Java
public class PersonForm {
    private String name;
    private int age;
}
Kotlin
class PersonForm(
        private val name: String,
        private val age: Int
)

Bean 検証では、次の例に示すように制約を宣言できます。

Java
public class PersonForm {

    @NotNull
    @Size(max=64)
    private String name;

    @Min(0)
    private int age;
}
Kotlin
class PersonForm(
    @get:NotNull @get:Size(max=64)
    private val name: String,
    @get:Min(0)
    private val age: Int
)

Bean 検証バリデーターは、宣言された制約に基づいてこのクラスのインスタンスを検証します。API に関する一般情報については、Bean バリデーション (英語) を参照してください。特定の制約については、Hibernate バリデーター (英語) の資料を参照してください。Bean 検証プロバイダーを Spring Bean としてセットアップする方法については、読み続けてください。

3.7.2. Bean 検証プロバイダーの構成

Spring は、Bean 検証プロバイダーを Spring Bean としてブートストラップするなど、Bean 検証 API を完全にサポートしています。これにより、アプリケーションで検証が必要な場所に javax.validation.ValidatorFactory または javax.validation.Validator を挿入できます。

次の例に示すように、LocalValidatorFactoryBean を使用して、デフォルトのバリデーターを Spring Bean として構成できます。

Java
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;

@Configuration
public class AppConfig {

    @Bean
    public LocalValidatorFactoryBean validator() {
        return new LocalValidatorFactoryBean();
    }
}
XML
<bean id="validator"
    class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>

前述の例の基本構成は、Bean 検証をトリガーして、デフォルトのブートストラップメカニズムを使用して初期化します。Hibernate Validator などの Bean 検証プロバイダーは、クラスパスに存在すると予想され、自動的に検出されます。

バリデーターの注入

LocalValidatorFactoryBean は、javax.validation.ValidatorFactory と javax.validation.Validator の両方、および Spring の org.springframework.validation.Validator を実装しています。これらのインターフェースのいずれかへの参照を、検証ロジックを呼び出す必要がある Bean に注入できます。

次の例に示すように、Bean Validation API を直接操作する場合は、javax.validation.Validator への参照を挿入できます。

Java
import javax.validation.Validator;

@Service
public class MyService {

    @Autowired
    private Validator validator;
}
Kotlin
import javax.validation.Validator;

@Service
class MyService(@Autowired private val validator: Validator)

次の例に示すように、Bean で Spring 検証 API が必要な場合は、org.springframework.validation.Validator への参照を挿入できます。

Java
import org.springframework.validation.Validator;

@Service
public class MyService {

    @Autowired
    private Validator validator;
}
Kotlin
import org.springframework.validation.Validator

@Service
class MyService(@Autowired private val validator: Validator)
カスタム制約の構成

各 Bean 検証制約は、2 つの部分で構成されています。

  • 制約とその構成可能なプロパティを宣言する @Constraint アノテーション。

  • 制約の動作を実装する javax.validation.ConstraintValidator インターフェースの実装。

宣言を実装に関連付けるために、各 @Constraint アノテーションは対応する ConstraintValidator 実装クラスを参照します。実行時に、ConstraintValidatorFactory は、ドメインモデルで制約アノテーションが検出されると、参照される実装をインスタンス化します。

デフォルトでは、LocalValidatorFactoryBean は Spring を使用して ConstraintValidator インスタンスを作成する SpringConstraintValidatorFactory を構成します。これにより、カスタム ConstraintValidators は、他の Spring Bean と同様に依存性注入の恩恵を受けます。

次の例は、カスタム @Constraint 宣言の後に、依存性注入に Spring を使用する関連 ConstraintValidator 実装を示しています。

Java
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy=MyConstraintValidator.class)
public @interface MyConstraint {
}
Kotlin
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.FIELD)
@Retention(AnnotationRetention.RUNTIME)
@Constraint(validatedBy = MyConstraintValidator::class)
annotation class MyConstraint
Java
import javax.validation.ConstraintValidator;

public class MyConstraintValidator implements ConstraintValidator {

    @Autowired;
    private Foo aDependency;

    // ...
}
Kotlin
import javax.validation.ConstraintValidator

class MyConstraintValidator(private val aDependency: Foo) : ConstraintValidator {

    // ...
}

上記の例が示すように、ConstraintValidator 実装は、他の Spring Bean と同様に、@Autowired の依存関係を持つことができます。

Spring 駆動のメソッド検証

Bean バリデーション 1.1(および Hibernate Validator 4.3 によっても)でサポートされるメソッド検証機能を、MethodValidationPostProcessor Bean 定義を介して Spring コンテキストに統合できます。

Java
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;

@Configuration
public class AppConfig {

    @Bean
    public MethodValidationPostProcessor validationPostProcessor() {
        return new MethodValidationPostProcessor();
    }
}
XML
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor"/>

Spring 駆動のメソッド検証の対象となるには、すべてのターゲットクラスに Spring の @Validated アノテーションを付ける必要があります。このアノテーションは、使用する検証グループをオプションで宣言することもできます。Hibernate Validator および Bean バリデーション 1.1 プロバイダーを使用したセットアップの詳細については、MethodValidationPostProcessor (Javadoc) を参照してください。

メソッドの検証は、インターフェース上のメソッドの JDK 動的プロキシまたは CGLIB プロキシのいずれかである、ターゲットクラスの周囲の AOP プロキシに依存します。プロキシの使用には特定の制限があり、その一部は AOP プロキシについてで説明されています。さらに、プロキシされたクラスでは常にメソッドとアクセサーを使用することを忘れないでください。直接フィールドアクセスは機能しません。

追加の構成オプション

ほとんどの場合、デフォルトの LocalValidatorFactoryBean 構成で十分です。メッセージの補間からトラバーサル解決まで、さまざまな Bean 検証コンストラクトの構成オプションが多数あります。これらのオプションの詳細については、LocalValidatorFactoryBean (Javadoc) javadoc を参照してください。

3.7.3. DataBinder の構成

Spring 3, 以降は、Validator を使用して DataBinder インスタンスを構成できます。構成が完了すると、binder.validate() を呼び出して Validator を呼び出すことができます。検証 Errors はすべて、バインダーの BindingResult に自動的に追加されます。

次の例は、DataBinder をプログラムで使用して、ターゲットオブジェクトにバインドした後に検証ロジックを呼び出す方法を示しています。

Java
Foo target = new Foo();
DataBinder binder = new DataBinder(target);
binder.setValidator(new FooValidator());

// bind to the target object
binder.bind(propertyValues);

// validate the target object
binder.validate();

// get BindingResult that includes any validation errors
BindingResult results = binder.getBindingResult();
Kotlin
val target = Foo()
val binder = DataBinder(target)
binder.validator = FooValidator()

// bind to the target object
binder.bind(propertyValues)

// validate the target object
binder.validate()

// get BindingResult that includes any validation errors
val results = binder.bindingResult

dataBinder.addValidators および dataBinder.replaceValidators を介して、複数の Validator インスタンスで DataBinder を構成することもできます。これは、グローバルに構成された Bean 検証と、DataBinder インスタンスでローカルに構成された Spring Validator を組み合わせるときに役立ちます。Spring MVC 検証構成を参照してください。

3.7.4. Spring MVC 3 検証

Spring MVC の章の検証を参照してください。

4. Spring 式言語 (SpEL)

Spring Expression Language(略して「SpEL」)は、実行時にオブジェクトグラフのクエリと操作をサポートする強力な式言語です。言語構文は Unified EL に似ていますが、追加機能、特にメソッド呼び出しと基本的な文字列テンプレート機能を提供します。

他にもいくつかの Java 式言語(OGNL、MVEL、JBoss EL など)がありますが、Spring 式言語は、Spring コミュニティに、サポートされているすべての製品で使用できる単一の十分にサポートされた式言語を提供するために作成されます。Spring ポートフォリオ。その言語機能は、Eclipse Pleiades All in One (STS, Lombok 付属) または Eclipse 用 Spring Tools (英語) 内のコード補完サポートのツール要件を含む、Spring ポートフォリオのプロジェクトの要件によって決まります。つまり、SpEL はテクノロジーに依存しない API に基づいており、必要に応じて他の式言語の実装を統合できます。

SpEL は Spring ポートフォリオ内の式評価の基盤として機能しますが、Spring に直接結び付けられておらず、単独で使用できます。自己完結型であるために、この章の例の多くは SpEL を独立した表現言語であるかのように使用しています。これには、パーサーなどのいくつかのブートストラップインフラストラクチャクラスを作成する必要があります。ほとんどの Spring ユーザーは、このインフラストラクチャを扱う必要はなく、代わりに評価用の式文字列のみを作成できます。この典型的な使用例は、Bean 定義を定義するための式のサポートに示すように、XML またはアノテーションベースの Bean 定義の作成への SpEL の統合です。

この章では、式言語、API、言語構文の機能について説明します。いくつかの場所では、Inventor および Society クラスが式評価のターゲットオブジェクトとして使用されます。これらのクラス宣言と設定するために使用されるデータは、この章の最後にリストされています。

式言語は、次の機能をサポートしています。

  • リテラル式

  • ブール演算子と関係演算子

  • 正規表現

  • クラス式

  • プロパティ、配列、リスト、マップへのアクセス

  • メソッド呼び出し

  • 比較演算子

  • 代入

  • コンストラクターの呼び出し

  • Bean 参照

  • 配列構成

  • インラインリスト

  • インラインマップ

  • 三項演算子

  • 変数

  • ユーザー定義関数

  • コレクションの射影

  • コレクションの選択

  • テンプレート式

4.1. 評価

このセクションでは、SpEL インターフェースとその表現言語の簡単な使用箇所を紹介します。完全な言語リファレンスは言語リファレンスにあります。

次のコードは、リテラル文字列式 Hello World を評価する SpEL API を紹介しています。

Java
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("'Hello World'"); (1)
String message = (String) exp.getValue();
1 メッセージ変数の値は 'Hello World' です。
Kotlin
val parser = SpelExpressionParser()
val exp = parser.parseExpression("'Hello World'") (1)
val message = exp.value as String
1 メッセージ変数の値は 'Hello World' です。

使用する可能性が最も高い SpEL クラスとインターフェースは、org.springframework.expression パッケージとそのサブパッケージ(spel.support など)にあります。

ExpressionParser インターフェースは、式ストリングの解析を担当します。前の例では、式文字列は、周囲の単一引用符で示された文字列リテラルです。Expression インターフェースは、以前に定義された式ストリングを評価するロールを果たします。parser.parseExpression および exp.getValue をそれぞれ呼び出した場合にスローできる 2 つの例外、ParseException および EvaluationException

SpEL は、メソッドの呼び出し、プロパティへのアクセス、コンストラクターの呼び出しなど、幅広い機能をサポートしています。

次のメソッド呼び出しの例では、文字列リテラルで concat メソッドを呼び出します。

Java
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("'Hello World'.concat('!')"); (1)
String message = (String) exp.getValue();
1message の値は現在「Hello World!」です。
Kotlin
val parser = SpelExpressionParser()
val exp = parser.parseExpression("'Hello World'.concat('!')") (1)
val message = exp.value as String
1message の値は現在「Hello World!」です。

JavaBean プロパティを呼び出す次の例は、String プロパティ Bytes を呼び出します。

Java
ExpressionParser parser = new SpelExpressionParser();

// invokes 'getBytes()'
Expression exp = parser.parseExpression("'Hello World'.bytes"); (1)
byte[] bytes = (byte[]) exp.getValue();
1 この行は、リテラルをバイト配列に変換します。
Kotlin
val parser = SpelExpressionParser()

// invokes 'getBytes()'
val exp = parser.parseExpression("'Hello World'.bytes") (1)
val bytes = exp.value as ByteArray
1 この行は、リテラルをバイト配列に変換します。

SpEL は、標準のドット表記(prop1.prop2.prop3 など)とそれに対応するプロパティ値の設定を使用して、ネストされたプロパティもサポートします。パブリックフィールドにもアクセスできます。

次の例は、ドット表記を使用してリテラルの長さを取得する方法を示しています。

Java
ExpressionParser parser = new SpelExpressionParser();

// invokes 'getBytes().length'
Expression exp = parser.parseExpression("'Hello World'.bytes.length"); (1)
int length = (Integer) exp.getValue();
1'Hello World'.bytes.length は、リテラルの長さを示します。
Kotlin
val parser = SpelExpressionParser()

// invokes 'getBytes().length'
val exp = parser.parseExpression("'Hello World'.bytes.length") (1)
val length = exp.value as Int
1'Hello World'.bytes.length は、リテラルの長さを示します。

次の例に示すように、文字列リテラルを使用する代わりに、文字列のコンストラクターを呼び出すことができます。

Java
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("new String('hello world').toUpperCase()"); (1)
String message = exp.getValue(String.class);
1 リテラルから新しい String を作成し、大文字にします。
Kotlin
val parser = SpelExpressionParser()
val exp = parser.parseExpression("new String('hello world').toUpperCase()")  (1)
val message = exp.getValue(String::class.java)
1 リテラルから新しい String を作成し、大文字にします。

ジェネリックメソッド public <T> T getValue(Class<T> desiredResultType) の使用に注意してください。このメソッドを使用すると、式の値を目的の結果タイプにキャストする必要がなくなります。値をタイプ T にキャストできないか、登録済みのタイプコンバーターを使用して変換できない場合、EvaluationException がスローされます。

SpEL のより一般的な使用箇所は、特定のオブジェクトインスタンス(ルートオブジェクトと呼ばれる)に対して評価される式文字列を提供することです。次の例は、Inventor クラスのインスタンスから name プロパティを取得する方法、またはブール条件を作成する方法を示しています。

Java
// Create and set a calendar
GregorianCalendar c = new GregorianCalendar();
c.set(1856, 7, 9);

// The constructor arguments are name, birthday, and nationality.
Inventor tesla = new Inventor("Nikola Tesla", c.getTime(), "Serbian");

ExpressionParser parser = new SpelExpressionParser();

Expression exp = parser.parseExpression("name"); // Parse name as an expression
String name = (String) exp.getValue(tesla);
// name == "Nikola Tesla"

exp = parser.parseExpression("name == 'Nikola Tesla'");
boolean result = exp.getValue(tesla, Boolean.class);
// result == true
Kotlin
// Create and set a calendar
val c = GregorianCalendar()
c.set(1856, 7, 9)

// The constructor arguments are name, birthday, and nationality.
val tesla = Inventor("Nikola Tesla", c.time, "Serbian")

val parser = SpelExpressionParser()

var exp = parser.parseExpression("name") // Parse name as an expression
val name = exp.getValue(tesla) as String
// name == "Nikola Tesla"

exp = parser.parseExpression("name == 'Nikola Tesla'")
val result = exp.getValue(tesla, Boolean::class.java)
// result == true

4.1.1. EvaluationContext を理解する

EvaluationContext インターフェースは、式を評価してプロパティ、メソッド、またはフィールドを解決し、型変換を実行する際に使用されます。Spring は 2 つの実装を提供します。

  • SimpleEvaluationContext: SpEL 言語構文の全範囲を必要とせず、有意に制限される必要がある式のカテゴリに対して、本質的な SpEL 言語機能および構成オプションのサブセットを公開します。例には、データバインディング式およびプロパティベースのフィルターが含まれますが、これらに限定されません。

  • StandardEvaluationContext: SpEL 言語機能と構成オプションの完全なセットを公開します。これを使用して、デフォルトのルートオブジェクトを指定し、利用可能なすべての評価関連戦略を構成できます。

SimpleEvaluationContext は、SpEL 言語構文のサブセットのみをサポートするように設計されています。Java 型参照、コンストラクター、Bean 参照は除外されます。また、式のプロパティとメソッドのサポートのレベルを明示的に選択する必要があります。デフォルトでは、create() 静的ファクトリメソッドはプロパティへの読み取りアクセスのみを有効にします。ビルダーを入手して、必要なサポートの正確なレベルを構成し、次の 1 つまたはいくつかの組み合わせをターゲットにすることもできます。

  • カスタム PropertyAccessor のみ (リフレクションなし)

  • 読み取り専用アクセスのデータバインディングプロパティ

  • 読み取りおよび書き込み用のデータバインディングプロパティ

型変換

デフォルトでは、SpEL は Spring コア(org.springframework.core.convert.ConversionService)で利用可能な変換サービスを使用します。この変換サービスには、一般的な変換用の多くの組み込みコンバーターが付属していますが、完全に拡張可能であるため、タイプ間でカスタム変換を追加できます。さらに、ジェネリックに対応しています。つまり、式でジェネリック型を使用する場合、SpEL は変換を試みて、検出したオブジェクトの型の正確性を維持します。

これは実際にはどういう意味でしょうか? setValue() を使用した割り当てが、List プロパティの設定に使用されているとします。プロパティのタイプは、実際には List<Boolean> です。SpEL は、リストの要素を配置する前に Boolean に変換する必要があることを認識しています。次の例は、その方法を示しています。

Java
class Simple {
    public List<Boolean> booleanList = new ArrayList<Boolean>();
}

Simple simple = new Simple();
simple.booleanList.add(true);

EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();

// "false" is passed in here as a String. SpEL and the conversion service
// will recognize that it needs to be a Boolean and convert it accordingly.
parser.parseExpression("booleanList[0]").setValue(context, simple, "false");

// b is false
Boolean b = simple.booleanList.get(0);
Kotlin
class Simple {
    var booleanList: MutableList<Boolean> = ArrayList()
}

val simple = Simple()
simple.booleanList.add(true)

val context = SimpleEvaluationContext.forReadOnlyDataBinding().build()

// "false" is passed in here as a String. SpEL and the conversion service
// will recognize that it needs to be a Boolean and convert it accordingly.
parser.parseExpression("booleanList[0]").setValue(context, simple, "false")

// b is false
val b = simple.booleanList[0]

4.1.2. パーサー構成

パーサー構成オブジェクト(org.springframework.expression.spel.SpelParserConfiguration)を使用して、SpEL 式パーサーを構成することができます。構成オブジェクトは、一部の式コンポーネントの動作を制御します。例:配列またはコレクションにインデックスを付け、指定されたインデックスの要素が null の場合、SpEL は自動的に要素を作成できます。これは、プロパティ参照のチェーンで構成される式を使用する場合に役立ちます。配列またはリストにインデックスを付け、配列またはリストの現在のサイズの終わりを超えるインデックスを指定すると、SpEL はそのインデックスに対応するように配列またはリストを自動的に拡張できます。指定されたインデックスに要素を追加するために、SpEL は、指定された値を設定する前に、要素タイプのデフォルトコンストラクターを使用して要素を作成しようとします。要素タイプにデフォルトのコンストラクターがない場合、null が配列またはリストに追加されます。値の設定メソッドを知っている組み込みまたはカスタムのコンバーターがない場合、null は指定されたインデックスの配列またはリストに残ります。次の例は、リストを自動的に拡大する方法を示しています。

Java
class Demo {
    public List<String> list;
}

// Turn on:
// - auto null reference initialization
// - auto collection growing
SpelParserConfiguration config = new SpelParserConfiguration(true,true);

ExpressionParser parser = new SpelExpressionParser(config);

Expression expression = parser.parseExpression("list[3]");

Demo demo = new Demo();

Object o = expression.getValue(demo);

// demo.list will now be a real collection of 4 entries
// Each entry is a new empty String
Kotlin
class Demo {
    var list: List<String>? = null
}

// Turn on:
// - auto null reference initialization
// - auto collection growing
val config = SpelParserConfiguration(true, true)

val parser = SpelExpressionParser(config)

val expression = parser.parseExpression("list[3]")

val demo = Demo()

val o = expression.getValue(demo)

// demo.list will now be a real collection of 4 entries
// Each entry is a new empty String

4.1.3. SpEL のコンパイル

Spring Framework 4.1 には、基本的な式コンパイラが含まれています。式は通常解釈され、評価中に多くの動的な柔軟性を提供しますが、最適なパフォーマンスは提供しません。ときどき式を使用する場合はこれで問題ありませんが、Spring Integration などの他のコンポーネントで使用する場合、パフォーマンスは非常に重要になる可能性があり、ダイナミズムは実際には必要ありません。

SpEL コンパイラは、このニーズに対処することを目的としています。評価中に、コンパイラは実行時の式の動作を具体化する Java クラスを生成し、そのクラスを使用して式の評価をより高速に実行します。式の周囲に入力できないため、コンパイラーは、コンパイルの実行時に式の解釈された評価中に収集された情報を使用します。例:純粋に式からプロパティ参照のタイプを知りませんが、最初に解釈された評価の間に、それが何であるかを見つけます。もちろん、そのような派生情報に基づいてコンパイルを行うと、さまざまな式要素のタイプが時間とともに変化する場合、後でトラブルを引き起こす可能性があります。このため、コンパイルは、評価が繰り返されても型情報が変更されない式に最適です。

以下の基本的な表現を考えてください:

someArray[0].someProperty.someOtherProperty < 0.1

上記の式には配列アクセス、一部のプロパティの逆参照、および数値演算が含まれるため、パフォーマンスの向上は非常に顕著です。50000 反復のマイクロベンチマークの実行例では、インタープリターを使用して評価するのに 75 ミリ秒かかり、コンパイルされたバージョンの式を使用して 3 ミリ秒しかかかりませんでした。

コンパイラー構成

コンパイラはデフォルトでは有効になっていませんが、2 つの異なる方法のいずれかで有効にすることができます。パーサー構成プロセス(前述)を使用するか、SpEL の使用が別のコンポーネント内に埋め込まれているときにシステムプロパティを使用することで、この機能をオンにできます。このセクションでは、これらのオプションの両方について説明します。

コンパイラは、org.springframework.expression.spel.SpelCompilerMode 列挙型でキャプチャーされる 3 つのモードのいずれかで動作できます。モードは次のとおりです。

  • OFF (デフォルト): コンパイラはオフになります。

  • IMMEDIATE: 即時モードでは、式はできるだけ早くコンパイルされます。これは通常、最初に解釈された評価の後です。コンパイルされた式が失敗する場合(通常、前述のように型の変更が原因)、式の評価の呼び出し元は例外を受け取ります。

  • MIXED: 混合モードでは、式は時間の経過とともにサイレントモードとインタープリターモードを切り替えます。いくつかの解釈された実行の後、コンパイルされたフォームに切り替わり、コンパイルされたフォームに何か問題が発生した場合(前述のタイプ変更など)、式は自動的に再び解釈されたフォームに戻ります。しばらくしてから、別のコンパイル済みフォームを生成し、それに切り替える可能性があります。基本的に、ユーザーが IMMEDIATE モードで取得する例外は、代わりに内部的に処理されます。

MIXED モードは、副作用のある式に課題を引き起こす可能性があるため、IMMEDIATE モードが存在します。コンパイルされた式が部分的に成功した後に展開した場合、システムの状態に影響を与えている何かをすでに行っている可能性があります。これが発生した場合、式の一部が 2 回実行される可能性があるため、呼び出し側はインタープリターモードで静かに再実行することを望まない場合があります。

モードを選択した後、SpelParserConfiguration を使用してパーサーを構成します。次の例は、その方法を示しています。

Java
SpelParserConfiguration config = new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE,
    this.getClass().getClassLoader());

SpelExpressionParser parser = new SpelExpressionParser(config);

Expression expr = parser.parseExpression("payload");

MyMessage message = new MyMessage();

Object payload = expr.getValue(message);
Kotlin
val config = SpelParserConfiguration(SpelCompilerMode.IMMEDIATE,
        this.javaClass.classLoader)

val parser = SpelExpressionParser(config)

val expr = parser.parseExpression("payload")

val message = MyMessage()

val payload = expr.getValue(message)

コンパイラモードを指定する場合、クラスローダーも指定できます(null を渡すことは許可されます)。コンパイルされた式は、指定されたものに作成された子クラスローダーで定義されます。クラスローダーが指定されている場合、式評価プロセスに関係するすべてのタイプを確認できるようにすることが重要です。クラスローダーを指定しない場合、デフォルトのクラスローダーが使用されます(通常、式の評価中に実行されているスレッドのコンテキストクラスローダー)。

コンパイラを構成する 2 番目の方法は、SpEL が他のコンポーネント内に埋め込まれている場合に使用し、構成オブジェクトを介して構成できない場合があります。これらの場合、システムプロパティを使用できます。spring.expression.compiler.mode プロパティを SpelCompilerMode 列挙値(offimmediate または mixed)のいずれかに設定できます。

コンパイラの制限

Spring Framework 4.1, 以降、基本的なコンパイルフレームワークが用意されています。ただし、フレームワークはまだすべての種類の式のコンパイルをサポートしていません。最初の焦点は、パフォーマンスが重要なコンテキストで使用される可能性が高い一般的な表現にありました。現在、次の種類の式はコンパイルできません。

  • 代入を含む式

  • 変換サービスに依存する式

  • カスタムリゾルバーまたはアクセサーを使用する式

  • 選択または射影を使用した式

将来、より多くの種類の表現がコンパイル可能になります。

4.2. Bean 定義の式

BeanDefinition インスタンスを定義するために、XML ベースまたはアノテーションベースの構成メタデータで SpEL 式を使用できます。どちらの場合も、式を定義する構文は #{ <expression string> } の形式です。

4.2.1. XML 構成

次の例に示すように、式を使用してプロパティまたはコンストラクターの引数値を設定できます。

<bean id="numberGuess" class="org.spring.samples.NumberGuess">
    <property name="randomNumber" value="#{ T(java.lang.Math).random() * 100.0 }"/>

    <!-- other properties -->
</bean>

アプリケーションコンテキスト内のすべての Bean は、共通の Bean 名を持つ定義済み変数として使用できます。これには、ランタイム環境にアクセスするための environment (タイプ org.springframework.core.env.Environment の)および systemProperties および systemEnvironment (タイプ Map<String, Object> の)などの標準コンテキスト Bean が含まれます。

次の例は、systemProperties Bean への SpEL 変数としてのアクセスを示しています。

<bean id="taxCalculator" class="org.spring.samples.TaxCalculator">
    <property name="defaultLocale" value="#{ systemProperties['user.region'] }"/>

    <!-- other properties -->
</bean>

ここでは、事前定義された変数の前に # 記号を付ける必要がないことに注意してください。

次の例に示すように、他の Bean プロパティを名前で参照することもできます。

<bean id="numberGuess" class="org.spring.samples.NumberGuess">
    <property name="randomNumber" value="#{ T(java.lang.Math).random() * 100.0 }"/>

    <!-- other properties -->
</bean>

<bean id="shapeGuess" class="org.spring.samples.ShapeGuess">
    <property name="initialShapeSeed" value="#{ numberGuess.randomNumber }"/>

    <!-- other properties -->
</bean>

4.2.2. アノテーション設定

デフォルト値を指定するには、フィールド、メソッド、メソッドまたはコンストラクターのパラメーターに @Value アノテーションを配置できます。

次の例では、フィールド変数のデフォルト値を設定します。

Java
public class FieldValueTestBean {

    @Value("#{ systemProperties['user.region'] }")
    private String defaultLocale;

    public void setDefaultLocale(String defaultLocale) {
        this.defaultLocale = defaultLocale;
    }

    public String getDefaultLocale() {
        return this.defaultLocale;
    }
}
Kotlin
class FieldValueTestBean {

    @Value("#{ systemProperties['user.region'] }")
    var defaultLocale: String? = null
}

次の例は、同等のプロパティ setter メソッドを示しています。

Java
public class PropertyValueTestBean {

    private String defaultLocale;

    @Value("#{ systemProperties['user.region'] }")
    public void setDefaultLocale(String defaultLocale) {
        this.defaultLocale = defaultLocale;
    }

    public String getDefaultLocale() {
        return this.defaultLocale;
    }
}
Kotlin
class PropertyValueTestBean {

    @Value("#{ systemProperties['user.region'] }")
    var defaultLocale: String? = null
}

次の例に示すように、オートワイヤーされたメソッドとコンストラクターも @Value アノテーションを使用できます。

Java
public class SimpleMovieLister {

    private MovieFinder movieFinder;
    private String defaultLocale;

    @Autowired
    public void configure(MovieFinder movieFinder,
            @Value("#{ systemProperties['user.region'] }") String defaultLocale) {
        this.movieFinder = movieFinder;
        this.defaultLocale = defaultLocale;
    }

    // ...
}
Kotlin
class SimpleMovieLister {

    private lateinit var movieFinder: MovieFinder
    private lateinit var defaultLocale: String

    @Autowired
    fun configure(movieFinder: MovieFinder,
                @Value("#{ systemProperties['user.region'] }") defaultLocale: String) {
        this.movieFinder = movieFinder
        this.defaultLocale = defaultLocale
    }

    // ...
}
Java
public class MovieRecommender {

    private String defaultLocale;

    private CustomerPreferenceDao customerPreferenceDao;

    public MovieRecommender(CustomerPreferenceDao customerPreferenceDao,
            @Value("#{systemProperties['user.country']}") String defaultLocale) {
        this.customerPreferenceDao = customerPreferenceDao;
        this.defaultLocale = defaultLocale;
    }

    // ...
}
Kotlin
class MovieRecommender(private val customerPreferenceDao: CustomerPreferenceDao,
            @Value("#{systemProperties['user.country']}") private val defaultLocale: String) {
    // ...
}

4.3. 言語リファレンス

このセクションでは、Spring 式言語の機能について説明します。次のトピックについて説明します。

4.3.1. リテラル式

サポートされているリテラル式のタイプは、文字列、数値(int、real、hex)、boolean、null です。文字列は単一引用符で区切られます。文字列に単一引用符自体を挿入するには、2 つの単一引用符文字を使用します。

次のリストは、リテラルの簡単な使用箇所を示しています。通常、これらはこのように単独で使用されるのではなく、より複雑な式の一部として使用されます。たとえば、論理比較演算子の片側でリテラルを使用します。

Java
ExpressionParser parser = new SpelExpressionParser();

// evals to "Hello World"
String helloWorld = (String) parser.parseExpression("'Hello World'").getValue();

double avogadrosNumber = (Double) parser.parseExpression("6.0221415E+23").getValue();

// evals to 2147483647
int maxValue = (Integer) parser.parseExpression("0x7FFFFFFF").getValue();

boolean trueValue = (Boolean) parser.parseExpression("true").getValue();

Object nullValue = parser.parseExpression("null").getValue();
Kotlin
val parser = SpelExpressionParser()

// evals to "Hello World"
val helloWorld = parser.parseExpression("'Hello World'").value as String

val avogadrosNumber = parser.parseExpression("6.0221415E+23").value as Double

// evals to 2147483647
val maxValue = parser.parseExpression("0x7FFFFFFF").value as Int

val trueValue = parser.parseExpression("true").value as Boolean

val nullValue = parser.parseExpression("null").value

数字は、負符号、指数表記、小数点の使用をサポートしています。デフォルトでは、実数は Double.parseDouble() を使用して解析されます。

4.3.2. プロパティ、配列、リスト、マップ、インデクサー

プロパティ参照を使用した移動は簡単です。そのためには、ピリオドを使用して、ネストされたプロパティ値を示します。Inventor クラスのインスタンス pupin および tesla には、例で使用されているクラスセクションにリストされたデータが取り込まれました。「下」に移動して、テスラの生年とピューピンの生年月日を取得するには、次の式を使用します。

Java
// evals to 1856
int year = (Integer) parser.parseExpression("birthdate.year + 1900").getValue(context);

String city = (String) parser.parseExpression("placeOfBirth.city").getValue(context);
Kotlin
// evals to 1856
val year = parser.parseExpression("birthdate.year + 1900").getValue(context) as Int

val city = parser.parseExpression("placeOfBirth.city").getValue(context) as String

プロパティ名の最初の文字では、大文字と小文字を区別しないことが許可されています。上記の例の式は、それぞれ Birthdate.Year + 1900 および PlaceOfBirth.City と書くことができます。さらに、プロパティには、オプションでメソッド呼び出しを介してアクセスできます(たとえば、placeOfBirth.city ではなく getPlaceOfBirth().getCity())。

配列とリストの内容は、次の例に示すように、角括弧表記を使用して取得されます。

Java
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();

// Inventions Array

// evaluates to "Induction motor"
String invention = parser.parseExpression("inventions[3]").getValue(
        context, tesla, String.class);

// Members List

// evaluates to "Nikola Tesla"
String name = parser.parseExpression("members[0].name").getValue(
        context, ieee, String.class);

// List and Array navigation
// evaluates to "Wireless communication"
String invention = parser.parseExpression("members[0].inventions[6]").getValue(
        context, ieee, String.class);
Kotlin
val parser = SpelExpressionParser()
val context = SimpleEvaluationContext.forReadOnlyDataBinding().build()

// Inventions Array

// evaluates to "Induction motor"
val invention = parser.parseExpression("inventions[3]").getValue(
        context, tesla, String::class.java)

// Members List

// evaluates to "Nikola Tesla"
val name = parser.parseExpression("members[0].name").getValue(
        context, ieee, String::class.java)

// List and Array navigation
// evaluates to "Wireless communication"
val invention = parser.parseExpression("members[0].inventions[6]").getValue(
        context, ieee, String::class.java)

マップの内容は、括弧内のリテラルキー値を指定することにより取得されます。次の例では、officers マップのキーは文字列であるため、文字列リテラルを指定できます。

Java
// Officer's Dictionary

Inventor pupin = parser.parseExpression("officers['president']").getValue(
        societyContext, Inventor.class);

// evaluates to "Idvor"
String city = parser.parseExpression("officers['president'].placeOfBirth.city").getValue(
        societyContext, String.class);

// setting values
parser.parseExpression("officers['advisors'][0].placeOfBirth.country").setValue(
        societyContext, "Croatia");
Kotlin
// Officer's Dictionary

val pupin = parser.parseExpression("officers['president']").getValue(
        societyContext, Inventor::class.java)

// evaluates to "Idvor"
val city = parser.parseExpression("officers['president'].placeOfBirth.city").getValue(
        societyContext, String::class.java)

// setting values
parser.parseExpression("officers['advisors'][0].placeOfBirth.country").setValue(
        societyContext, "Croatia")

4.3.3. インラインリスト

{} 表記を使用して、式でリストを直接表現できます。

Java
// evaluates to a Java list containing the four numbers
List numbers = (List) parser.parseExpression("{1,2,3,4}").getValue(context);

List listOfLists = (List) parser.parseExpression("{{'a','b'},{'x','y'}}").getValue(context);
Kotlin
// evaluates to a Java list containing the four numbers
val numbers = parser.parseExpression("{1,2,3,4}").getValue(context) as List<*>

val listOfLists = parser.parseExpression("{{'a','b'},{'x','y'}}").getValue(context) as List<*>

{} 自体は、空のリストを意味します。パフォーマンス上の理由から、リスト自体が固定リテラルで完全に構成されている場合、(各評価で新しいリストを作成するのではなく)式を表す定数リストが作成されます。

4.3.4. インラインマップ

{key:value} 表記を使用して、式でマップを直接表現することもできます。次の例は、その方法を示しています。

Java
// evaluates to a Java map containing the two entries
Map inventorInfo = (Map) parser.parseExpression("{name:'Nikola',dob:'10-July-1856'}").getValue(context);

Map mapOfMaps = (Map) parser.parseExpression("{name:{first:'Nikola',last:'Tesla'},dob:{day:10,month:'July',year:1856}}").getValue(context);
Kotlin
// evaluates to a Java map containing the two entries
val inventorInfo = parser.parseExpression("{name:'Nikola',dob:'10-July-1856'}").getValue(context) as Map<*, >

val mapOfMaps = parser.parseExpression("{name:{first:'Nikola',last:'Tesla'},dob:{day:10,month:'July',year:1856}}").getValue(context) as Map<, *>

{:} 自体は、空のマップを意味します。パフォーマンス上の理由から、マップ自体が固定リテラルまたは他のネストされた定数構造(リストまたはマップ)で構成されている場合、(各評価で新しいマップを作成するのではなく)式を表す定数マップが作成されます。マップキーの引用はオプションです。上記の例では、引用符付きキーを使用しません。

4.3.5. 配列構成

使い慣れた Java 構文を使用して配列を構築できます。オプションで、構築時に配列を設定する初期化子を指定できます。次の例は、その方法を示しています。

Java
int[] numbers1 = (int[]) parser.parseExpression("new int[4]").getValue(context);

// Array with initializer
int[] numbers2 = (int[]) parser.parseExpression("new int[]{1,2,3}").getValue(context);

// Multi dimensional array
int[][] numbers3 = (int[][]) parser.parseExpression("new int[4][5]").getValue(context);
Kotlin
val numbers1 = parser.parseExpression("new int[4]").getValue(context) as IntArray

// Array with initializer
val numbers2 = parser.parseExpression("new int[]{1,2,3}").getValue(context) as IntArray

// Multi dimensional array
val numbers3 = parser.parseExpression("new int[4][5]").getValue(context) as Array<IntArray>

現在、多次元配列を作成するときに初期化子を提供することはできません。

4.3.6. メソッド

一般的な Java プログラミング構文を使用してメソッドを呼び出すことができます。リテラルでメソッドを呼び出すこともできます。可変引数もサポートされています。次の例は、メソッドを呼び出す方法を示しています。

Java
// string literal, evaluates to "bc"
String bc = parser.parseExpression("'abc'.substring(1, 3)").getValue(String.class);

// evaluates to true
boolean isMember = parser.parseExpression("isMember('Mihajlo Pupin')").getValue(
        societyContext, Boolean.class);
Kotlin
// string literal, evaluates to "bc"
val bc = parser.parseExpression("'abc'.substring(1, 3)").getValue(String::class.java)

// evaluates to true
val isMember = parser.parseExpression("isMember('Mihajlo Pupin')").getValue(
        societyContext, Boolean::class.java)

4.3.7. 演算子

Spring Expression Language は、次の種類の演算子をサポートしています。

比較演算子

関係演算子(等しい、等しくない、より小さい、以下、より大きい、以上)は、標準の演算子表記を使用してサポートされます。次のリストは、演算子のいくつかの例を示しています。

Java
// evaluates to true
boolean trueValue = parser.parseExpression("2 == 2").getValue(Boolean.class);

// evaluates to false
boolean falseValue = parser.parseExpression("2 < -5.0").getValue(Boolean.class);

// evaluates to true
boolean trueValue = parser.parseExpression("'black' < 'block'").getValue(Boolean.class);
Kotlin
// evaluates to true
val trueValue = parser.parseExpression("2 == 2").getValue(Boolean::class.java)

// evaluates to false
val falseValue = parser.parseExpression("2 < -5.0").getValue(Boolean::class.java)

// evaluates to true
val trueValue = parser.parseExpression("'black' < 'block'").getValue(Boolean::class.java)

null との大小比較は、単純なルールに従います。null はゼロとしてではなく、ゼロとして扱われます。結果として、他の値は常に null より大きく(X > null は常に true)、他の値がゼロより小さくなることはありません(X < null は常に false です)。

代わりに数値比較を使用する場合は、ゼロとの比較を優先して、数値ベースの null 比較を避けます(たとえば、X > 0 または X < 0)。

標準の関係演算子に加えて、SpEL は instanceof および正規表現ベースの matches 演算子をサポートしています。次のリストは、両方の例を示しています。

Java
// evaluates to false
boolean falseValue = parser.parseExpression(
        "'xyz' instanceof T(Integer)").getValue(Boolean.class);

// evaluates to true
boolean trueValue = parser.parseExpression(
        "'5.00' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);

//evaluates to false
boolean falseValue = parser.parseExpression(
        "'5.0067' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);
Kotlin
// evaluates to false
val falseValue = parser.parseExpression(
        "'xyz' instanceof T(Integer)").getValue(Boolean::class.java)

// evaluates to true
val trueValue = parser.parseExpression(
        "'5.00' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean::class.java)

//evaluates to false
val falseValue = parser.parseExpression(
        "'5.0067' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean::class.java)
プリミティブ型はラッパー型にすぐにボックス化されるため、1 instanceof T(int) は false に評価され、1 instanceof T(Integer) は true に評価されるため、プリミティブ型には注意してください。

各記号演算子は、純粋にアルファベットの同等物として指定することもできます。これにより、使用されているシンボルが、式が埋め込まれているドキュメントタイプ(XML ドキュメントなど)に対して特別な意味を持つという問題が回避されます。同等のテキストは次のとおりです。

  • lt (<)

  • gt (>)

  • le (<=)

  • ge (>=)

  • eq (==)

  • ne (!=)

  • div (/)

  • mod (%)

  • not (!).

テキスト演算子はすべて大文字と小文字を区別しません。

論理演算子

SpEL は次の論理演算子をサポートしています。

  • and (&&)

  • or (||)

  • not (!)

次の例は、論理演算子の使用方法を示しています

Java
// -- AND --

// evaluates to false
boolean falseValue = parser.parseExpression("true and false").getValue(Boolean.class);

// evaluates to true
String expression = "isMember('Nikola Tesla') and isMember('Mihajlo Pupin')";
boolean trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);

// -- OR --

// evaluates to true
boolean trueValue = parser.parseExpression("true or false").getValue(Boolean.class);

// evaluates to true
String expression = "isMember('Nikola Tesla') or isMember('Albert Einstein')";
boolean trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);

// -- NOT --

// evaluates to false
boolean falseValue = parser.parseExpression("!true").getValue(Boolean.class);

// -- AND and NOT --
String expression = "isMember('Nikola Tesla') and !isMember('Mihajlo Pupin')";
boolean falseValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);
Kotlin
// -- AND --

// evaluates to false
val falseValue = parser.parseExpression("true and false").getValue(Boolean::class.java)

// evaluates to true
val expression = "isMember('Nikola Tesla') and isMember('Mihajlo Pupin')"
val trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean::class.java)

// -- OR --

// evaluates to true
val trueValue = parser.parseExpression("true or false").getValue(Boolean::class.java)

// evaluates to true
val expression = "isMember('Nikola Tesla') or isMember('Albert Einstein')"
val trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean::class.java)

// -- NOT --

// evaluates to false
val falseValue = parser.parseExpression("!true").getValue(Boolean::class.java)

// -- AND and NOT --
val expression = "isMember('Nikola Tesla') and !isMember('Mihajlo Pupin')"
val falseValue = parser.parseExpression(expression).getValue(societyContext, Boolean::class.java)
数学演算子

数字と文字列の両方で加算演算子を使用できます。減算演算子、乗算演算子、除算演算子は、数値に対してのみ使用できます。モジュラス(%)および指数乗(^)演算子も使用できます。標準の演算子の優先順位が適用されます。次の例は、使用中の数学演算子を示しています。

Java
// Addition
int two = parser.parseExpression("1 + 1").getValue(Integer.class);  // 2

String testString = parser.parseExpression(
        "'test' + ' ' + 'string'").getValue(String.class);  // 'test string'

// Subtraction
int four = parser.parseExpression("1 - -3").getValue(Integer.class);  // 4

double d = parser.parseExpression("1000.00 - 1e4").getValue(Double.class);  // -9000

// Multiplication
int six = parser.parseExpression("-2 * -3").getValue(Integer.class);  // 6

double twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(Double.class);  // 24.0

// Division
int minusTwo = parser.parseExpression("6 / -3").getValue(Integer.class);  // -2

double one = parser.parseExpression("8.0 / 4e0 / 2").getValue(Double.class);  // 1.0

// Modulus
int three = parser.parseExpression("7 % 4").getValue(Integer.class);  // 3

int one = parser.parseExpression("8 / 5 % 2").getValue(Integer.class);  // 1

// Operator precedence
int minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(Integer.class);  // -21
Kotlin
// Addition
val two = parser.parseExpression("1 + 1").getValue(Int::class.java)  // 2

val testString = parser.parseExpression(
        "'test' + ' ' + 'string'").getValue(String::class.java)  // 'test string'

// Subtraction
val four = parser.parseExpression("1 - -3").getValue(Int::class.java)  // 4

val d = parser.parseExpression("1000.00 - 1e4").getValue(Double::class.java)  // -9000

// Multiplication
val six = parser.parseExpression("-2 * -3").getValue(Int::class.java)  // 6

val twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(Double::class.java)  // 24.0

// Division
val minusTwo = parser.parseExpression("6 / -3").getValue(Int::class.java)  // -2

val one = parser.parseExpression("8.0 / 4e0 / 2").getValue(Double::class.java)  // 1.0

// Modulus
val three = parser.parseExpression("7 % 4").getValue(Int::class.java)  // 3

val one = parser.parseExpression("8 / 5 % 2").getValue(Int::class.java)  // 1

// Operator precedence
val minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(Int::class.java)  // -21
割り当て演算子

プロパティを設定するには、代入演算子(=)を使用します。これは通常、setValue への呼び出し内で行われますが、getValue への呼び出し内でも行うことができます。次のリストは、代入演算子を使用する両方の方法を示しています。

Java
Inventor inventor = new Inventor();
EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();

parser.parseExpression("name").setValue(context, inventor, "Aleksandar Seovic");

// alternatively
String aleks = parser.parseExpression(
        "name = 'Aleksandar Seovic'").getValue(context, inventor, String.class);
Kotlin
val inventor = Inventor()
val context = SimpleEvaluationContext.forReadWriteDataBinding().build()

parser.parseExpression("name").setValue(context, inventor, "Aleksandar Seovic")

// alternatively
val aleks = parser.parseExpression(
        "name = 'Aleksandar Seovic'").getValue(context, inventor, String::class.java)

4.3.8. タイプ

特別な T 演算子を使用して、java.lang.Class (タイプ)のインスタンスを指定できます。静的メソッドもこの演算子を使用して呼び出されます。StandardEvaluationContext は TypeLocator を使用して型を検索し、StandardTypeLocator (交換可能)は java.lang パッケージを理解して構築されています。つまり、java.lang 内の型への T() 参照は完全に修飾する必要はありませんが、他のすべての型参照は修飾する必要があります。次の例は、T 演算子の使用方法を示しています。

Java
Class dateClass = parser.parseExpression("T(java.util.Date)").getValue(Class.class);

Class stringClass = parser.parseExpression("T(String)").getValue(Class.class);

boolean trueValue = parser.parseExpression(
        "T(java.math.RoundingMode).CEILING < T(java.math.RoundingMode).FLOOR")
        .getValue(Boolean.class);
Kotlin
val dateClass = parser.parseExpression("T(java.util.Date)").getValue(Class::class.java)

val stringClass = parser.parseExpression("T(String)").getValue(Class::class.java)

val trueValue = parser.parseExpression(
        "T(java.math.RoundingMode).CEILING < T(java.math.RoundingMode).FLOOR")
        .getValue(Boolean::class.java)

4.3.9. コンストラクター

new 演算子を使用して、コンストラクターを呼び出すことができます。プリミティブ型(intfloat など)およびストリングを除くすべてのクラスに完全修飾クラス名を使用する必要があります。次の例は、new 演算子を使用してコンストラクターを呼び出す方法を示しています。

Java
Inventor einstein = p.parseExpression(
        "new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German')")
        .getValue(Inventor.class);

//create new inventor instance within add method of List
p.parseExpression(
        "Members.add(new org.spring.samples.spel.inventor.Inventor(
            'Albert Einstein', 'German'))").getValue(societyContext);
Kotlin
val einstein = p.parseExpression(
        "new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German')")
        .getValue(Inventor::class.java)

//create new inventor instance within add method of List
p.parseExpression(
        "Members.add(new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German'))")
        .getValue(societyContext)

4.3.10. 変数

#variableName 構文を使用して、式の変数を参照できます。変数は、EvaluationContext 実装で setVariable メソッドを使用して設定されます。

有効な変数名は、サポートされている次の文字の 1 つ以上で構成されている必要があります。

  • 手紙 : A から Z および a から z

  • 桁 : 0 から 9

  • 下線 : _

  • ドル記号 : $

次の例は、変数の使用方法を示しています。

Java
Inventor tesla = new Inventor("Nikola Tesla", "Serbian");

EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();
context.setVariable("newName", "Mike Tesla");

parser.parseExpression("name = #newName").getValue(context, tesla);
System.out.println(tesla.getName())  // "Mike Tesla"
Kotlin
val tesla = Inventor("Nikola Tesla", "Serbian")

val context = SimpleEvaluationContext.forReadWriteDataBinding().build()
context.setVariable("newName", "Mike Tesla")

parser.parseExpression("name = #newName").getValue(context, tesla)
println(tesla.name)  // "Mike Tesla"
#this および #root 変数

#this 変数は常に定義され、現在の評価オブジェクトを参照します(どの非修飾参照が解決されるかに対して)。#root 変数は常に定義され、ルートコンテキストオブジェクトを参照します。#this は式のコンポーネントが評価されると異なる場合がありますが、#root は常にルートを参照します。次の例は、#this 変数と #root 変数の使用方法を示しています。

Java
// create an array of integers
List<Integer> primes = new ArrayList<Integer>();
primes.addAll(Arrays.asList(2,3,5,7,11,13,17));

// create parser and set variable 'primes' as the array of integers
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataAccess();
context.setVariable("primes", primes);

// all prime numbers > 10 from the list (using selection ?{...})
// evaluates to [11, 13, 17]
List<Integer> primesGreaterThanTen = (List<Integer>) parser.parseExpression(
        "#primes.?[#this>10]").getValue(context);
Kotlin
// create an array of integers
val primes = ArrayList<Int>()
primes.addAll(listOf(2, 3, 5, 7, 11, 13, 17))

// create parser and set variable 'primes' as the array of integers
val parser = SpelExpressionParser()
val context = SimpleEvaluationContext.forReadOnlyDataAccess()
context.setVariable("primes", primes)

// all prime numbers > 10 from the list (using selection ?{...})
// evaluates to [11, 13, 17]
val primesGreaterThanTen = parser.parseExpression(
        "#primes.?[#this>10]").getValue(context) as List<Int>

4.3.11. 関数

式文字列内で呼び出すことができるユーザー定義関数を登録することにより、SpEL を継承できます。関数は EvaluationContext を介して登録されます。次の例は、ユーザー定義関数を登録する方法を示しています。

Java
Method method = ...;

EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
context.setVariable("myFunction", method);
Kotlin
val method: Method = ...

val context = SimpleEvaluationContext.forReadOnlyDataBinding().build()
context.setVariable("myFunction", method)

例:文字列を逆にする次のユーティリティメソッドを検討します。

Java
public abstract class StringUtils {

    public static String reverseString(String input) {
        StringBuilder backwards = new StringBuilder(input.length());
        for (int i = 0; i < input.length(); i++) {
            backwards.append(input.charAt(input.length() - 1 - i));
        }
        return backwards.toString();
    }
}
Kotlin
fun reverseString(input: String): String {
    val backwards = StringBuilder(input.length)
    for (i in 0 until input.length) {
        backwards.append(input[input.length - 1 - i])
    }
    return backwards.toString()
}

その後、次の例に示すように、前述の方法を登録して使用できます。

Java
ExpressionParser parser = new SpelExpressionParser();

EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
context.setVariable("reverseString",
        StringUtils.class.getDeclaredMethod("reverseString", String.class));

String helloWorldReversed = parser.parseExpression(
        "#reverseString('hello')").getValue(context, String.class);
Kotlin
val parser = SpelExpressionParser()

val context = SimpleEvaluationContext.forReadOnlyDataBinding().build()
context.setVariable("reverseString", ::reverseString::javaMethod)

val helloWorldReversed = parser.parseExpression(
        "#reverseString('hello')").getValue(context, String::class.java)

4.3.12. Bean 参照

評価コンテキストが Bean リゾルバーで構成されている場合、@ シンボルを使用して式から Bean を検索できます。次の例は、その方法を示しています。

Java
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
context.setBeanResolver(new MyBeanResolver());

// This will end up calling resolve(context,"something") on MyBeanResolver during evaluation
Object bean = parser.parseExpression("@something").getValue(context);
Kotlin
val parser = SpelExpressionParser()
val context = StandardEvaluationContext()
context.setBeanResolver(MyBeanResolver())

// This will end up calling resolve(context,"something") on MyBeanResolver during evaluation
val bean = parser.parseExpression("@something").getValue(context)

ファクトリ Bean 自体にアクセスするには、代わりに Bean 名の前に & シンボルを付ける必要があります。次の例は、その方法を示しています。

Java
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
context.setBeanResolver(new MyBeanResolver());

// This will end up calling resolve(context,"&foo") on MyBeanResolver during evaluation
Object bean = parser.parseExpression("&foo").getValue(context);
Kotlin
val parser = SpelExpressionParser()
val context = StandardEvaluationContext()
context.setBeanResolver(MyBeanResolver())

// This will end up calling resolve(context,"&foo") on MyBeanResolver during evaluation
val bean = parser.parseExpression("&foo").getValue(context)

4.3.13. 三項演算子 (If-Then-Else)

式内で if-then-else 条件ロジックを実行するには、三項演算子を使用できます。次のリストは、最小限の例を示しています。

Java
String falseString = parser.parseExpression(
        "false ? 'trueExp' : 'falseExp'").getValue(String.class);
Kotlin
val falseString = parser.parseExpression(
        "false ? 'trueExp' : 'falseExp'").getValue(String::class.java)

この場合、ブール値 false は文字列値 'falseExp' を返します。より現実的な例を次に示します。

Java
parser.parseExpression("name").setValue(societyContext, "IEEE");
societyContext.setVariable("queryName", "Nikola Tesla");

expression = "isMember(#queryName)? #queryName + ' is a member of the ' " +
        "+ Name + ' Society' : #queryName + ' is not a member of the ' + Name + ' Society'";

String queryResultString = parser.parseExpression(expression)
        .getValue(societyContext, String.class);
// queryResultString = "Nikola Tesla is a member of the IEEE Society"
Kotlin
parser.parseExpression("name").setValue(societyContext, "IEEE")
societyContext.setVariable("queryName", "Nikola Tesla")

expression = "isMember(#queryName)? #queryName + ' is a member of the ' " + "+ Name + ' Society' : #queryName + ' is not a member of the ' + Name + ' Society'"

val queryResultString = parser.parseExpression(expression)
        .getValue(societyContext, String::class.java)
// queryResultString = "Nikola Tesla is a member of the IEEE Society"

三項演算子のさらに短い構文については、エルビス演算子の次のセクションを参照してください。

4.3.14. エルビス演算子

Elvis 演算子は、三項演算子構文の短縮形であり、Groovy (英語) 言語で使用されます。三項演算子構文では、次の例に示すように、通常、変数を 2 回繰り返す必要があります。

String name = "Elvis Presley";
String displayName = (name != null ? name : "Unknown");

代わりに、エルビス演算子を使用できます(エルビスの髪型に似ているため)。次の例は、エルビス演算子の使用方法を示しています。

Java
ExpressionParser parser = new SpelExpressionParser();

String name = parser.parseExpression("name?:'Unknown'").getValue(new Inventor(), String.class);
System.out.println(name);  // 'Unknown'
Kotlin
val parser = SpelExpressionParser()

val name = parser.parseExpression("name?:'Unknown'").getValue(Inventor(), String::class.java)
println(name)  // 'Unknown'

次のリストは、より複雑な例を示しています。

Java
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();

Inventor tesla = new Inventor("Nikola Tesla", "Serbian");
String name = parser.parseExpression("name?:'Elvis Presley'").getValue(context, tesla, String.class);
System.out.println(name);  // Nikola Tesla

tesla.setName(null);
name = parser.parseExpression("name?:'Elvis Presley'").getValue(context, tesla, String.class);
System.out.println(name);  // Elvis Presley
Kotlin
val parser = SpelExpressionParser()
val context = SimpleEvaluationContext.forReadOnlyDataBinding().build()

val tesla = Inventor("Nikola Tesla", "Serbian")
var name = parser.parseExpression("name?:'Elvis Presley'").getValue(context, tesla, String::class.java)
println(name)  // Nikola Tesla

tesla.setName(null)
name = parser.parseExpression("name?:'Elvis Presley'").getValue(context, tesla, String::class.java)
println(name)  // Elvis Presley

Elvis 演算子を使用して、式にデフォルト値を適用できます。次の例は、@Value 式で Elvis 演算子を使用する方法を示しています。

@Value("#{systemProperties['pop3.port'] ?: 25}")

これは、定義されている場合はシステムプロパティ pop3.port を、定義されていない場合は 25 を注入します。

4.3.15. 安全なナビゲーションオペレーター

安全なナビゲーション演算子は、NullPointerException を回避するために使用され、Groovy (英語) 言語に由来します。通常、オブジェクトへの参照がある場合、オブジェクトのメソッドまたはプロパティにアクセスする前に、それが null でないことを確認する必要があります。これを回避するために、安全なナビゲーション演算子は例外をスローする代わりに null を返します。次の例は、安全なナビゲーション演算子の使用方法を示しています。

Java
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();

Inventor tesla = new Inventor("Nikola Tesla", "Serbian");
tesla.setPlaceOfBirth(new PlaceOfBirth("Smiljan"));

String city = parser.parseExpression("placeOfBirth?.city").getValue(context, tesla, String.class);
System.out.println(city);  // Smiljan

tesla.setPlaceOfBirth(null);
city = parser.parseExpression("placeOfBirth?.city").getValue(context, tesla, String.class);
System.out.println(city);  // null - does not throw NullPointerException!!!
Kotlin
val parser = SpelExpressionParser()
val context = SimpleEvaluationContext.forReadOnlyDataBinding().build()

val tesla = Inventor("Nikola Tesla", "Serbian")
tesla.setPlaceOfBirth(PlaceOfBirth("Smiljan"))

var city = parser.parseExpression("placeOfBirth?.city").getValue(context, tesla, String::class.java)
println(city)  // Smiljan

tesla.setPlaceOfBirth(null)
city = parser.parseExpression("placeOfBirth?.city").getValue(context, tesla, String::class.java)
println(city)  // null - does not throw NullPointerException!!!

4.3.16. コレクションの選択

選択は、エントリから選択することでソースコレクションを別のコレクションに変換できる強力な式言語機能です。

選択では、.?[selectionExpression] の構文を使用します。コレクションをフィルタリングし、元の要素のサブセットを含む新しいコレクションを返します。例:次の例に示すように、選択によりセルビアの発明者のリストを簡単に取得できます。

Java
List<Inventor> list = (List<Inventor>) parser.parseExpression(
        "members.?[nationality == 'Serbian']").getValue(societyContext);
Kotlin
val list = parser.parseExpression(
        "members.?[nationality == 'Serbian']").getValue(societyContext) as List<Inventor>

リストとマップの両方で選択が可能です。リストの場合、選択条件は個々のリスト要素に対して評価されます。マップに対して、選択条件は各マップエントリ(Java タイプ Map.Entry のオブジェクト)に対して評価されます。各マップエントリには、選択で使用するプロパティとしてアクセス可能なキーと値があります。

次の式は、エントリ値が 27 未満の元のマップの要素で構成される新しいマップを返します。

Java
Map newMap = parser.parseExpression("map.?[value<27]").getValue();
Kotlin
val newMap = parser.parseExpression("map.?[value<27]").getValue()

選択したすべての要素を返すことに加えて、最初または最後の値のみを取得できます。選択に一致する最初のエントリを取得するための構文は .^[selectionExpression] です。最後に一致する選択を取得するための構文は .$[selectionExpression] です。

4.3.17. コレクションの射影

射影により、コレクションは部分式の評価を促進し、結果は新しいコレクションになります。射影の構文は .![projectionExpression] です。例:発明者のリストがあるが、彼らが生まれた都市のリストが必要だとします。事実上、発明者リストのすべてのエントリについて「placeOfBirth.city」を評価したいと思います。次の例では、射影を使用してこれを行います。

Java
// returns ['Smiljan', 'Idvor' ]
List placesOfBirth = (List)parser.parseExpression("members.![placeOfBirth.city]");
Kotlin
// returns ['Smiljan', 'Idvor' ]
val placesOfBirth = parser.parseExpression("members.![placeOfBirth.city]") as List<*>

マップを使用して射影を駆動することもできます。この場合、射影式はマップの各エントリ(Java Map.Entry として表されます)に対して評価されます。マップ全体の射影の結果は、各マップエントリに対する射影式の評価で構成されるリストです。

4.3.18. 式テンプレート

式テンプレートを使用すると、リテラルテキストを 1 つ以上の評価ブロックと混合できます。各評価ブロックは、定義可能なプレフィックス文字とサフィックス文字で区切られています。一般的な選択は、次の例に示すように、#{ } を区切り文字として使用することです。

Java
String randomPhrase = parser.parseExpression(
        "random number is #{T(java.lang.Math).random()}",
        new TemplateParserContext()).getValue(String.class);

// evaluates to "random number is 0.7038186818312008"
Kotlin
val randomPhrase = parser.parseExpression(
        "random number is #{T(java.lang.Math).random()}",
        TemplateParserContext()).getValue(String::class.java)

// evaluates to "random number is 0.7038186818312008"

文字列は、リテラルテキスト 'random number is ' と #{ } 区切り文字内の式を評価した結果(この場合、その random() メソッドを呼び出した結果)を連結することにより評価されます。parseExpression() メソッドの 2 番目の引数は、タイプ ParserContext です。ParserContext インターフェースは、式テンプレート機能をサポートするために、式の解析方法に影響を与えるために使用されます。TemplateParserContext の定義は次のとおりです。

Java
public class TemplateParserContext implements ParserContext {

    public String getExpressionPrefix() {
        return "#{";
    }

    public String getExpressionSuffix() {
        return "}";
    }

    public boolean isTemplate() {
        return true;
    }
}
Kotlin
class TemplateParserContext : ParserContext {

    override fun getExpressionPrefix(): String {
        return "#{"
    }

    override fun getExpressionSuffix(): String {
        return "}"
    }

    override fun isTemplate(): Boolean {
        return true
    }
}

4.4. 例で使用されるクラス

このセクションでは、この章全体の例で使用されるクラスをリストします。

Inventor.Java
package org.spring.samples.spel.inventor;

import java.util.Date;
import java.util.GregorianCalendar;

public class Inventor {

    private String name;
    private String nationality;
    private String[] inventions;
    private Date birthdate;
    private PlaceOfBirth placeOfBirth;

    public Inventor(String name, String nationality) {
        GregorianCalendar c= new GregorianCalendar();
        this.name = name;
        this.nationality = nationality;
        this.birthdate = c.getTime();
    }

    public Inventor(String name, Date birthdate, String nationality) {
        this.name = name;
        this.nationality = nationality;
        this.birthdate = birthdate;
    }

    public Inventor() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getNationality() {
        return nationality;
    }

    public void setNationality(String nationality) {
        this.nationality = nationality;
    }

    public Date getBirthdate() {
        return birthdate;
    }

    public void setBirthdate(Date birthdate) {
        this.birthdate = birthdate;
    }

    public PlaceOfBirth getPlaceOfBirth() {
        return placeOfBirth;
    }

    public void setPlaceOfBirth(PlaceOfBirth placeOfBirth) {
        this.placeOfBirth = placeOfBirth;
    }

    public void setInventions(String[] inventions) {
        this.inventions = inventions;
    }

    public String[] getInventions() {
        return inventions;
    }
}
Inventor.kt
class Inventor(
    var name: String,
    var nationality: String,
    var inventions: Array<String>? = null,
    var birthdate: Date =  GregorianCalendar().time,
    var placeOfBirth: PlaceOfBirth? = null)
PlaceOfBirth.java
package org.spring.samples.spel.inventor;

public class PlaceOfBirth {

    private String city;
    private String country;

    public PlaceOfBirth(String city) {
        this.city=city;
    }

    public PlaceOfBirth(String city, String country) {
        this(city);
        this.country = country;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String s) {
        this.city = s;
    }

    public String getCountry() {
        return country;
    }

    public void setCountry(String country) {
        this.country = country;
    }
}
PlaceOfBirth.kt
class PlaceOfBirth(var city: String, var country: String? = null) {
Society.java
package org.spring.samples.spel.inventor;

import java.util.*;

public class Society {

    private String name;

    public static String Advisors = "advisors";
    public static String President = "president";

    private List<Inventor> members = new ArrayList<Inventor>();
    private Map officers = new HashMap();

    public List getMembers() {
        return members;
    }

    public Map getOfficers() {
        return officers;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public boolean isMember(String name) {
        for (Inventor inventor : members) {
            if (inventor.getName().equals(name)) {
                return true;
            }
        }
        return false;
    }
}
Society.kt
package org.spring.samples.spel.inventor

import java.util.*

class Society {

    val Advisors = "advisors"
    val President = "president"

    var name: String? = null

    val members = ArrayList<Inventor>()
    val officers = mapOf<Any, Any>()

    fun isMember(name: String): Boolean {
        for (inventor in members) {
            if (inventor.name == name) {
                return true
            }
        }
        return false
    }
}

5. Spring によるアスペクト指向プログラミング

アスペクト指向プログラミング(AOP)は、プログラム構造に関する別の考え方を提供することにより、オブジェクト指向プログラミング(OOP)を補完します。OOP のモジュール性の重要な単位はクラスですが、AOP のモジュール性の単位はアスペクトです。アスペクトにより、複数のタイプとオブジェクトにまたがる懸念事項(トランザクション管理など)のモジュール化が可能になります。(このような懸念は、AOP の文献ではしばしば「横断的」懸念と呼ばれています。)

Spring の重要なコンポーネントの 1 つは、AOP フレームワークです。Spring IoC コンテナーは AOP に依存しませんが(必要ない場合は AOP を使用する必要はありません)、AOP は Spring IoC を補完して、非常に有能なミドルウェアソリューションを提供します。

AspectJ ポイントカットを使用した Spring AOP

Spring は、スキーマベースのアプローチまたは @AspectJ アノテーションスタイルのいずれかを使用して、カスタムアスペクトを記述するシンプルかつ強力な方法を提供します。これらのスタイルは両方とも、Spring AOP をウィービングに使用しながら、完全に型指定されたアドバイスと AspectJ ポイントカット言語の使用を提供します。

この章では、スキーマおよび @AspectJ ベースの AOP サポートについて説明します。下位レベルの AOP サポートについては、次の章で説明します

AOP は Spring Framework で次の目的で使用されます。

  • 宣言的なエンタープライズサービスを提供します。最も重要なそのようなサービスは、宣言的なトランザクション管理です。

  • ユーザーにカスタムアスペクトを実装させ、OOP と AOP の使用を補完します。

汎用の宣言型サービスまたはプーリングなどの事前にパッケージ化された宣言型ミドルウェアサービスのみに関心がある場合は、Spring AOP を直接操作する必要はなく、この章のほとんどをスキップできます。

5.1. AOP の概念

いくつかの中心的な AOP の概念と用語を定義することから始めましょう。これらの用語は、Spring 固有のものではありません。残念ながら、AOP の用語は特に直感的ではありません。ただし、Spring が独自の用語を使用すると、さらに混乱を招きます。

  • アスペクト : 複数のクラスにまたがる関心事のモジュール化。トランザクション管理は、エンタープライズ Java アプリケーションにおける横断的な関心事の良い例です。Spring AOP では、アスペクトは通常のクラス(スキーマベースのアプローチ)または @Aspect アノテーションが付けられた通常のクラス(@AspectJ スタイル)を使用して実装されます。

  • 参加ポイント : メソッドの実行や例外の処理など、プログラムの実行中のポイント。Spring AOP では、ジョインポイントは常にメソッドの実行を表します。

  • 助言 : 特定のジョインポイントでアスペクトによって実行されるアクション。さまざまな種類のアドバイスには、「around」、「before」、「after」アドバイスが含まれます。(アドバイスのタイプについては後で説明します)Spring を含む多くの AOP フレームワークは、アドバイスをインターセプターとしてモデル化し、ジョインポイント周辺でインターセプターのチェーンを維持します。

  • ポイントカット : ジョインポイントに一致する述語。アドバイスはポイントカット式に関連付けられ、ポイントカットに一致する任意のジョインポイントで実行されます(たとえば、特定の名前のメソッドの実行)。ポイントカット式と一致するジョインポイントの概念は AOP の中心であり、Spring はデフォルトで AspectJ ポイントカット式言語を使用します。

  • 導入 : 型に代わって追加のメソッドまたはフィールドを宣言します。Spring AOP を使用すると、推奨オブジェクトに新しいインターフェース(および対応する実装)を導入できます。例:導入を使用して、Bean に IsModified インターフェースを実装させ、キャッシングを簡素化できます。(概要は、AspectJ コミュニティでの型間宣言として知られています。)

  • 対象物 : 1 つ以上のアスペクトによってアドバイスされているオブジェクト。「推奨オブジェクト」とも呼ばれます。Spring AOP はランタイムプロキシを使用して実装されるため、このオブジェクトは常にプロキシオブジェクトです。

  • AOP プロキシ : アスペクト契約を実装するために AOP フレームワークによって作成されたオブジェクト(メソッドの実行などをアドバイス)。Spring Framework では、AOP プロキシは JDK 動的プロキシまたは CGLIB プロキシです。

  • 機織り : アスペクトを他のアプリケーションタイプまたはオブジェクトとリンクして、推奨オブジェクトを作成します。これは、コンパイル時(たとえば、AspectJ コンパイラを使用)、ロード時、または実行時に実行できます。Spring AOP は、他の純粋な Java AOP フレームワークと同様に、実行時にウィービングを実行します。

Spring AOP には、次の種類のアドバイスが含まれています。

  • Before アドバイス : ジョインポイントの前に実行されるが、例外がスローされない限り、実行フローがジョインポイントに進むことを防ぐ機能がないアドバイス。

  • After returning アドバイス : ジョインポイントが正常に完了した後に実行するアドバイス(たとえば、メソッドが例外をスローせずに戻る場合)。

  • After throwing アドバイス : 例外をスローしてメソッドが終了した場合に実行されるアドバイス。

  • After (finally) アドバイス : ジョインポイントが存在する方法(通常または例外的なリターン)に関係なく実行されるアドバイス。

  • Around アドバイス : メソッド呼び出しなどのジョインポイントを囲むアドバイス。これは最も強力なアドバイスです。Around アドバイスは、メソッド呼び出しの前後にカスタム動作を実行できます。また、ジョインポイントに進むか、独自の戻り値を返すか例外をスローすることにより、推奨されるメソッド実行をショートカットするかを選択する責任もあります。

Around アドバイスは、最も一般的な種類のアドバイスです。Spring AOP は、AspectJ と同様、あらゆる種類のアドバイスを提供するため、必要な動作を実装できる最も強力でないアドバイスタイプを使用することをお勧めします。例:メソッドの戻り値でキャッシュを更新するだけでよい場合は、around アドバイスよりも after returning アドバイスを実装する方が適切ですが、around アドバイスでも同じことができます。最も具体的なアドバイスタイプを使用すると、エラーが発生する可能性が低く、シンプルなプログラミングモデルが提供されます。例:回避アドバイスに使用される JoinPoint で proceed() メソッドを呼び出す必要はないため、呼び出しに失敗することはありません。

すべてのアドバイスパラメーターは静的に型付けされているため、Object 配列ではなく、適切な型(たとえば、メソッド実行からの戻り値の型)のアドバイスパラメーターを操作できます。

ポイントカットと一致するジョインポイントの概念は、AOP の鍵であり、インターセプトのみを提供する古いテクノロジーと区別します。ポイントカットを使用すると、オブジェクト指向の階層とは無関係にアドバイスをターゲットにできます。例:複数のオブジェクト(サービス層のすべてのビジネスオペレーションなど)にまたがる一連のメソッドに宣言型トランザクション管理を提供するアラウンドアドバイスを適用できます。

5.2. Spring AOP の機能とゴール

Spring AOP は、純粋な Java で実装されています。特別なコンパイルプロセスは必要ありません。Spring AOP はクラスローダー階層を制御する必要がないため、サーブレットコンテナーまたはアプリケーションサーバーでの使用に適しています。

Spring AOP は現在、メソッド実行の結合点(Spring Bean でのメソッドの実行をアドバイスする)のみをサポートしています。フィールドインターセプトは実装されていませんが、コア Spring AOP API を壊すことなくフィールドインターセプトのサポートを追加できます。フィールドアクセスをアドバイスし、ジョインポイントを更新する必要がある場合は、AspectJ などの言語を検討してください。

Spring AOP の AOP へのアプローチは、他のほとんどの AOP フレームワークのアプローチとは異なります。目的は、最も完全な AOP 実装を提供することではありません(ただし、Spring AOP は非常に優れています)。むしろ、AOP 実装と Spring IoC を密接に統合して、エンタープライズアプリケーションの一般的な問題を解決することを目的としています。

たとえば、Spring Framework の AOP 機能は通常、Spring IoC コンテナーと組み合わせて使用されます。アスペクトは、通常の Bean 定義構文を使用して構成されます(ただし、これにより強力な「自動プロキシ」機能が可能になります)。これは、他の AOP 実装との決定的な違いです。Spring AOP では、非常にきめの細かいオブジェクト(通常はドメインオブジェクト)のアドバイスなど、いくつかのことを簡単または効率的に行うことはできません。このような場合には、AspectJ が最適です。ただし、私たちの経験では、Spring AOP は、AOP に対応しているエンタープライズ Java アプリケーションのほとんどの問題に対する優れたソリューションを提供します。

Spring AOP は、包括的な AOP ソリューションを提供するために AspectJ と競合することは決してありません。Spring AOP などのプロキシベースのフレームワークと AspectJ などの本格的なフレームワークはどちらも価値があり、競争ではなく補完的なものであると考えています。Spring は、Spring AOP と IoC を AspectJ とシームレスに統合し、一貫した Spring ベースのアプリケーションアーキテクチャ内で AOP のすべての使用を可能にします。この統合は、Spring AOP API または AOP Alliance API には影響しません。Spring AOP は下位互換性を維持しています。Spring AOP API の説明については、次の章を参照してください。

Spring Framework の中心的な教義の 1 つは、非侵襲性です。これは、フレームワーク固有のクラスとインターフェースをビジネスモデルまたはドメインモデルに強制的に導入するべきではないという考え方です。ただし、一部の場所では、Spring Framework は、Spring フレームワーク固有の依存関係をコードベースに導入するオプションを提供します。そのようなオプションを提供する理由は、特定のシナリオでは、そのようなメソッドで特定の機能の一部を読んだりコーディングしたりするのが簡単です。ただし、Spring Framework(ほとんど)は常に選択肢を提供します。特定のユースケースまたはシナリオに最適なオプションについて、十分な情報に基づいて判断することができます。

この章に関連するそのような選択の 1 つは、どの AOP フレームワーク(およびどの AOP スタイル)を選択するかです。AspectJ、Spring AOP、またはその両方を選択できます。また、@AspectJ アノテーションスタイルのアプローチまたは Spring XML 構成スタイルのアプローチのいずれかを選択できます。この章が最初に @AspectJ スタイルのアプローチを導入することを選択したという事実は、Spring チームが Spring XML 構成スタイルよりも @AspectJ アノテーションスタイルのアプローチを好むことを示すものと解釈すべきではありません。

各スタイルの「理由と理由」の詳細については、使用する AOP 宣言スタイルの選択を参照してください。

5.3. AOP プロキシ

Spring AOP は、デフォルトで AOP プロキシに標準 JDK 動的プロキシを使用します。これにより、任意のインターフェース(またはインターフェースのセット)をプロキシできます。

Spring AOP は CGLIB プロキシも使用できます。これは、インターフェースではなくクラスをプロキシするために必要です。デフォルトでは、ビジネスオブジェクトがインターフェースを実装しない場合、CGLIB が使用されます。クラスではなくインターフェースにプログラミングすることをお勧めするため、ビジネスクラスは通常 1 つ以上のビジネスインターフェースを実装します。インターフェース上で宣言されていないメソッドをアドバイスする必要がある場合や、プロキシオブジェクトをメソッドに具象型として渡す必要がある場合(まれに)に、CGLIB の使用を強制することができます。

Spring AOP はプロキシベースであるという事実を理解することが重要です。この実装の詳細が実際に何を意味するかを正確に調べるには、AOP プロキシについてを参照してください。

5.4. @AspectJ サポート

@AspectJ は、アスペクトをアノテーション付きの通常の Java クラスとして宣言するスタイルを指します。@AspectJ スタイルは、AspectJ 5 リリースの一部として AspectJ プロジェクト (英語) によって導入されました。Spring は、ポイントカットの解析とマッチングのために AspectJ が提供するライブラリを使用して、AspectJ 5 と同じアノテーションを解釈します。ただし、AOP ランタイムは依然として純粋な Spring AOP であり、AspectJ コンパイラーまたはウィーバーへの依存関係はありません。

AspectJ コンパイラと weaver を使用すると、完全な AspectJ 言語の使用が可能になります。これについては、Spring アプリケーションでの AspectJ の使用で説明しています。

5.4.1. @AspectJ のサポートを有効にする

Spring 構成で @AspectJ アスペクトを使用するには、@AspectJ アスペクトに基づいて Spring AOP を構成するための Spring サポートと、それらのアスペクトによってアドバイスされているかどうかに基づいて自動プロキシ Bean を有効にする必要があります。自動プロキシとは、Spring が Bean が 1 つ以上のアスペクトからアドバイスを受けていると判断した場合、その Bean のプロキシを自動的に生成してメソッド呼び出しをインターセプトし、必要に応じてアドバイスが実行されるようにすることを意味します。

@AspectJ サポートは、XML または Java スタイルの構成で有効にできます。いずれの場合も、AspectJ の aspectjweaver.jar ライブラリがアプリケーションのクラスパス(バージョン 1.8 以降)にあることを確認する必要があります。このライブラリは、AspectJ ディストリビューションの lib ディレクトリまたは Maven セントラルリポジトリから入手できます。

Java 構成で @AspectJ サポートを有効にする

Java @Configuration で @AspectJ サポートを有効にするには、次の例に示すように、@EnableAspectJAutoProxy アノテーションを追加します。

Java
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {

}
Kotlin
@Configuration
@EnableAspectJAutoProxy
class AppConfig
XML 設定で @AspectJ サポートを有効にする

XML ベースの構成で @AspectJ サポートを有効にするには、次の例に示すように、aop:aspectj-autoproxy 要素を使用します。

<aop:aspectj-autoproxy/>

これは、XML スキーマベースの構成で説明されているスキーマサポートを使用することを前提としています。aop 名前空間にタグをインポートする方法については、AOP スキーマを参照してください。

5.4.2. アスペクトを宣言する

@AspectJ サポートを有効にすると、@AspectJ アスペクト(@Aspect アノテーションを持つ)のクラスを使用してアプリケーションコンテキストで定義された Bean は、Spring によって自動的に検出され、Spring AOP の構成に使用されます。次の 2 つの例は、あまり有用ではないアスペクトに必要な最小限の定義を示しています。

2 つの例の最初は、@Aspect アノテーションを持つ Bean クラスを指すアプリケーションコンテキストの通常の Bean 定義を示しています。

<bean id="myAspect" class="org.xyz.NotVeryUsefulAspect">
    <!-- configure properties of the aspect here -->
</bean>

2 つの例の 2 番目は、NotVeryUsefulAspect クラス定義を示しています。これには、org.aspectj.lang.annotation.Aspect アノテーションが付けられています。

Java
package org.xyz;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class NotVeryUsefulAspect {

}
Kotlin
package org.xyz

import org.aspectj.lang.annotation.Aspect;

@Aspect
class NotVeryUsefulAspect

アスペクト(@Aspect アノテーションが付けられたクラス)は、他のクラスと同じようにメソッドとフィールドを持つことができます。また、ポイントカット、アドバイス、導入(タイプ間)宣言を含めることもできます。

コンポーネントのスキャンによるアスペクトの自動検出
他の Spring 管理 Bean と同じように、アスペクトクラスを Spring XML 構成の通常の Bean として登録したり、クラスパススキャンで自動検出したりできます。ただし、@Aspect アノテーションは、クラスパスでの自動検出には不十分であることに注意してください。そのためには、別の @Component アノテーション(または、Spring のコンポーネントスキャナーのルールに従って修飾するカスタムステレオタイプアノテーション)を追加する必要があります。
アスペクトを他のアスペクトにアドバイスできますか?
Spring AOP では、アスペクト自体を他のアスペクトからのアドバイスの対象にすることはできません。クラスの @Aspect アノテーションは、それをアスペクトとしてマークするため、自動プロキシから除外します。

5.4.3. ポイントカットの宣言

ポイントカットは、関心のあるジョインポイントを決定するため、アドバイスの実行時期を制御できます。Spring AOP は Spring Bean のメソッド実行ジョインポイントのみをサポートするため、ポイントカットは Spring Bean でのメソッドの実行と一致すると考えることができます。ポイントカット宣言には 2 つの部分があります。名前とパラメーターを含むシグネチャーと、対象のメソッド実行を正確に決定するポイントカット式です。AOP の @AspectJ アノテーションスタイルでは、ポイントカットシグネチャーは通常のメソッド定義によって提供されます。ポイントカット式は、@Pointcut アノテーションを使用して示されます(ポイントカットシグネチャーとして機能するメソッドには、void 戻り型が必要です)。

例は、ポイントカット署名とポイントカット表現のこの区別を明確にできます。次の例では、transfer という名前のメソッドの実行に一致する anyOldTransfer という名前のポイントカットを定義しています。

Java
@Pointcut("execution(* transfer(..))") // the pointcut expression
private void anyOldTransfer() {} // the pointcut signature
Kotlin
@Pointcut("execution(* transfer(..))") // the pointcut expression
private fun anyOldTransfer() {} // the pointcut signature

@Pointcut アノテーションの値を形成するポイントカット式は、通常の AspectJ 5 ポイントカット式です。AspectJ のポイントカット言語の詳細については、AspectJ プログラミングガイド (英語) (および拡張機能については AspectJ 5 開発者向けノートブック (英語) )または AspectJ に関する本の 1 つ(たとえば、Collyer et al。による Eclipse AspectJ、または Ramnivas Laddad による AspectJ in Action)を参照してください。

サポートされているポイントカット指定子

Spring AOP は、ポイントカット式で使用するために、次の AspectJ ポイントカット指定子(PCD)をサポートしています。

  • execution: マッチングメソッドの実行のジョインポイント。これは、Spring AOP で作業するときに使用する主要なポイントカット指定子です。

  • within: 特定のタイプ内のジョインポイントへの一致を制限します(Spring AOP を使用する場合、一致するタイプ内で宣言されたメソッドの実行)。

  • this: Bean 参照(Spring AOP プロキシ)が指定されたタイプのインスタンスであるジョインポイント(Spring AOP を使用する場合のメソッドの実行)へのマッチングを制限します。

  • target: ターゲットオブジェクト(プロキシ化されるアプリケーションオブジェクト)が指定されたタイプのインスタンスであるジョインポイント(Spring AOP を使用する場合のメソッドの実行)へのマッチングを制限します。

  • args: 引数が指定されたタイプのインスタンスであるジョインポイント(Spring AOP を使用する場合のメソッドの実行)へのマッチングを制限します。

  • @target: 実行中のオブジェクトのクラスに特定のタイプのアノテーションがあるジョインポイント(Spring AOP を使用する場合のメソッドの実行)へのマッチングを制限します。

  • @args: 渡される実際の引数の実行時の型が指定された型のアノテーションを持っている結合点(Spring AOP を使用する場合のメソッドの実行)へのマッチングを制限します。

  • @within: 指定されたアノテーションを持つ型内のジョインポイントへのマッチングを制限します(Spring AOP を使用する場合、指定されたアノテーションを持つ型で宣言されたメソッドの実行)。

  • @annotation: ジョインポイントのサブジェクト(Spring AOP で実行されているメソッド)に特定のアノテーションが付いているジョインポイントにマッチングを制限します。

その他のポイントカットタイプ

完全な AspectJ ポイントカット言語は、Spring でサポートされていない追加のポイントカット指定子をサポートしています: callgetsetpreinitializationstaticinitializationinitializationhandleradviceexecutionwithincodecflowcflowbelowif@this および @withincode。Spring AOP によって解釈されるポイントカット式でこれらのポイントカット指定子を使用すると、IllegalArgumentException がスローされます。

Spring AOP でサポートされるポイントカット指定子のセットは、今後のリリースで拡張され、より多くの AspectJ ポイントカット指定子をサポートする可能性があります。

Spring AOP はメソッド実行の結合点のみにマッチングを制限するため、ポイントカット指定子に関する前述の説明では、AspectJ プログラミングガイドで見つけることができるよりも狭い定義を示しています。さらに、AspectJ 自体には型ベースのセマンティクスがあり、実行ジョインポイントでは、this と target の両方が同じオブジェクト(メソッドを実行するオブジェクト)を参照します。Spring AOP はプロキシベースのシステムであり、プロキシオブジェクト自体(this にバインドされている)とプロキシの背後のターゲットオブジェクト(target にバインドされている)を区別します。

Spring の AOP フレームワークのプロキシベースの性質により、ターゲットオブジェクト内の呼び出しは、定義上、インターセプトされません。JDK プロキシの場合、プロキシ上のパブリックインターフェースメソッド呼び出しのみをインターセプトできます。CGLIB を使用すると、プロキシでのパブリックおよび protected メソッド呼び出しがインターセプトされます(必要に応じて、パッケージ private メソッドも)。ただし、プロキシを介した一般的な相互作用は、常に公開署名を介して設計する必要があります。

ポイントカットの定義は通常、インターセプトされたメソッドと一致することに注意してください。プロキシを介した潜在的な非公開相互作用がある CGLIB プロキシシナリオであっても、ポイントカットが厳密に公開専用であることを意図している場合、それに応じて定義する必要があります。

インターセプトにターゲットクラス内のメソッド呼び出しまたはコンストラクターを含める必要がある場合は、Spring のプロキシベースの AOP フレームワークの代わりに、Spring 駆動のネイティブ AspectJ ウィービングの使用を検討してください。これは、異なる特性を備えた AOP 使用の異なるモードを構成するため、決定を下す前に、ウィービングに精通してください。

Spring AOP は、bean という名前の追加の PCD もサポートします。この PCD を使用すると、ジョインポイントの一致を特定の名前付き Spring Bean または名前付き Spring Bean のセット(ワイルドカードを使用する場合)に制限できます。bean PCD の形式は次のとおりです。

Java
bean(idOrNameOfBean)
Kotlin
bean(idOrNameOfBean)

idOrNameOfBean トークンは、任意の Spring Bean の名前にすることができます。* 文字を使用する制限されたワイルドカードサポートが提供されるため、Spring Bean の命名規則を確立する場合、bean PCD 式を記述して選択できます。他のポイントカット指定子の場合と同様に、bean PCD は、&& (および)、|| (または)、! (否定)演算子でも使用できます。

bean PCD は Spring AOP でのみサポートされ、ネイティブの AspectJ ウィービングではサポートされません。これは、AspectJ が定義する標準 PCD に対する Spring 固有の拡張であるため、@Aspect モデルで宣言されたアスペクトでは使用できません。

bean PCD は、型レベル(ウィービングベースの AOP が制限されている)だけでなく、インスタンスレベル(Spring Bean の名前の概念に基づいて構築)で動作します。インスタンスベースのポイントカット指定子は、Spring のプロキシベースの AOP フレームワークの特別な機能であり、Spring Bean ファクトリとの緊密な統合であり、特定の Bean を名前で識別するのが自然で簡単です。

ポイントカット式の組み合わせ

&&,|| と ! を使用して、ポイントカット式を組み合わせることができます。ポイントカット式を名前で参照することもできます。次の例は、3 つのポイントカット式を示しています。

Java
@Pointcut("execution(public * *(..))")
private void anyPublicOperation() {} (1)

@Pointcut("within(com.xyz.myapp.trading..*)")
private void inTrading() {} (2)

@Pointcut("anyPublicOperation() && inTrading()")
private void tradingOperation() {} (3)
1anyPublicOperation は、メソッド実行ジョインポイントが public メソッドの実行を表す場合に一致します。
2inTrading は、メソッドの実行が取引モジュール内にある場合に一致します。
3tradingOperation は、メソッドの実行が取引モジュールの public メソッドを表す場合に一致します。
Kotlin
@Pointcut("execution(public * *(..))")
private fun anyPublicOperation() {} (1)

@Pointcut("within(com.xyz.myapp.trading..*)")
private fun inTrading() {} (2)

@Pointcut("anyPublicOperation() && inTrading()")
private fun tradingOperation() {} (3)
1anyPublicOperation は、メソッド実行ジョインポイントが public メソッドの実行を表す場合に一致します。
2inTrading は、メソッドの実行が取引モジュール内にある場合に一致します。
3tradingOperation は、メソッドの実行が取引モジュールの public メソッドを表す場合に一致します。

前に示したように、より小さな名前のコンポーネントからより複雑なポイントカット式を構築することをお勧めします。名前でポイントカットを参照する場合、通常の Java 可視性ルールが適用されます(同じタイプのプライベートポイントカット、階層内の保護されたポイントカット、どこでもパブリックポイントカットなどを表示できます)。可視性はポイントカットのマッチングには影響しません。

共通のポイントカット定義を共有する

エンタープライズアプリケーションを使用する場合、開発者はアプリケーションのモジュールと特定の操作セットをいくつかのアスペクトから参照することを望みます。この目的で一般的なポイントカット式をキャプチャーする CommonPointcuts アスペクトを定義することをお勧めします。このようなアスペクトは通常、次の例のようになります。

Java
package com.xyz.myapp;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class CommonPointcuts {

    /**
     * A join point is in the web layer if the method is defined
     * in a type in the com.xyz.myapp.web package or any sub-package
     * under that.
     */
    @Pointcut("within(com.xyz.myapp.web..*)")
    public void inWebLayer() {}

    /**
     * A join point is in the service layer if the method is defined
     * in a type in the com.xyz.myapp.service package or any sub-package
     * under that.
     */
    @Pointcut("within(com.xyz.myapp.service..*)")
    public void inServiceLayer() {}

    /**
     * A join point is in the data access layer if the method is defined
     * in a type in the com.xyz.myapp.dao package or any sub-package
     * under that.
     */
    @Pointcut("within(com.xyz.myapp.dao..*)")
    public void inDataAccessLayer() {}

    /**
     * A business service is the execution of any method defined on a service
     * interface. This definition assumes that interfaces are placed in the
     * "service" package, and that implementation types are in sub-packages.
     *
     * If you group service interfaces by functional area (for example,
     * in packages com.xyz.myapp.abc.service and com.xyz.myapp.def.service) then
     * the pointcut expression "execution(* com.xyz.myapp..service.*.*(..))"
     * could be used instead.
     *
     * Alternatively, you can write the expression using the 'bean'
     * PCD, like so "bean(*Service)". (This assumes that you have
     * named your Spring service beans in a consistent fashion.)
     */
    @Pointcut("execution(* com.xyz.myapp..service.*.*(..))")
    public void businessService() {}

    /**
     * A data access operation is the execution of any method defined on a
     * dao interface. This definition assumes that interfaces are placed in the
     * "dao" package, and that implementation types are in sub-packages.
     */
    @Pointcut("execution(* com.xyz.myapp.dao.*.*(..))")
    public void dataAccessOperation() {}

}
Kotlin
package com.xyz.myapp

import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Pointcut

@Aspect
class CommonPointcuts {

    /**
    * A join point is in the web layer if the method is defined
    * in a type in the com.xyz.myapp.web package or any sub-package
    * under that.
    */
    @Pointcut("within(com.xyz.myapp.web..*)")
    fun inWebLayer() {
    }

    /**
    * A join point is in the service layer if the method is defined
    * in a type in the com.xyz.myapp.service package or any sub-package
    * under that.
    */
    @Pointcut("within(com.xyz.myapp.service..*)")
    fun inServiceLayer() {
    }

    /**
    * A join point is in the data access layer if the method is defined
    * in a type in the com.xyz.myapp.dao package or any sub-package
    * under that.
    */
    @Pointcut("within(com.xyz.myapp.dao..*)")
    fun inDataAccessLayer() {
    }

    /**
    * A business service is the execution of any method defined on a service
    * interface. This definition assumes that interfaces are placed in the
    * "service" package, and that implementation types are in sub-packages.
    *
    * If you group service interfaces by functional area (for example,
    * in packages com.xyz.myapp.abc.service and com.xyz.myapp.def.service) then
    * the pointcut expression "execution(* com.xyz.myapp..service.*.*(..))"
    * could be used instead.
    *
    * Alternatively, you can write the expression using the 'bean'
    * PCD, like so "bean(*Service)". (This assumes that you have
    * named your Spring service beans in a consistent fashion.)
    */
    @Pointcut("execution(* com.xyz.myapp..service.*.*(..))")
    fun businessService() {
    }

    /**
    * A data access operation is the execution of any method defined on a
    * dao interface. This definition assumes that interfaces are placed in the
    * "dao" package, and that implementation types are in sub-packages.
    */
    @Pointcut("execution(* com.xyz.myapp.dao.*.*(..))")
    fun dataAccessOperation() {
    }

}

このようなアスペクトで定義されたポイントカットは、ポイントカット式が必要な場所であればどこでも参照できます。例:サービス層をトランザクション化するには、次のように記述できます。

<aop:config>
    <aop:advisor
        pointcut="com.xyz.myapp.CommonPointcuts.businessService()"
        advice-ref="tx-advice"/>
</aop:config>

<tx:advice id="tx-advice">
    <tx:attributes>
        <tx:method name="*" propagation="REQUIRED"/>
    </tx:attributes>
</tx:advice>

<aop:config> および <aop:advisor> 要素については、スキーマベースの AOP サポートで説明しています。トランザクション要素については、トランザクション管理で説明しています。

サンプル

Spring AOP ユーザーは、execution ポイントカット指定子を最も頻繁に使用する可能性があります。実行式の形式は次のとおりです。

    execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern)
                throws-pattern?)

返されるタイプパターン(前のスニペットの ret-type-pattern)、名前パターン、パラメーターパターンを除くすべての部分はオプションです。戻り値