SpringのIOCコンテナアーキテクチャ
Springフレームワークは、BeanFactoryとApplicationContextという2つの主要なインターフェースを通じてIOCコンテナを抽象化しています。ApplicationContextはBeanFactoryを拡張したより高度なインターフェースであり、両者はSpringのIoC(制御の反転)コンテナの核となる要素です。
BeanFactoryの基本機能
BeanFactoryはSpring IoCコンテナの基盤となるインターフェースで、Beanのインスタンス管理と維持を担当します。Bean定義の読み込み、Beanのインスタンス化、ライフサイクル管理、および依存関係の注入といった重要な機能を提供します。
- Beanのロードと登録: Bean定義をXML、アノテーション、Java設定クラスなどの設定ソースから読み取り、コンテナに登録します。
- Beanのインスタンス化: Bean定義に基づいてインスタンスを作成し、プロパティの設定(依存性注入)や初期化コールバックを実行します。
- ライフサイクル管理: Beanの作成、初期化、使用、破棄を管理し、適切なタイミングで特定のライフサイクルコールバックを実行します。
- 依存関係管理: 依存性注入メカニズムを通じてBean間の依存関係を管理し、コンポーネント間の結合を減らします。
ApplicationContextの拡張機能
ApplicationContextは「高度コンテナ」とも呼ばれ、Bean管理に加えて多くの追加機能を提供します。BeanFactoryを拡張し、ListableBeanFactory、HierarchicalBeanFactory、MessageSource、ApplicationEventPublisher、ResourcePatternResolverなどのインターフェースを実装しています。
- イベント伝播: コンテナ内でのイベントの発行と監視をサポートし、モジュール間の疎結合を実現します。
- 国際化サポート: 多言語や地域別リソースの処理を容易にします。
- リソース読み込み: ファイル、クラスパスリソース、URLなど様々な種類のリソースを管理します。
- 自動登録機能: BeanPostProcessorやBeanFactoryPostProcessorを自動的に登録します。
BeanFactoryとApplicationContextの比較
| 特性 | BeanFactory | ApplicationContext |
|---|---|---|
| ロード戦略 | 遅延ロード(getBean()呼び出し時にインスタンス化) | 事前ロード(起動時にシングルトンBeanをインスタンス化) |
| 設定問題発見 | Beanが初めて要求された時点で発現 | 起動時に全Beanを検証 |
| メモリ使用量 | 必要なBeanのみをロードするため少ない | 全シングルトンBeanを事前にロードするため多い |
| 起動時間 | Beanが必要になるまでロードしないため高速全Beanを事前ロードするため遅い |
BeanFactoryPostProcessorの役割と動作
BeanFactoryPostProcessorは、SpringコンテナがBeanをインスタンス化する前に、Bean定義を変更またはカスタマイズできる特殊なBeanです。主な用途には、Beanのプロパティの追加や変更、Bean間の依存関係の変更などがあります。
実行プロセス
- 検出: コンテナに登録された全Bean定義から、BeanFactoryPostProcessorを実装したBeanを特定します。
- インスタンス化: 検出された各BeanFactoryPostProcessorをインスタンス化します。
- ソート: Orderedインターフェースを実装または@Orderアノテーションが付与されているBeanは、指定された順序で呼び出されます。
- 処理の実行: 各インスタンスのpostProcessBeanFactoryメソッドを呼び出し、Bean定義を変更します。
BeanPostProcessorの仕組みと実装例
BeanPostProcessorインターフェースは、Beanの初期化メソッドの前後にカスタムロジックを挿入できるようにします。これにより、ログ記録、セキュリティチェック、パフォーマンス監視などの横断的関心事を処理できます。
基本的な実装手順
- インターフェースの実装: BeanPostProcessorインターフェースを実装し、postProcessBeforeInitializationとpostProcessAfterInitializationメソッドをオーバーライドします。
- コンテナへの登録: 実装クラスをSpringコンテナに登録します。
- 自動適用: コンテナは各Beanの初期化時に自動的にBeanPostProcessorを適用します。
実装例
package com.springioc;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
public class ItemProxyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (Item.class.equals(bean.getClass())) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Item.class);
enhancer.setCallback((MethodInterceptor) (obj, method, args, methodProxy) -> {
if (!method.equals(Item.class.getMethod("display"))) {
return methodProxy.invokeSuper(obj, args);
}
System.out.println("Item#display()実行前");
Object result = methodProxy.invokeSuper(obj, args);
System.out.println("Item#display()実行後");
return result;
});
Item item = (Item) enhancer.create();
item.setTitle(((Item) bean).getTitle());
item.setWriter(((Item) bean).getWriter());
return item;
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
実行結果の例
------ItemProxyBeanPostProcessor#postProcessBeforeInitialization :Item{Item{'title='「非暴力コミュニケーション」', writer='マーシャル・ローゼンブルグ'}}
------ItemProxyBeanPostProcessor#postProcessBeforeInitialization :[Item]のdisplay()メソッドを強化
------Item#afterPropertiesSet :ItemProxyBeanPostProcessor$Item$$EnhancerByCGLIB$$30cc4462{Item{'title='「非暴力コミュニケーション」', writer='マーシャル・ローゼンブルグ'}}
------Item#customInit :ItemProxyBeanPostProcessor$Item$$EnhancerByCGLIB$$30cc4462{Item{'title='「非暴力コミュニケーション」', writer='マーシャル・ローゼンブルグ'}}
------ItemProxyBeanPostProcessor#postProcessAfterInitialization :ItemProxyBeanPostProcessor$Item$$EnhancerByCGLIB$$30cc4462{Item{'title='「非暴力コミュニケーション」', writer='マーシャル・ローゼンブルグ'}}
Item#display()実行前
item#display():Item{title='「非暴力コミュニケーション」', writer='マーシャル・ローゼンブルグ'}
Item#display()実行後
ポイント
- BeanPostProcessorは、Beanのライフサイクルにおける重要なポイントでカスタムロジックを挿入できます。
- 複数のBeanPostProcessorを定義する場合、依存関係と初期化順序に注意が必要です。
- BeanPostProcessor自身もBeanとして扱われるため、他のBeanPostProcessorの影響を受けます。