Bean の性質のカスタマイズ

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

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

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 インターフェースは単一のメソッドを指定します:

void afterPropertiesSet() throws Exception;

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

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

  • Kotlin

public class ExampleBean {

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

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

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

<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
  • Java

  • Kotlin

public class AnotherExampleBean implements InitializingBean {

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

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

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

@PostConstruct および初期化メソッドは一般に、コンテナーのシングルトン作成ロック内で実行されることに注意してください。Bean インスタンスは、完全に初期化され、@PostConstruct メソッドから戻った後に他のインスタンスに公開できる状態になっているとみなされます。このような個別の初期化メソッドは、構成状態を検証し、場合によっては指定された構成に基づいていくつかのデータ構造を準備することのみを目的としていますが、外部 Bean アクセスによるそれ以上のアクティビティは意図されていません。そうしないと、初期化デッドロックが発生する危険性があります。

高負荷な初期化後のアクティビティがトリガーされるシナリオの場合。非同期データベースの準備手順では、Bean は SmartInitializingSingleton.afterSingletonsInstantiated() を実装するか、コンテキストリフレッシュイベントに依存する必要があります ( ApplicationListener<ContextRefreshedEvent> を実装するか、そのアノテーションと同等の @EventListener(ContextRefreshedEvent.class) を宣言します)。これらのバリアントは、すべての通常のシングルトン初期化の後に発生するため、シングルトン作成ロックの外側にあります。

あるいは、(Smart)Lifecycle インターフェースを実装し、自動起動メカニズム、破棄前の停止ステップ、潜在的な停止 / 再起動コールバックを含むコンテナーの全体的なライフサイクル管理と統合することもできます (以下を参照)。

破棄コールバック

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

void destroy() throws Exception;

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

<bean id="exampleDestructionBean" class="examples.ExampleBean" destroy-method="cleanup"/>
  • Java

  • Kotlin

public class ExampleBean {

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

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

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

<bean id="exampleDestructionBean" class="examples.AnotherExampleBean"/>
  • Java

  • Kotlin

public class AnotherExampleBean implements DisposableBean {

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

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

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

Spring は、パブリック close または shutdown メソッドを検出する、destroy メソッドの推論もサポートしていることに注意してください。これは、Java 構成クラスの @Bean メソッドのデフォルトの動作であり、java.lang.AutoCloseable または java.io.Closeable 実装と自動的に一致し、破棄ロジックを Spring に結合しません。

XML を使用した destroy メソッド推論の場合、<bean> 要素の destroy-method 属性に特別な (inferred) 値を割り当てることができます。これにより、特定の Bean 定義の Bean クラスのパブリック close または shutdown メソッドを自動的に検出するように Spring に指示されます。この特別な (inferred) 値を <beans> 要素の default-destroy-method 属性に設定して、この動作を Bean 定義のセット全体に適用することもできます ( デフォルトの初期化および破棄メソッドを参照)。

延長されたシャットダウンフェーズでは、Lifecycle インターフェースを実装し、シングルトン Bean の破棄メソッドが呼び出される前に早期停止シグナルを受信できます。また、時間制限のある停止ステップ用に SmartLifecycle を実装することもできます。このステップでは、コンテナーはメソッドの破棄に進む前に、そのようなすべての停止処理が完了するのを待ちます。

デフォルトの初期化および破棄メソッド

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

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

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

  • Java

  • Kotlin

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.");
		}
	}
}
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 つのオプションがあります。

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

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

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

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

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

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

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

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

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

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

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

public interface Lifecycle {

	void start();

	void stop();

	boolean isRunning();
}

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

public interface LifecycleProcessor extends Lifecycle {

	void onRefresh();

	void onClose();
}

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

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

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

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

public interface Phased {

	int getPhase();
}

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

public interface SmartLifecycle extends Lifecycle, Phased {

	boolean isAutoStartup();

	void stop(Runnable callback);
}

開始時に、最も低いフェーズのオブジェクトが最初に開始されます。停止するときは、逆の順序に従います。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

  • Kotlin

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

スレッドの安全性と可視性

Spring コアコンテナーは、作成されたシングルトンインスタンスをスレッドセーフな方法で公開し、シングルトンロックを通じてアクセスを保護し、他のスレッドでの可視性を保証します。

結果として、アプリケーションが提供する Bean クラスは、初期化状態の可視性を考慮する必要がありません。通常の構成フィールドは、初期化フェーズ中にのみ変更される限り、volatile としてマークされる必要はありません。初期フェーズ中に変更可能な setter ベースの構成状態であっても、final と同様の可視性が保証されます。このようなフィールドが Bean 作成フェーズおよびその後の最初の公開後に変更された場合、volatile として宣言されるか、アクセスされるたびに共通ロックによって保護される必要があります。

シングルトン Bean インスタンスでのそのような構成状態への同時アクセスには注意してください。コントローラーインスタンスまたはリポジトリインスタンスの場合、コンテナー側からの安全な初期公開の後は完全にスレッドセーフになります。これには、一般的なシングルトンロック内で処理される共通のシングルトン FactoryBean インスタンスも含まれます。

破棄コールバックの場合、構成状態はスレッドセーフのままですが、初期化と破棄の間に蓄積されたランタイム状態は、一般的な Java ガイドラインに従ってスレッドセーフ構造 (または単純な場合は volatile フィールド) に保持される必要があります。

上記のようなより深い Lifecycle 統合には、volatile として宣言する必要がある runnable フィールドなどの実行時変更可能な状態が含まれます。一般的なライフサイクルコールバックは特定の順序に従います。開始コールバックは完全な初期化後にのみ発生し、停止コールバックは初期開始後にのみ発生することが保証されています。共通の破棄前停止の取り決めには特殊なケースがあります。そのような Bean の内部状態では、これは、キャンセルされたブートストラップ後の異常なシャットダウン中、または別の Bean によって引き起こされた停止タイムアウトの場合に発生する可能性があるためです。

ApplicationContextAware および BeanNameAware

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

public interface ApplicationContextAware {

	void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}

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 インターフェースの定義を示しています。

public interface BeanNameAware {

	void setBeanName(String name) throws BeansException;
}

コールバックは、通常の Bean プロパティの設定後、InitializingBean.afterPropertiesSet() やカスタム init メソッドなどの初期化コールバックの前に呼び出されます。

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

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

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

ApplicationContextAware

ApplicationContext の宣言。

ApplicationContextAware および BeanNameAware

ApplicationEventPublisherAware

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

ApplicationContext の追加機能

BeanClassLoaderAware

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

Bean のインスタンス化

BeanFactoryAware

BeanFactory の宣言。

BeanFactory API

BeanNameAware

宣言する Bean の名前。

ApplicationContextAware および BeanNameAware

LoadTimeWeaverAware

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

Spring Framework の AspectJ を使用したロード時ウィービング

MessageSourceAware

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

ApplicationContext の追加機能

NotificationPublisherAware

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

通知

ResourceLoaderAware

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

Resources

ServletConfigAware

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

Spring MVC

ServletContextAware

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

Spring MVC

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