最新の安定バージョンについては、Spring Framework 7.0.7 を使用してください!

コンテナー拡張ポイント

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

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 を早期にインスタンス化する必要があるため、この早期型検出は重要です。

さらに、@Bean ファクトリメソッドを介して BeanPostProcessor を登録する場合は、メソッドを static として宣言し、できれば依存関係をなくしてください。そうすることで、設定クラスや他の Bean の即時初期化を回避できます。即時初期化が行われると、完全な後処理(自動プロキシなど)が利用できなくなります。詳細は、@Bean (Javadoc) の Javadoc にある「BeanPostProcessor が返す @Bean メソッド」のセクションを参照してください。

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

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

このような Bean が発生した場合、以下のような WARN レベルのログメッセージが表示されます。

型 [org.example.SomeType] の Bean 'someBean' は、すべての BeanPostProcessors による処理の対象ではありません (たとえば、自動プロキシの対象ではありません)。

影響を受ける Bean の数を最小限に抑えるには、依存関係のない static @Bean メソッドで BeanPostProcessor を登録します (上記の注記を参照)。自動ワイヤリングまたは @Resource (自動ワイヤリングにフォールバックする場合があります) を使用して BeanPostProcessor に Bean がワイヤリングされている場合、Spring は型に一致する依存関係の候補を検索する際に予期しない Bean にアクセスし、その結果、自動プロキシやその他の種類の Bean 後処理の対象外になる可能性があります。例: フィールドまたは setter 名が Bean の宣言された名前に直接対応せず、名前属性が使用されていない @Resource でアノテーションが付けられた依存関係がある場合、Spring は型で一致させるために他の Bean にアクセスします。

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

サンプル: Hello World、BeanPostProcessor スタイル

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

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

  • Java

  • Kotlin

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);
		return bean;
	}
}
package scripting

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

static @Bean メソッドを使用することで、Java 構成に InstantiationTracingBeanPostProcessor を登録できます(構成クラスやその他の Bean の即時初期化を避けるため、この方法を推奨します)。

  • Java

  • Kotlin

@Configuration
public class AppConfig {

	@Bean
	public static InstantiationTracingBeanPostProcessor instantiationTracingBeanPostProcessor() {
		return new InstantiationTracingBeanPostProcessor();
	}

	// ... other bean definitions
}
@Configuration
class AppConfig {

	@Bean
	companion object {
		@JvmStatic
		fun instantiationTracingBeanPostProcessor() = InstantiationTracingBeanPostProcessor()
	}

	// ... other bean definitions
}

あるいは、InstantiationTracingBeanPostProcessor は XML 設定を使用して 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: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

  • Kotlin

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

}
import org.springframework.beans.factory.getBean

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

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

Bean 'messenger' created : org.springframework.scripting.groovy.GroovyMessenger@272961
org.springframework.scripting.groovy.GroovyMessenger@272961

サンプル: AutowiredAnnotationBeanPostProcessor

カスタム BeanPostProcessor 実装と組み合わせてコールバックインターフェースまたはアノテーションを使用することは、Spring IoC コンテナーを継承する一般的な手段です。例として、Spring の AutowiredAnnotationBeanPostProcessor があります。BeanPostProcessor の実装には、Spring ディストリビューションが付属しており、アノテーション付きフィールド、setter メソッド、任意の構成メソッドがオートワイヤーされます。

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 と同様にデプロイできます。

@Configuration クラスで @Bean ファクトリメソッドを介して BeanFactoryPostProcessor を登録する場合、構成クラスでのアノテーション処理 (@Autowired@Value@PostConstruct など) とのライフサイクル競合を避けるため、メソッドを static として宣言してください。詳細と例については、@Bean (Javadoc) の Javadoc の「BeanFactoryPostProcessor から @Bean メソッドを返す」セクションを参照してください。

BeanFactoryPostProcessor 型の戻り値を持つ非静的 @Bean ファクトリメソッドの場合、以下のような INFO レベルのログメッセージが表示されるはずです。

@Bean のメソッド MyConfig.myBfpp は非静的であり、Spring の BeanFactoryPostProcessor インターフェースに割り当て可能なオブジェクトを返します。そのため、メソッドを宣言している @Configuration クラス内の @Autowired、@Resource、@PostConstruct などのアノテーションの処理が失敗します。これらのコンテナーライフサイクルの課題を回避するには、このメソッドに 'static' 修飾子を追加してください。詳細については、@Bean の Javadoc を参照してください。
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" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
	<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 ファイルから設定されたプロパティを示しています。実行時には、PropertySourcesPlaceholderConfigurer がメタデータに適用され、DataSource の一部のプロパティが置き換えられます。置き換えられる値は、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 定義のほとんどのプロパティと属性のプレースホルダをチェックします。さらに、プレースホルダのプレフィックス、サフィックス、デフォルトの値セパレータ、エスケープ文字をカスタマイズできます。さらに、JVM システムプロパティ(または SpringProperties メカニズム)を介して spring.placeholder.escapeCharacter.default プロパティを設定することで、デフォルトのエスケープ文字をグローバルに変更または無効化できます。

context 名前空間では、専用の構成要素を用いてプロパティプレースホルダーを設定できます。location 属性には、以下の例のように、1 つ以上の場所をカンマ区切りのリストとして指定できます。

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

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

必要なプロパティを持つ特定のアプリケーションに対して、そのような要素を 1 つだけ定義する必要があります。個別のプレースホルダー構文 (${…​}) がある限り、いくつかのプロパティプレースホルダーを構成できます。

置換に使用されるプロパティのソースをモジュール化する必要がある場合は、複数のプロパティプレースホルダーを作成しないでください。むしろ、使用するプロパティを集めた独自の PropertySourcesPlaceholderConfigurer Bean を作成する必要があります。

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

このサンプルファイルは、driverClassName および 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"/>

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

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

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

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

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

  • boolean isSingleton(): この FactoryBean がシングルトンを返す場合は true を返し、それ以外の場合は false を返します。このメソッドのデフォルトの実装は true を返します。

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

FactoryBean の概念とインターフェースは、Spring Framework 内のさまざまな場所で使用されています。FactoryBean インターフェースの 50 以上の実装には、Spring 自体が付属しています。

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