春のIOC
依存性注入
- コンストラクター注入
- セッター注入
- 界面注入
コンストラクター注入
public FXNewsProvider(IFXNewsListener newsListner,IFXNewsPersister newsPersister) {
this.newsListener = newsListner;
this.newPersistener = newsPersister;
}
IoC サービス プロバイダーは、注入されたオブジェクトの構築方法を確認し、必要な依存オブジェクトのリストを取得してから、対応するオブジェクトをそれに注入します。同じオブジェクトを 2 回構築することは不可能であるため、注入されたオブジェクトの構築とそのライフ サイクル全体は、IoC サービス プロバイダーによって管理される必要があります。
セッターメソッドインジェクション
JavaBean オブジェクトの場合、対応するプロパティは通常、setXXX() および getXXX() メソッドを介してアクセスされます。これらの setXXX() メソッドは総称してセッター メソッドと呼ばれ、getXXX() はもちろんゲッター メソッドと呼ばれます。セッター メソッドを介して、対応するオブジェクト プロパティを変更できます。ゲッター メソッドを介して、対応するプロパティの状態を取得できます。したがって、現在のオブジェクトがその依存オブジェクトに対応する属性に setter メソッドを追加する限り、対応する依存オブジェクトは、setter メソッドを介して注入されたオブジェクトに設定できます。
public class FXNewsProvider{
private IFXNewsListener newsListener;
private IFXNewsPersister newPersistener;
public IFXNewsListener getNewsListener() {
return newsListener;
}
public void setNewsListener(IFXNewsListener newsListener) {
this.newsListener = newsListener;
}
public IFXNewsPersister getNewPersistener() {
return newPersistener;
}
public void setNewPersistener(IFXNewsPersister newPersistener) {
this.newPersistener = newPersistener;
}
}
このようにして、外部の世界は、setNewsListener および setNewPersistener メソッドを呼び出すことによって、依存オブジェクトを FXNewsProvider オブジェクトに注入できます。
セッターメソッドインジェクションは、コンストラクションメソッドインジェクションとは異なり、構築が完了した後にオブジェクトを使用できるようになりますが、比較的緩く、オブジェクト構築が完了した後にインジェクトできます。
界面注入
最初の 2 つの注入方法と比較すると、界面注入はそれほど単純でも明確でもありません。注入されたオブジェクトが IoC サービス プロバイダーに依存オブジェクトを注入させたい場合は、インターフェイスを実装する必要があります。このインターフェースは、依存オブジェクトを注入するためのメソッドを提供します。IoC サービス プロバイダーは最終的にこれらのインターフェイスを使用して、注入されたオブジェクトにどの依存オブジェクトを注入する必要があるかを理解します。以下の図は、インターフェース注入を使用して FXNewsProvider の依存オブジェクトを注入する方法を示しています。
IoC サービス プロバイダーが依存する IFXNewsListener を注入するために、FXNewsProvider は最初に IFXNewsListenerCallable インターフェイスを実装する必要があります。このインターフェイスは、injectNewsListner メソッドを宣言します (メソッド名はオプションです)。このメソッドのパラメーターは、依存オブジェクト。このように、InjectionServiceContainer オブジェクト、つまり対応する IoC サービス プロバイダーは、このインターフェイス メソッドを介して、依存オブジェクトを注入オブジェクト FXNewsProvider に注入できます。
3つの注入方法の比較
- インターフェース注入。インジェクションメソッドの利用という意味では、界面インジェクションは現在では提唱されていないメソッドであり、基本的には「リタイアした状態」にあります。注入されたオブジェクトに不要なインターフェイスを実装するよう強制するため、侵入的です。これは、コンストラクター注入とセッター メソッド注入には必要ありません。
- コンストラクター注入。この注入方法の利点は、オブジェクトが構築された後、準備完了状態になり、すぐに使用できることです。欠点は、依存オブジェクトが多い場合、構築メソッドのパラメーター リストが比較的長くなることです。リフレクションを介してオブジェクトを構築する場合、同じ型のパラメーターを処理するのはより難しくなり、保守と使用がより面倒になります。そして Java では、コンストラクターを継承できず、デフォルト値を設定できません。本質的でない依存関係の処理については、複数の構築方法を導入する必要があり、パラメーターの数の変更によりメンテナンスに不便が生じる可能性があります。
- セッターメソッドインジェクション。メソッドには名前を付けることができるため、setter メソッドの注入は、コンストラクター メソッドの注入よりも説明的です。さらに、setter メソッドを継承できるため、デフォルト値を設定でき、IDE のサポートも充実しています。もちろん、欠点は、オブジェクトが作成直後に準備完了状態になれないことです。
IOC の利点
依存関係を能動的に取得する方式からIoC方式への切り替えは、一方向の変更だけではなく、単純な変換の裏には実はもっと謎が潜んでいます。IOC モデルがもたらす利点について言えば、さまざまな資料や書籍に記載されていることがたくさんあります。たとえば、ビジネスオブジェクトにあまり干渉しません.IoCを使用した後、オブジェクトのテスト容易性、再利用性、およびスケーラビリティなどが向上します. ただし、一般的な話をしても、IoC パターンによってもたらされる多くの利点を深く理解することはできないかもしれません。そのため、具体的な例から始めましょう。
IOC プロバイダー
ビジネス オブジェクトは IOC を介して対応する依存関係を宣言できますが、これらの相互に依存するオブジェクトをバインドするにはサービスが必要であり、IoC サービス プロバイダーがこの作業を行います。
Ioc サービス プロバイダーは、IoC シナリオでビジネス オブジェクトをバインドする任意の実装を参照できる抽象的な概念です。コードの一部、関連するクラスのグループ、またはより一般的な IoC フレームワークまたは IoC コンテナーの実装である場合もあります。Spring の Ioc コンテナーは、依存性注入サービスを提供する Ioc サービス プロバイダーです。
IoC サービス プロバイダーの責任
IoC サービス プロバイダーの責任は比較的単純で、ビジネス オブジェクトの構築と管理、およびビジネス オブジェクト間の依存関係バインディングという 2 つの主な責任があります。
- **ビジネス オブジェクトの構築管理:** IoC シナリオでは、ビジネス オブジェクトは、依存するオブジェクトを構築および取得する方法を気にする必要はありませんが、作業のこの部分には常に誰かがそれを行う必要があります。したがって、IoC サービス プロバイダーは、オブジェクト構築ロジックをクライアント オブジェクトから分離して、ロジックのこの部分がビジネス オブジェクトの実装を汚染するのを防ぐ必要があります。
- **ビジネス オブジェクト間の依存関係の結合: **Ioc サービス プロバイダーにとって、この責任は最も難しく、最も重要です。責任のこの部分に問題がある場合、注入する必要があるオブジェクトは、必要な依存オブジェクトを見つけられません。Ioc Service Provider は、以前に構築および管理されたすべてのビジネス オブジェクトと、各ビジネス オブジェクト間の識別可能な依存関係を組み合わせることにより、これらのオブジェクトが依存するオブジェクトを注入およびバインドします。使用状態。
IoC Service Provider がオブジェクト間の依存関係を管理する方法
注入されたオブジェクトは、さまざまな方法で対応する依存関係を注入するように IoC サービス プロバイダーに通知できます。しかし問題は、通知を受け取る IoC サービス プロバイダーが、注入されたオブジェクトの意図を完全に理解し、必要な依存関係をタイムリーかつ効果的な方法で提供できるかどうかです。時には、私たちが当たり前だと思っていることがそうではないかもしれません。注入されたオブジェクトに依存性注入を提供する IoC サービス プロバイダーの場合、注入されたオブジェクトと、管理およびマスターする依存オブジェクトとの間の対応関係を知る必要があります。
IoC サービス プロバイダーには、オブジェクト間の対応を記録する方法が必要です。例えば:
- 注入されたオブジェクトとその依存オブジェクト間の対応関係は、基本的なテキスト ファイルを介して記録できます。
- これらの対応する情報は、非常に記述的な XML ファイル形式で登録できます。
- これらの対応するメッセージは、コードを記述することで登録できます。
では、実際の状況では、さまざまな特定の IoC サービス プロバイダーの実装が「サービス情報」をどのように記録しているのでしょうか。現在人気のある IoC Service Provider 製品は、オブジェクト管理情報を登録するために次の方法を使用していると要約できます。
ダイレクトエンコーディング
現在の IoC コンテナのほとんどは、PicoContainer、Spring、Avalon などの直接エンコーディングをサポートする必要があります。コンテナが起動する前に、プログラムのコーディングにより、注入されたオブジェクトと依存オブジェクトをコンテナに登録し、それらの間の依存注入関係を明確にすることができます。
IoContainer container = ...;
container.register(FXNewsProvider.class,new FXNewsProvider());
container.register(IFXNewsListener.class,new DowJonesNewsListener());
...
FXNewsProvider newsProvider = (FXNewsProvider)container.get(FXNewsProvider.class);
newProvider.getAndPersistNews();
対応するクラスに対応する特定のインスタンスを指定することで、このタイプのオブジェクト インスタンスが必要な場合は、コンテナーに登録されている対応する特定のインスタンスを返してくださいと IoC コンテナーに伝えることができます。
インターフェース注入の場合、擬似コードはもう少し見えるかもしれません。ただし、原則は同じですが、対応するオブジェクトを登録するだけでなく、「注入フラグ インターフェイス」を対応する依存オブジェクトにバインドする必要があります。これにより、コンテナは最終的にどのような対応関係であるかを知ることができます。
直接コーディング形式のインターフェース注入管理に基づく依存性注入
IoContainer container = ...;
container.register(FXNewsProvider.class,new FxNewsProvider);
container.register(IFXNewsListener.class,new DowJonesNewsListener);
...
container.bind(IFXNewsListenerCallabe.class,container.get(IFXNewsListener.class));
...
FXNewsProvider newsProvider = (FXNewsProvider)contanier.get(IFXNewsProvider.class);
newProvider.getAndPersistNews();
bind メソッドにより、「注入されたオブジェクト」が依存するオブジェクトを、コンテナに登録されている IFXNewsListener 型のオブジェクト インスタンスにバインドできます。コンテナは、FXNewsProvider オブジェクト インスタンスを返す前に、バインディング情報に従って、コンテナ内の IFXNewsListener に登録されているオブジェクト インスタンスを「注入されたオブジェクト」FxNewsProvider に注入し、最終的に組み立てられた FXNewsProvider オブジェクトを返します。
したがって、最終的な IoC サービス プロバイダー (つまり、各 IoC フレームワークまたはコンテナーの実装) が、プログラムのコーディングを通じてサービスの "難解な意味" を認識できるようにすることが、依存関係のバインディング関係を管理する最も基本的な方法である必要があります。
構成ファイルの方法
これは、依存性注入管理のより一般的な方法です。一般的なテキスト ファイル、プロパティ ファイル、XML ファイルなどはすべて、依存関係の挿入を管理するためのキャリアになります。ただし、最も一般的な方法は、XML ファイルを使用してオブジェクトの登録とオブジェクト間の依存関係を管理することです。
<bean id="newsProvider" class=" ..FXNewsProvider">
<property name= "newsListener" >
<ref bean="djNewsListener"/>
</property>
<property name="newPersistener">
<ref bean="djNewsPersister"/>
</property>
</bean>
<bean id="djNewsListener" class=" ..impl.DowJonesNewsListener" ></bean>
<bean id="djNewsPersister" class=" ..impl.DowJonesNewsPersister"></bean>
メタデータ メソッド
このメソッドの代表的な実装は Google Guice です。これは、Java 5 アノテーションと Generic に基づいて Bob Lee によって開発された IOC フレームワークのセットです。クラス内のメタデータ情報を直接使用して、オブジェクト間の依存関係をマークできます。Guice フレームワークは、これらのアノテーションによって提供される情報に従ってこれらのオブジェクトを組み立て、クライアント オブジェクトに渡して使用します。
Guice アノテーションを使用して依存関係をマークした後の FXNewsProvider の定義
public class FXNewsProvider {
private IFXNewsListenernewsListener;private IFXNewsPersister newPersistener ;
@Inject
public FXNewsProvider(IFXNewsListener listener,IFXNewsPersister persister) {
this.newsListener= listener;
this.newPersistener = persister ;
}
}
@Inject を通じて、IoC サービス プロバイダーが、コンストラクター インジェクションを通じて FXNewsProvider に依存するオブジェクトをインジェクトする必要があることを示します。残りの依存関係に関する情報については、Guice の対応するモジュールによって提供されます。
FXNewsProvider が使用するモジュールの実装
public class NewsBindingModule extends AbstractModule{
@Override
protected void configure() {
bind (IFXNewsListener.class).to(DowJonesNewsListener.class).in(Scopes.SINGLETON);
bind (工FXNewsPersister.class).to(DowJonesNewsPersister.class).in (Scopes.SINGLETON)
}
モジュールを介して依存関係の注入に関連する情報をさらに指定した後、最終的に注入され、Guice から直接利用できるオブジェクトを直接取得できます。
Injector injector = Guice.createInjector(new NewsBindingModule()) ;
FXNewsProvider newsProvider = injector.getInstance (FXNewsProvider.class)newsProvider.getAndPersistNews ( ) ;
もちろん、最終的な注入関係はコード処理によって決定される必要があり、この観点からすると、アノテーション方式はコーディング方式の特殊なケースと見なすことができます。
Spring の IOC コンテナーの BeanFactory
先ほど、Spring の IoC コンテナーが IoC サービス プロバイダーであると述べましたが、これは IoC という名前の理由の一部に過ぎず、無視できないのは「コンテナー」です。Spring の IoC コンテナーは、IoC をサポートする軽量コンテナーです. 基本的な IoC サポートに加えて、軽量コンテナーとして IoC 以外のサポートも提供します。たとえば、Spring の IoC コンテナーに加えて、Spring は、対応する AOP フレームワーク サポートやエンタープライズ レベルのサービス統合などのサービスも提供します。Spring の IoC コンテナと IoC Service Provider が提供するサービスには一定の共通点があり、両者の関係を図に示します。
Spring は、BeanFactory と ApplicationContext の 2 つのコンテナー タイプを提供します。
- BeanFactory の基本タイプの IOC コンテナは、完全な IOC サービス サポートを提供します。指定しない場合、デフォルトで遅延初期化戦略 (lazy-load) が採用されます。クライアント オブジェクトがコンテナー内の管理対象オブジェクトにアクセスする必要がある場合にのみ、管理対象オブジェクトが初期化され、依存関係が挿入されます。したがって、比較的言えば、コンテナの起動の初期速度は比較的速く、必要なリソースは限られています。リソースが限られており、機能要件がそれほど厳密ではないシナリオでは、BeanFactory がより適切な IOC コンテナーの選択肢です。
- アプリケーション コンテキスト。ApplicationContext は、比較的高度なコンテナ実装である BeanFactory に基づいて構築されています. BeanFactory のすべてのサポートに加えて、ApplicationContext は、イベント発行、国際化情報のサポートなど、他の高度な機能も提供します。 . ApplicationContext によって管理されるオブジェクトは、このタイプのコンテナーが開始された後、デフォルトですべて初期化され、バインドされます。そのため、ApplicationContext は BeanFactory に比べて多くのシステム リソースを必要とすると同時に、起動時にすべての初期化が完了するため、コンテナの起動時間も BeanFactory よりも長くなります。システム リソースが十分にあり、より多くの機能が必要な場合は、ApplicationContext タイプのコンテナーが適しています。
ApplicationContext と Beanfactory の関係
注: ApplicationContext は BeanFactory から間接的に継承するため、BeanFactory の上に構築された IOC コンテナーです。
BeanFactory は自動車工場のようなものです。他の自動車部品メーカーや自社の部品製造部門から自動車部品を調達して、この自動車生産工場に送り込み、最終的には生産ラインの最後から完成車を手に入れるだけで済みます。同様に、アプリケーションが必要とするすべてのビジネス オブジェクトを BeanFactory に渡した後、あとは、最終的に組み立てられた使用可能なオブジェクトを BeanFactory から直接取得するだけです。最終的なビジネス オブジェクトを組み立てる方法については、BeanFactory が自動的に行うため、気にする必要はありません。
したがって、クライアントにとって、BeanFactory を扱うことは実際には非常に簡単です。最も基本的なレベルでは、BeanFactory は、アセンブルされたオブジェクトを取得するためのメソッド インターフェイスを公開する必要があります。
BeanFactory の定義
public interface BeanFactory {
String FACTORY_BEAN_PREFIX = "&";
Object getBean(String name) throws BeansException;
object getBean(String name,Class requiredType) throws BeansException;
/**
*@since 2.5
*/
0bject getBean(String name,Object [] args) throws BeansException;boolean containsBean (string name) ;
boolean issingleton(String name) throws NoSuchBeanDefinitionException;
/**
*@since 2.0.3
*/
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
/**
* @since 2.0.1
*/
boolean isTypeMatch(String name,Class targetType) throws NoSuchBeanDefinitionException;Class getType(String name) throws NoSuchBeanDefinitionException;
string[] getAliases (string name) ;
}
上記コード内のメソッドは基本的にクエリ関連のメソッドで、例えば、オブジェクトを取得するメソッド(getBean)、オブジェクトがコンテナ内に存在するかどうかをクエリするメソッド(containsBean)、Bean またはメソッドの種類など 通常、スタンドアロン アプリケーションの場合、メイン エントリ クラスのみがコンテナの API と直接結合されるためです。