Google
 
Web andore.com
Powered by SmartDoc

3. ビーンとApplicationContext (Ver 2.0M2)

3.1 はじめに

Springの中で最も基本となる重要な2つのパッケージはorg.springframework.beansパッケージとorg.springframework.contextパッケージだ。このパッケージに含まれているコードではSpringのInversion of Control(Dependency Injectionと呼ぶ人もいる)機能が提供されている。これに含まれているBeanFactoryにより先進的なコンフィグレーションメカニズムが提供されており、Dependency Injectionと最近このパターンに名付け出した人々もいる(これに関する情報については、2.1章「Inversion of Control / Dependency Injection」を参照して欲しい)。このBeanFactoryではいろんな種類のストレージファシリティを潜在的に利用してどんなビーンでも運用が可能な先進的なコンフィグレーションメカニズムが用意されている。このApplicationContextBeanFactory(これはサブクラスである)の上位に構築され、SpringのAOP機能や(i18nを用いた)メッセージング、他のコンテキストから継承したコンテキストを持たせたり、WebApplicationContextのようなアプリケーションレイヤ独自のコンテキストを定義することが可能だ。

要するに、このBeanFactoryでは設定を行うためのフレームワークと基本的な機能性を提供していて、例えApplicationcontextに機能が拡張されていても,そのいくつかはおそらくよりJ2EEやエンタープライズ向けのものだ。一般的に、ApplicationContextは完全BeanFactoryのスーパーセットであり、BeanFactoryでできることや振る舞いに関する記述はどれもApplicationContextにも当てはまるとみなすべきである。

ユーザはその特定の状況でBeanFactoryApplicationContextのどちらを使うのが適しているのかわからないことがある。通常、J2EE環境でアプリケーションを構築する場合、ApplicationContextを用いるのがベストな選択肢だ。というのは、ApplicationContextではBeanFactoryの全部の機能を使うことができるし、さらに機能が追加されているからだ。他にもいくつかの機能を用いる上で一般的に望ましいより宣言的なアプローチが使えるからだ。BeanFactoryを使ったほうが好ましい使用例というのは、(残り1キロバイトが重要なアプレットのような)メモリの使い方がシビアな場合や、ApplicationContextの全ての機能を必要としない場合だ。

本チャプターではBeanFactoryApplicationContextの両方に関係する事柄を扱う。BeanFactoryのみに言及している場合には、その文章はApplicationContextにも当てはまるとみなしていただいて構わない。ApplicationContextでのみ利用可能な機能の場合はそのことを明示してある。

3.2 BeanFactoryとBeanDefinitions -基本編

3.2.1 BeanFactory

BeanFactoryは数多くのビーンを初期化して、設定して管理を行うコンテナだ。これらビーンはお互いに協調して動作し、お互いに依存関係を持っている。この依存性BeanFactoryによって利用される設定データに反映されている(でも、一部には設定データとして表現されず、ランタイム時にビーン間の相互作用としてプログラムで記述される機能もある)。

BeanFactoryorg.springframework.beans.factory.BeanFactoryインタフェースによって表され、その実装は多数用意されている。最も一般的に用いられる単純なBeanFactoryの実装は、org.springframework.beans.factory.xml.XmlBeanFactoryだ。(これは、ApplicationContextBeanFactoryのサブクラスであり、ほとんどのユーザは結局ApplicationContextのXML派生を使っているということだ)。

ほとんどのシナリオについて、BeanFactoryで管理されたほとんど全てのユーザコードはBeanFactoryを意識してはいけないが、どうにかしてBeanFactoryを初期化しなければならない。これは下記のようにしてユーザコードから明示的に行うことができる。

Resource res = new FileSystemResource("beans.xml");
XmlBeanFactory factory = new XmlBeanFactory(res);

あるいは、

ClassPathResource res = new ClassPathResource("beans.xml");
XmlBeanFactory factory = new XmlBeanFactory(res);

あるいは、

ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext(
        new String[] {"applicationContext.xml", "applicationContext-part2.xml"});
// of course, an ApplicationContext is just a BeanFactory
BeanFactory factory = (BeanFactory) appContext;

注:ひとたび本章からビーンファクトリやアプリケーションコンテキストの基本を学んだら、4章の低レイヤへの抽象アクセスに記載されているSpringのリソース抽象化を調べるのも役に立つだろう。ロケーションパスやApplicationContextのコンストラクタに渡されるパスは実際にはリソース文字列が簡単な形式になっており、特定のコンテキストの実装に適切に割当られる(例えば、ClassPathXmlApplicationContextは単純なロケーションパスをクラスパスロケーションとして扱う)。しかし、実際のコンテキスト型がどうであれ、クラスパスやURLから強制的に定義をロードするための特別なプレフィックスを使うことも可能である。これとは別の特別なプレフィクスである、classpath*:は、コンテキストを構築するのに、見つけて組み合わせるためのクラスパス上で同じ名前のコンテキスト定義ファイルをすべて使うことができる。より詳細な情報については、リソースに関する章を参照してほしい。

多くの利用例では、ユーザコードでBeanFactoryApplicationContextを初期化する必要はない。というのはSpring Frameworkでこれを行っているからである。例えば、ウェブ層では、J2EEのWebアプリケーションの通常の起動プロセスの一部としてSpringのApplicationContextを自動的にロードするためのコードが提供されている。(3.19 "ウェブアプリケーションからApplicationContextを生成する"を参照のこと)。

BeanFactoryをプログラム的に操作することに関しては後述する。次のセクションではBeanFactoryの設定について焦点を当てる。

BeanFactoryの設定は、最も基礎的なレベルには、BeanFactoryが管理しなければならないビーンの定義により構成される。XmlBeanFactoryでは、トップレベルのbeans要素の中に複数のbean要素が構成されている。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" \
    "http://www.springframework.org/dtd/spring-beans.dtd">

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

  ...

</beans>

3.2.2 The BeanDefinition

(XmlBeanFactoryのような)DefaultListableBeanFactoryの派生の内部にあるビーン定義はBeanDefinitionオブジェクトとして表現される。これには、(他の情報と合わせて)下記のような詳細が含まれている。

上記にリストした概念は直接ビーン定義を構成する要素の組み合わせに変換される。これらの要素のうちのいくつかは、各々に関連するドキュメントへのリンクといっしょに下記に挙げておく。

機能 詳細情報
クラス 節[3.2.3 ビーンクラス]
idや名前 節[3.2.4 ビーン識別子(IDと名前)]
シングルトンもしくはプロトタイプ 節[3.2.5 シングルトンにするかそれともしないか]
コンストラクタ引数 節[3.3.1 ビーンプロパティの設定とコラボレータ]
ビーンプロパティ 節[3.3.1 ビーンプロパティの設定とコラボレータ]
autowireモード 節[3.3.6 コラボレータをオートワイヤリングする]
依存性チェックモード 節[3.3.7 依存関係をチェックする]
初期化メソッド 節[3.4.1 ライフサイクルインタフェース]
デストラクタメソッド 節[3.4.1 ライフサイクルインタフェース]

ビーン定義は内部的にはorg.springframework.beans.factory.config.BeanDefinitionインタフェースや、そのさまざまな実装(Root/ChildBeanDefinition)で表されていている。しかしながら、ほとんどのユーザコードがBeanDefinitionと一緒に動くということはほとんどない。ユーザコードがBeanDefinitionオブジェクトと一緒に直接動作することはほとんどない。通常、ビーン定義は(XMLのような)起動時に読み込まれるメタデータフォーマットで表現される。このようなビーン定義は、内部ではファクトリ内にあるBeanDefinitionオブジェクトで表現される。

特定のビーンの生成法に関する情報が含まれているビーン定義とあわせて、BanFactoryの実装にファクトリの外部で(独自コードにより)生成された既存のオブジェクトの登録が可能だ。BeanFactoryには、生成法についての情報がわかっている特定のビーン定義だけでなく、ファクトリの外部で(独自のコードにより)生成される既存のビーンオブジェクトも登録することができる。DefaultListableBeanFactoryではこの機能を、org.springframework.beans.factory.config.ConfigurableBeanFactoryインタフェースで定義されているように、registerSingletonメソッドによってサポートしている。でも、典型的なアプリケーションでは純粋にメタデータビーン定義によるビーン定義を使って行っている。

3.2.3 ビーンクラス

クラス属性は通常、マンダトリ(セクション3.2.3.3「インスタンスファクトリメソッドによるビーンの生成」と、セクション3.5「アブストラクトビーン定義、子ビーン定義」を参照のこと)であり、2つの目的のうちの1つのために利用される。BeanFactory自身が直接コンストラクタを呼ぶことによりビーンを生成する(Javaコードがnewを呼ぶのと同じ)という最も一般的なケースでは、クラス属性は生成されたビーンのクラスが指定されている。これほどは一般的ではないが、BeanFactoryが静的な、ファクトリメソッドと呼ばれるそのクラスに定義されているメソッドを呼んでビーンを生成する場合は、クラス属性には、その静的なファクトリメソッドを含んでいるクラスが指定される(その静的ファクトリメソッドから返されるビーンの型は同じクラスかもしれないし別のクラスかもしれないが、それは重要ではない)。

3.2.3.1 コンストラクタによるビーン生成

コンストラクタによる方法を用いてビーンを生成する場合、通常のクラスは全てSpringで利用可能であり、互換性がある。すなわち、生成されたクラスは特定のインタフェースが実装されていたり、特定の方法でコーディングされている必要はないのである。ビーンクラスを単に指定するだけで十分なのだ。しかしながら、その特定のビーンに利用するいづれかのIoCに依存するのであれば、デフォルト(空の)コンストラクタが必要になるかもしれない。

さらに、そのBeanFactoryはJavaBeansだけを管理するだけではなく、管理させたい全てのクラスの管理を行うことが可能なのだ。Spring を使っているほとんどの人は、実際には(デフォルト(引数を持たない)コンストラクタとプロパティに基づいた適切なセッターやゲッターをもつ)JavaBeansをBeanFactoryで保持することを好むが、それだけでなくもっと斬新な非ビーンスタイルのクラスをBeanFactoryに保持してもかまわないのである。もし、例えば、JavaBean仕様に完全には準拠しないレガシーなコネクションプールを使用する必要があっても、Springでは同じように管理することができるのだ。

XmlBeanFactoryを使えば、ビーンクラスは以下のように指定することができる。

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

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

コンストラクタに(オプション)引数を供給するメカニズム、あるいはオブジェクトインスタンスが生成された後でプロパティが設定されるメカニズムについて簡単に述べる。

3.2.3.2 静的ファクトリメソッドによるビーンの生成

静的なファクトリメソッドを使って生成されるビーンを定義する場合、その性的なファクトリメソッドを含んだクラスを指定するクラス属性に加えて、他のfactory-methodという名前の属性がファクトリメソッドを指定するのに必要とされる。Springではこのメソッド(と後述するオプションの引数リスト)が呼び出し可能であり、実際のオブジェクトを戻すことができることが期待されている。このオブジェクトはその時点から、あたかも普通にコンストラクタかあ生成されたかのように扱うことができる。そのようなビーン定義のための1つの方法は静的なファクトリをレガシーコードの中で呼ぶことだ。

<bean id="exampleBean"
      class="examples.ExampleBean2"
      factory-method="createInstance"/>

ファクトリメソッドに(オプションの)引数を供給する、もしくはファクトリから返された後でそのオブジェクトにプロパティを設定するためのメカニズムについては後述する。

3.2.3.3 インスタンスのファクトリメソッドによるビーン生成

インスタンスの(静的でない)ファクトリメソッドを使うのは、静的なファクトリメソッドを使ってビーンを生成するのと全く同じで、そのファクトリから既存のビーンのファクトリメソッドが新しいビーンを生成するために呼ばれる。

このメカニズムを使うために、そのクラスの属性が空でないといけない。また、factory-bean属性にカレントの、あるいはそのファクトリメソッドをもっている祖先のビーンファクトリのビーン名が指定されていなければならない。このファクトリメソッド自身は(下記の例にあるように)factory-method属性により設定される。

<!-- The factory bean, which contains a method called
     createInstance -->
<bean id="myFactoryBean" class="...">
  ...
</bean>
<!-- The bean to be created via the factory bean -->
<bean id="exampleBean"
      factory-bean="myFactoryBean"
      factory-method="createInstance"/>

3.2.4 ビーン識別子(IDと名前)

ビーンにはどれにでも1つあるいは複数ID(identifierとかnameとか呼ぶこともあるが全部同じもののことだ)がある。このidはそのビーンがホスティングされているBeanFactoryあるいはApplicationContext毎に一意でないといけない。あるビーンはIDを1つしか持っていないが、2つ以上のIDを持っている場合、余分は本質的にはエイリアスとみなす。

XmlBeanFactory(ApplicationContextの派生も含めて)では、idname属性をビーンIDを指定するのに使う。このid属性によってIDを1つ指定することができて、XML DTD(定義文書)で実際のXML要素のID属性が指定されているように、他の要素からこの要素に指し戻る場合、パーザは特別な検証を行うことができる。よって、これはビーンIDを指定するのに望ましい方法だ。しかしながら、XML仕様ではXML IDで有効な文字が制限されている。実際問題としては通常は制約にならないが、制限されている文字を使いたい、あるいはビーンに他のエイリアスを導入したいという必要が生じた場合(name属性からコンマかセミコロン、空白で区切られた)1つ以上のビーンIDを代わりに指定してもよい。

ビーン名をつけることは必須ではない、という点に注意してほしい。名前が明示的につけられてない場合、コンテナは前述のビーンに(ユニーク)な名前を生成する。ビーンに名前をつけない理由については、( 豆の名前を供給することは要求されないことに注意してください。名前が明示的に供給されない場合、コンテナーは前述の豆の(ユニーク)名前を生成するでしょう。豆の名前を供給しない動機は、その後(1つの使用ケースは内部の豆です)議論されるでしょう。

3.2.5 シングルトンにするかそれともしないか

ビーンはシングルトンか非シングルトンか2つのうちのどちらかのモードで配備されるように定義される。(後者は、プロトタイプとも呼ばれている。この用語はどうもしっくりこないので厳密に使われるわけではないが)。あるビーンがシングルトンの場合、そのビーンの共有インスタンスが1つだけ管理され、このビーンに対するビーン定義にマッチする1つあるいは複数のIDつきの全てのリクエストはその1つの特定のビーンインスタンスに基づいて値が返されることになる。

非シングルトン、プロトタイプモードのビーン配備は新しいビーンインスタンスそのビーンに対してリクエストされる毎に生成される。これは例えば各ユーザが独立したユーザオブジェクトやそれに似たものを必要とする場合は理想的だ。

ビーンは非シングルトンを指定しない限りデフォルトではシングルトンモードで配備される。非シングルトン(プロトタイプ)へタイプを変更することによってビーンに対してリクエスト毎に新しくビーンを生成するようになり、望んでいるものではなくなるかもしれないことは心に留めておいて欲しい。従ってプロトタイプへモードを変更するのは確実に必要であるときだけだ。

下記の例では、2つのビーンが、一方ははシングルトンとして定義され、他方は非シングルトン(プロトタイプ)であると宣言されている。exampleBeanは毎回クライアントがこのビーンのBeanFactoryにリクエストするたびに生成され、ここで、yetAnotherExampleは1回しか生成されない。つまり厳密に同じインスタンスへのリファレンスがこのビーンへのリクエストの度に返される。

<bean id="exampleBean"
      class="examples.ExampleBean" singleton="false"/>
<bean name="yetAnotherExample"
      class="examples.ExampleBeanTwo" singleton="true"/>

注:プロトタイプモードでビーンをデプロイする場合、ビーンのライフサイクルは若干変更される。定義によって、Springは非シングルトン/プロトタイプビーンのライフサイクルを完全に管理することはできない。生成された後、クライアントに渡され、コンテナはその跡を追いかけるようなことはしないからだ。非シングルトン/プロトタイプビーンを「new」オペレータの置き換えとして話をするとSpringの役割を考えることができる。そのポイントを経過したライフサイクルの様相はクライアントで処理されなければならないのだ。BeanFactoryにあるビーンのライフサイクルについては、セクション3.4.1 「ライフサイクルインタフェース」にて詳細に触れる。

3.3 プロパティ、コラボレータ、オートワイヤ、依存関係チェック

3.3.1 ビーンプロパティの設定とコラボレータ

Inversion of ControlはもうDependency Injectionと呼ばれている。この基本原理は、ビーンはその依存性(つまり、いっしょに動作する他のオブジェクト)をコンストラクタ引数か、ファクトリメソッドへの引数、あるいは生成後のオブジェクトインスタンスあるいはファクトリメソッドから返されたオブジェクトインスタンスに設定されるプロパティでしか定義しないということだ。従って、ビーンを生成するときにこの依存性を実際に注入するのはコンテナの仕事なのだ。これは基本的に、直接クラスのコンストラクタあるいは何かサービスロケータのようなものを使ってビーンのインスタンス化あるいは依存性の配置を行うことの正反対(これがInversion of Controlという名前の下になっている)なのだ。我々はDependency Injectionの利点についてあまり詳しく述べるつもりはないので、ビーンが依存性を見なければコードがより見通しよくなり、疎結合性を高めることもとても簡単になる、ということは使っている中で明らかになる。しかし、依存性が提供され、加えて、その依存性がどこにあってそれは実際にはどんなタイプなのかは分からない。

前のパラグラフで触れたように、Inversion of Control/Dependency Injectionには主要な派生が2つある。

BeanFactoryではこのどちらの派生でも管理しているビーンに注入するのをサポートしている。(実際には、コンストラクタ方式で依存性が適用された後でsetterベースの依存性の注入もサポートしている。)依存性に関する設定はBeanDefinitionの形をしており、これはプロパティをあるフォーマットから別のフォーマットへ変換する方法を知るためにJavaBeansのPropertyEditorといっしょに利用される。実際に渡される値は最終的にPropertyValueオブジェクトの形式になる。しかしながら、Springのユーザのほとんどはこのクラスを直接は(つまり、プログラム的には)使わない。それよりも内部でこれらのクラスのインスタンスに変換されるXML定義ファイルを用意して完全なBeanFactoryApplicationContextをロードするのに使う。

ビーンの依存性解決は通常以下のように行われる。

  1. BeanFactoryはビーンについて全てを記述した設定を使って生成され初期化される。Springユーザのほとんどは、XMLフォーマットの設定ファイルをサポートしているBeanFactoryかあるいはApplicationContextの派生を使っている。
  2. 各ビーンには依存性はプロパティ、コンストラクタ引数、あるいは普通のコンストラクタの代わりに利用される場合は静的ファクトリメソッドの引数の形式で表現されている。この依存性はビーンが実際に生成されたときにビーンに設定される。
  3. プロパティやコンストラクタ引数は各々、実際に設定される値の定義かあるいはBeanFactoryにある他のビーンへのリファレンスだ。ApplicationContextの場合は、このリファレンスが親のApplicationContextにあるビーンへのリファレンスの場合もある。
  4. プロパティやコンストラクタ引数の各々は、どんなフォーマットが指定されていてもプロパティやコンストラクタ引数の実際の型へ変換できないといけない。デフォルトでは、Springは文字列形式で与えられた値をint,Long,String,booleanなどのようなプリミティブな型に変換ができる。加えて、XMLベースのBeanFactoryの派生(ApplicationContextの派生も含む)では、ListやMap,Set、それにPropertyコレクション型の定義がサポートされている。さらに、Springではstring型の値を他の任意の型に変換できるようにJavaBeans PropertyEditor定義を使う。(独自のカスタム型へ変換できるように独自のPropertyEditor定義を使ったBeanFactoryを用意することもできる。PropertyEditorに関するもっと詳しい情報や自分で独自に何か追加する方法はセクション3.9の「カスタムエディタを追加して登録する」にある)。ビーンプロパティがJavaのクラス型であれば、Springではそのプロパティにクラス名を表す文字列を指定することができる。またClassEditor PropertyEditorが用意されているのでこのクラス名を実際のクラスインスタンスへ変換するのに使うことができる。
  5. BeanFactoryが生成される際に、BeanFactory中の各ビーンの設定をSpringが検証するのを有効にすることは重要だ。これには、ビーンリファレンスが実際に正しいビーンを参照するプロパティであるかどうか(つまり、参照されているビーンもBeanFactoryで定義されているか、もしくはApplicationContextの場合は親コンテキストで定義されているか)の検証も含んでいる。しかしながら、このビーンプロパティ自体はビーンが実際に生成されるまではセットされない。(ApplicationContextのシングルトンビーンのように)シングルトンで前もってインスタンス化されるように設定されている場合、ビーンの生成はBeanFactory生成時に行われるが、そうでなければ、ビーンがリクエストを受けたときにしか行われない。あるビーンが実際に生成されるべきときに、このビーンの依存性とこの依存性の依存性(以下略)が生成され、値が代入されるため、生成されるべき他のビーンへのグラフが潜在的に生成されることになるのだ。
  6. たいていはSpringは正しい動作をすると信じることはできるが、ここでは存在しないビーンへの参照や依存性の循環も含めたBeanFactoryロード時のコンフィグ問題を取り上げよう。実際にはプロパティの設定や依存関係の解決(つまり、必要であれば依存関係を生成すること)は可能な限り遅らせられ、ビーンが実際に生成された時だ。これはつまり、正しくロードされたBeanFactoryがその後ビーンを要求した時にもし、そのビーンかあるいは依存関係の1つを生成するのに問題が発生した場合に例外を投げることができるということだ。これは、例えばプロパティが足りないとか間違っていた場合の結果としてこのビーンが例外をthrowすれば起り得る。こうして設定上の問題が発生した際に知らせるのをこの潜在的に遅らせるのは、ApplicationContextがデフォルトでは前もってインスタンス化されるシングルトンビーンだからだ。実際に必要になる前にビーンを生成しておくためのある程度の時間メモリの先行投資を行うことによって設定ミスをApplicationContextが生成された際にすぐさま見つけられるようにしている。必要であればこのデフォルト動作をオーバライドしてシングルトンビーンのいくつかを(あらかじめ生成しておくのではなく)後からロードするようにすることも可能だ。

例:

最初は、setterベースインジェクションのBeanFactoryを使った例だ。下記はビーン定義をいくつか指定しているXMLBeanFactory設定ファイルのほんの一部だ。その次は適切なsetterが宣言してある実際のメインビーンのコードである。

<bean id="exampleBean" class="examples.ExampleBean">
  <property name="beanOne"><ref bean="anotherExampleBean"/></property>
  <property name="beanTwo"><ref bean="yetAnotherBean"/></property>
  <property name="integerProperty"><value>1</value></property>
</bean>

<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
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;
    }    
}

これで分かるように、setterがXMLファイルで指定されているプロパティに一致するように宣言されている。(このXMLファイルからのプロパティは直接RootBeanDefinitionからPropertyValuesオブジェクトへの関連を持っている)

これはIoCタイプ3(コンストラクタベースのDependency Injection)を使ったBeanFactoryの例だ。下記は、コンストラクタ引数を指定するXML設定の断片とそのコンストラクタの実際のビーンのコードである。

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

<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
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;
    }
}

これで分かるように、ビーン定義で指定されているコンストラクタ引数はExampleBeanのコンストラクタに引数として渡されるのに使われている。

それでは、コンストラクタを使う代わりに、Springがオブジェクトのインスタンスを返す静的なファクトリメソッドを呼ぶように指示された一例について見てみることにしよう。

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

<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
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;
    }
}

静的なファクトリメソッドへの引数はcontructor-arg要素で提供され、確かにコンストラクタが実際に使われるのと同じであるという点に注意してほしい。この引数はオプションだ。さらに、ファクトリメソッドから返されているクラスの型は静的ファクトリメソッドを含んでいるクラスと同じ型である必要はないという点を認識することは重要だ。この例では同じではあるが。前述した、インスタンスの(静的でない)ファクトリメソッドは(クラス属性の代わりにfactory-bean属性で利用されていることから)本質的に同じ方法なので、詳細は割愛する。

3.3.2 コンストラクタ引数の解決

コンストラクタ引数解決(訳注:Constructor argument resolution)は引数の型を使ってマッチングをとる。他のビーンが参照されていれば、型がわかるのでマッチングをとることができる。<value>true</value>のような単純な型が使われていれば、Springでは値の型を決定できることができず、そのままでは型によるマッチングをとることができない。下記のクラスで考えてみよう。このクラスは、この後の2つのセクションでも利用する。

package examples;

public class ExampleBean {

    private int years;             //No. of years to the calculate the Ultimate Answer
    private String ultimateAnswer; //The Answer to Life, the Universe, and Everything

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

3.3.2.1 コンストラクタ引数の型マッチング

上述したシナリオは、コンストラクタ引数のtype属性を使って明示的に指定することにより型のマッチングを行うことができる。下記はその例である。

<bean id="exampleBean" class="examples.ExampleBean">
     <constructor-arg type="int"><value>7500000</value></constructor-arg>
<constructor-arg \
    type="java.lang.String"><value>42</value></constructor-arg>
</bean> 

3.3.2.2 コンストラクタ引数のインデックス

コンストラクタ引数には、index属性を使って明示的に指定されたインデックスを持たせることができる。下記はその例である。

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

単純な値が複数ある場合のあいまい性の問題を解決するのと同様、インデックスを指定するのもコンストラクタに同じ型の引数が2つある場合のあいまい性の問題を解決する。このインデックスは0から始まる点については注意してほしい。

コンストラクタ引数インデックスを指定するのは、コンストラクタIoCを使うのに好ましい方法である。

3.3.3 ビーンプロパティとコンストラクタ引数の詳細

前の章で述べたように、ビーンプロパティやコンストラクタ引数を、他の管理されたビーン(コラボレータ)へのリファレンスやインラインで定義された値で定義することができる。XmlBeanFactoryはこの用途のためにpropertyconstructor-arg要素をもつ多くのsub-element型をサポートする。

3.3.3.1 value要素

value要素は人が判読可能な文字列表現としてプロパティやコンストラクタ引数を指定する。先に詳細に触れたように、JavaBeansのPropertyEditorsはこの文字列の値をjava.lang.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</value>
  </property>
  <property name="url">
    <value>jdbc:mysql://localhost:3306/mydb</value>
  </property>
  <property name="username">
    <value>root</value>
  </property>
</bean>

3.3.3.2 null要素

ヌル要素は、null値を扱うのに用いる。Springではプロパティや同種ものに空の引数を空文字列として扱う。以下はXmlBeanFactoryの設定だ。

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

この結果、emailのプロパティにはexampleBean.setEmail("")というjavaのコードと同じく、""が設定される。この特別な<null>要素はnull値をあらわすのに用いられる。したがって、

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

は、exampleBean.setEmail(null)というjavaのコードと同値である。

3.3.3.3 collection要素

listsetmappropsの要素は、定義や値の設定にListSetMapPropertiesというJavaの型をもつプロパティや引数を指定してもよい。

<bean id="moreComplexObject" class="example.ComplexObject">
  <!-- results in a setPeople(java.util.Properties) call -->
  <property name="people">
    <props>
      <prop key="HarryPotter">The magic property</prop>
      <prop key="JerrySeinfeld">The funny property</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><value>yup an entry</value></key>
        <value>just some string</value>
      </entry>
      <entry>
        <key><value>yup a ref</value></key>
        <ref bean="myDataSource"/>
      </entry>
    </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>

mapのキー値、value値、もしくはset値のいづれも任意の要素になりうる点に注意してほしい:

(bean | ref | idref | list | set | map | props | value | null)

3.3.3.4 入れ子bean要素を使った内部ビーン定義

property要素内部のbean要素はBeanFactoryのどこかで定義されたビーンを参照する代わりにビーンの値をインラインに定義するのに用いる。このインラインビーン定義には定義済みのidを指定しなくてもよい。

<bean id="outer" class="...">
  <!-- Instead of using a reference to target, just use an inner bean -->
  <property name="target">
    <bean class="com.mycompany.PersonImpl">
      <property name="name"><value>Tony</value></property>
      <property name="age"><value>51</value></property>
    </bean>
  </property>
</bean>

シングルトンフラグや任意のid属性はちゃんと無視されることに注目してほしい。内部ビーンは匿名のプロトタイプだ。

3.3.3.5 idref要素

idref要素は単に プロパティに文字列idかコンテナ中の他のビーン名を指定するための略記、あるいはエラー防止法だ。

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

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

これは、実行時には、以下のコード片と厳密に等価だ。

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

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

第一の形式が2つめの形式に比べて優れている主な理由は、idrefタグを使っていることで、Springがデプロイ時に、他のビーンが実際に存在していることを検証できるからだ。2つ目の方では、targetNameというプロパティが強制的に検証されるのと、それがおそらくコンテナが実際にデプロイされてからずっと後にクラスがSpringにより実際にインスタンス化された時にしか検証されないのだ。

さらに、もしその参照されているビーンが実際に同じXMLファイルにあって、そのビーン名がビーンIDであれば、ローカル属性が使われ、XMLパーザによってそのビーン名がXMLドキュメントが解析される時点で検証できるようになる。

    <property name="targetName">
        <idref local="theTargetBean"/>
    </property>

3.3.3.6 ref要素

このref要素はproperty定義要素中で許される最後の要素だ。これは、指定されたプロパティの値に、コンテナで管理されているほかのビーン、つまりコラボレータへの参照を指定するのに利用する。先のセクションで述べたように、referred-toで指定されたビーンはプロパティが設定されたビーンの依存関係であると見なされ、必要に応じてオンデマンドでプロパティが設定されるよりも前に初期化される(もし、シングルトンビーンであれば、コンテナによりすでに初期化されているはずだ)。全てのリファレンスは究極的には、他のオブジェクトへの参照であるが、他のオブジェクトのID,あるいは名前を指定する方法には3通りあり、そのどれかはどのようにスコープや検証が扱われるかによって決まる。

refタグのビーン属性を使ってターゲットビーンを指定するのは最も一般的な形式であり、これにより同じBeanFactory/ApplicationContextあるいは、親のBeanFactory/ApplicationContextのビーンへの参照(同じXMLファイル中にあろうとなかろうと)を生成できるようになる。そのbean属性の値はターゲットビーンのid属性かあるいはターゲットビーンのname属性の値の中のどれかと同じになる。

    <ref bean="someBean"/>

local属性を用いてターゲットのビーンを指定することは、XMLパーザが同じファイルにあるXMLのid参照の検証が可能になるのを利用している。local属性の値はターゲットビーンのid属性と同じでなければならないのだ。同じファイルの中にマッチする要素が見つからなければXMLパーザはエラーを検出する。なので、ターゲットビーンが同じXMLファイル中にあんるのであれば、(できるだけ早くエラーを知るために)localを使う方法が一番いい方法なのだ。

    <ref local="someBean"/>

parent属性を使ってターゲットビーンを指定すると、カレントのBeanFactory(もしくはApplicationContext)の親のBeanFactory(もしくはApplicationContext)にあるビーンへの参照が生成できるようになる。このparent属性の値は、ターゲットビーンのid属性かあるいはターゲットビーンのname属性のうちの1つと同じものになり、そのターゲットビーンは親のBeanFactoryApplicationContext中になければならない。このビーンリファレンス方式の主な用途は、親コンテキストにある既存のビーンをある種のプロキシ(これは親と同じ名前を持っている)でラップする必要がある場合や、ラップされているためにオリジナルのオブジェクトを必要とする場合だ。

    <ref parent="someBean"/>

3.3.3.7 短縮形のValueとRef

それは値あるいは豆参照を形成する必要があるのに非常に一般的です、全価格および審判要素を使用するほど冗長でないいくつかの近道形式が存在します。特性、建設者-argおよびエントリー要素はすべて、全価格要素を埋め込む代わりに使用されるかもしれない値属性を支援します。したがって下記: ある値やビーンリファレンスを設定する必要が生じることはよくあることであり、そのためにvalueref要素を全部書かなくてもいい短縮形が用意されている。propertyconstructor-argおよびentry要素はすべてvalue属性をサポートしており、すべて書かないといけないvalue要素の代わりに利用することができる。従って下記:

<property name="myProperty">
  <value>hello</value>
</property

<constructor-arg>
  <value>hello</value>
</constructor-arg>

<entry key="myKey">
  <value>hello</value>
</entry>

は、以下と等価である。

<property name="myProperty" value="hello"/>

<constructor-arg value="hello"/>

<entry key="myKey" value="hello"/>

通常、手で定義をタイプする場合、より冗長性の少ない省略形の方を使いたいと思うだろう。

このプロパティやconstructor-arg要素ではフルにネストされたref要素の代わりに、同様の省略形のref属性がサポートされている。したがって、下記:

<property name="myProperty">
    <ref bean="myBean">
</property

<constructor-arg>
    <ref bean="myBean">
</constructor-arg>

は、以下と等価である。

<property name="myProperty" ref="myBean"/>

<constructor-arg value="myBean"/>

しかしながらこの短縮形が<ref bean="xxx">と等価であっても、<ref local="xxx">の短縮形は存在しない、という点に注意して欲しい。ローカルのrefの検証を強化するために、長い形式を使う必要がある。

最後に、このentry要素ではmapのキーや値を指定するのに、key/key-refref/value-ref属性の形式で、短縮形が許されている。従って下記:

<entry>
  <key><ref bean="myKeyBean"/></key>
  <ref bean="myValueBean"/>
</entry>

は、下記と等価である。

<entry key-ref="myKeyBean" value-ref="myValueBean"/>

再度、この短縮形は、<ref bean="xxx">要素と等価であり、<ref local="xxx">の短縮形は存在しない。

3.3.3.8 複合プロパティ名

ビーンプロパティを設定する際に、最終的なプロパティ名がNULLでない場合を除いてすべてのコンポーネントのパスと同様、入れ子のプロパティ名やその組み合わせはすべて有効であることに注意してほしい。例えば、下記のビーン定義において、

<bean id="foo" class="foo.Bar">
  <property name="fred.bob.sammy" value="123"/>
</bean>

fooビーンには、bobプロパティをもつfredプロパティをもっていて、最下階層のsammyプロパティにはスカラー値の123が設定されている。これが有効に動作するために、foofredプロパティとfredbobプロパティは、このビーンが生成された後、非ヌルでなければならない、さもないとnull-pointer Exceptionが投げられる。

3.3.4 メソッドインジェクション

ユーザのほとんどにとって、コンテナにあるビーンの大部分はシングルトンだ。シングルトンビーンが他のシングルトンビーンとのコラボレートが必要になる場合、もしくは非シングルトンビーンが他の非シングルトンビーンとのコラボレートが必要になる場合、あるビーンを別のビーンのプロパティに定義することでこの依存関係を扱うという典型的なそして共通のアプローチは全く適切だ。 しかしながら、ビーンそれぞれのライフサイクルが違う場合、問題が発生する。あるシングルトンビーンAを考える。これはAにあるメソッドを起動するたびに非シングルトン(プロトタイプ)ビーンBを必要とする。コンテナはシングルトンビーンAを一度だけ生成し、プロパティへ設定を一度だけ行う機械を得る。でもコンテナがビーンAに、ビーンBが必要になるたびに新しいBのインスタンスを供給するような機会は得られないのだ。

この問題に対する1つの解は、inversion of controlをあきらめることだ。ビーンAはBeanFactoryAwareを実装することで、コンテナを意識することができ、(新しい)ビーンBが必要になるたびにgetBean("B")を呼んでコンテナに問い合わせるというプログラミング的な方法を用いることができる。これはビーンのコードがSpringへのアクセスを意識することになるので一般的に望ましい解ではない。

BeanFactoryの高度な機能であるメソッドインジェクションであれば、他のシナリオと同様に、クリーンな方法でこのようなユースケースを扱うことが可能となる。

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

ルックアップメソッドインジェクションは、コンテナ内で管理されたビーンに対し、抽象メソッドもしくは具象メソッドを上書きするというコンテナの能力を用い、コンテナ内の他の名前のついたビーンをルックアップした結果を返す。このルックアップは典型的には、上述したシナリオ(これはシングルトンでもありうる)ごとに非シングルトンビーンのものだ。SpringではこれをCGLIBライブラリによるバイトコード生成を用いて、メソッドがオーバライドされたサブクラスを動的に生成することにより実装している。

インジェクションされるメソッドを含んでいるクライアントクラスでは、そのメソッドの定義は以下の形式でアブストラクト(あるいは具象)定義でなければならない。

protected abstract SingleShotHelper createSingleShotHelper();

そのメソッドがアブストラクトでなければ、Springは単純に既存の実装をオーバライドする。XmlBeanFactoryの場合、ビーン定義にあるlookup-method要素を使ってSpringにこのメソッドに特定のビーンをコンテナから返すようにインジェクション/オーバライドするように指示する。例えばこんな風になるだろう。

<!-- a stateful bean deployed as a prototype (non-singleton) -->
<bean id="singleShotHelper" class="..." singleton="false">
</bean>

<!-- myBean uses singleShotHelper -->
<bean id="myBean" class="...">
  <lookup-method name="createSingleShotHelper" bean="singleShotHelper"/>
  <property>
    ...
  </property>
</bean>

このmyBeanで指定されているビーンは、singleShotHelperビーンのインスタンスを必要とするときは必ずcreateSingleShotHelperメソッドを呼ぶ。このビーンをデプロイする人は(それが実際に必要である場合に)singleShotHelperを非シングルトンとして注意深くデプロイしなければならない点に注意することは重要だ。もしシングルトンとしてデプロイされた場合(明示的に、あるいはこのフラグのデフォルトのtrueの値に頼って)、同じsingleShotHelperのインスタンスが毎回返されてしまう。

ルックアップメソッドインジェクションはコンストラクタインジェクション(生成されたビーンにオプションのコンストラクタ引数を供給する)やセッターインジェクション(ビーンが生成される際にプロパティを設定する)とも組み合わせることができることに注意してほしい。

3.3.4.2 任意のメソッド置き換え

メソッドインジェクションの中で、ルックアップメソッドインジェクションほど一般的で有用ではない方法だが、管理下のビーンの任意のメソッドを別の実装に置き換える機能があるが、この機能が実際に必要となるまではこのセクションの以降(この多少高度な機能について述べている)を読み飛ばしてもらってもかまわない。

XmlBeanFactoryでは、デプロイ済みのビーンに対し既存のメソッドの実装を他のものに置き換えるのにreplaced-method要素を用いる。下記の、computeValueメソッドをもつクラスをオーバライドすることを考えてみよう。

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

  ... some other methods
}

org.springframework.beans.factory.support.MethodReplacerインタフェースを実装するクラスには新しいメソッドの定義を用意する必要がある。

/** meant to be used to override the existing computeValue
    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 ...;
}

オリジナルクラスをデプロイするためのBeanFactoryデプロイ定義とメソッドのオーバライドの指定はこのようにして行う。

<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要素を含んだひとつ以上のarg-type要素が使われる。引数のシグネチャは実際にはメソッドがオーバライドされる場合に限り必要であり、クラスでは複数のやり方がある。便宜上、引数のための型をあらわす文字列は完全な正規のタイプ名の部分文字列となる。例えば下記の例は全てjava.lang.Stringにマッチする。

    java.lang.String
    String
    Str

個々の選択可能なものを区別するために引数の数が十分に多い場合があるので、この引数とマッチする短縮文字列を使うことで、タイピング量を減らすことができる。

3.3.5 depends-onを使う

ほとんどの状況では、あるビーンが他のビーンと依存関係にあるということは、つまり、あるビーンが他のビーンのプロパティに設定されていることで分かる。これは典型的にはXmlBeanFactoryにあるref要素で示される。この方式では、時々コンテナを意識したビーンがその依存相手(文字列型の値、もしくはidref要素が用いられる。これも文字列型の値と同じように評価される)のidが与えられる。そして1つ目のビーンがプログラムによって自分の依存関係をコンテナに問い合わせる。いづれの場合においても、依存関係は依存するビーンよりも先に適切に初期化が行われる。

ビーン間の依存関係がそれほど直接的ではない(例えばデータベースドライバの登録のようにクラスの静的なイニシャライザが起動される必要がある)ような比較的まれな状況においては、depends-on要素を使うビーンが初期化される前に明示的に1つ以上のビーンを初期化するためにdepends-on要素を用いてもかまわない。

下記は、設定例だ。

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

<bean id="manager" class="ManagerBean"/>

3.3.6 コラボレータをオートワイヤリングする

BeanFactoryは協調するビーンとの間で自動的に関連を持たせることができる。これは、BeanFactoryのコンテンツを認識することによって、自動的にSpringが協調相手(別のビーン)を解決するということだ。オートワイヤ機能には5つのモードが用意されている。オートワイヤは各ビーンごとに指定するので、あるビーンではオートワイヤが有効なのに、別のビーンではオートワイヤが無効といったことが可能だ。オートワイヤを使えば、指定しないといけないプロパティやコンストラクタ引数の数を減らす、さらにはなくすことができ、かなりのタイピングを減らすことができる(1)XmlBeanFactoryの場合、ビーン定義のオートワイヤのモードはbean要素のautowire属性を使って指定する。この属性には以下のような値が設定可能だ。

Autowireのモード
モード 説明
autowireは全て無効。ビーンリファレンスはref要素経由で定義されないといけない。これはデフォルト値であり、コラボレータを明示的に指定すると、制御しやすくまた明確になるので、より大規模なデプロイではこれを変更するのはお勧めしない。ある程度は、システムの構造についてのドキュメントの形をなしている。
byName プロパティ名によるオートワイヤ。このオプションはBeanFactoryを調べてオートワイヤされるべきプロパティと同じ名前のビーンを探す。例えば、名前によるオートワイヤが設定されているビーン定義があって、masterプロパティを持っている(これは、つまりsetMaster(...)メソッドがあるということ)とすると、Springはmasterという名前のビーン定義を探しだし、プロパティの設定にこれを用いる。
byType BeanFactoryにそのプロパティの型のビーンが1つだけある場合に、そのプロパティがオートワイヤされる。もし複数のビーンがあれば、致命的な例外がスローされる。つまりこのようなビーンに対しては、byTypeのオートワイヤは使ってはいけないということだ。一致するビーンがなければ何も起きない。つまりプロパティはセットされない。これが望ましくないのであれば、この場合にエラーを発生させるようにdependency-check="object"の属性値を指定する。
constructor これはbyTypeと似ているが、コンストラクタ引数に適用する。ビーンファクトリにコンストラクタ引数型のビーンが1個に限定できなければ致命的なエラーが発生する。
autodetect ビーンクラスの中身によってconstructorかbyTypeかを選択する。デフォルトコンストラクタがあれば、byTypeが適用される。

property要素やconstructor-arg要素における明示的な依存関係は常にオートワイヤを上書きすることに注意してほしい。オートワイヤの振る舞いは依存性チェックと組み合わせることができ、これはオートワイヤ処理が完了した後で実行される。

オートワイヤに関する長所、短所を理解することは重要だ。オートワイヤリングの長所を以下に挙げる。

オートワイヤリングの短所としては以下のようなものがある。

いかなる場合においても、「これは間違っている」「これは正しい」ということは言えない。我々はプロジェクトを通じた一貫性をお勧めする。例えば、オートワイヤリングを通常利用していない場合、たったひとつ、あるいは二つのビーン定義をするためだけにオートワイヤリングを用いるのは開発者を混乱させることになるだろう。

  1. 3.3.1 "ビーンプロパティの設定とコラボレータ"を参照のこと

3.3.7 依存関係をチェックする

Springでは、BeanFactoryにデプロイされたビーンの名前解決されていない依存関係が存在するかをチェックさせることができる。これは、ビーンのJavaBeansプロパティであって、これは、実際の値がビーン定義中に設定されていないかもしくは自動的にオートワイヤ機能によって設定されたものだ。

全てのプロパティ(もしくは特定の型のプロパティ全部)が設定されることを保証したい場合、この機能が役に立つことがある。もちろん、多くの場合、ビーンクラスはその多くのプロパティにはデフォルト値があり、中には想定シナリオ全部で適用するわけではないプロパティもあるので、この機能は限定的に利用される。依存関係チェックも、オートワイヤ機能と同じようにビーンごとに有効/無効を指定することができる。デフォルトでは依存関係チェックを行わない。依存関係チェックは異なるモードでの動作が可能だ。XmlBeanFactoryでは、ビーン定義にあるdependency-check属性で指定され、この属性には下記のような値が設定される。

依存関係チェックモード
no 依存関係チェックを行わない。指定された値を持たないプロパティは単に設定されないだけ。
simple プリミティブ型とコレクション(コラボレータ、つまり別のビーンを除く全て)に対して依存関係チェックを実行する。
object コラボレータに対して依存関係チェックを実施する。
all コラボレータ、プリミティブ型、コレクションに対して依存関係チェックを実施する。

3.4 ビーンの性質をカスタマイズする

3.4.1 ライフサイクルインタフェース

SpringではBeanFactory中でビーンの振る舞いを変更するためにマーカーインタフェースがいくつか提供されている。これにはInitializingBeanDisposableBeanも含まれている。このインタフェースを実装すると、初期化やデストラクタで何か処理ができるようにBeanFactoryで前者にはafterPropertiesSet()が呼ばれ、後者にはdestroy()が呼ばれ、ビーンの初期化、あるいは削除の際にアクションを実行させることができるようになる。

内部的には、Springは任意のマーカーインタフェースが適切なメソッドを見つけて実行するようにBeanPostProcessorsを使う。もし機能のカスタマイズをしたりSpringがかなわない他の優れたライフサイクル動作が必要であれば、BeanPostProcessorを自分で実装すればよい。これに関する詳細は章[3.7 BeanPostProcessorsを使ってBeanFactoryをカスタマイズする]にある。

異なるライフサイクルマーカーインタフェースは全て下記に示してある。付録の中に、Springがビーンをどのように管理するか、ライフサイクル機能がどのようにビーンの性質を変更するか、またそれらがどのように管理されるかを示すダイアグラムがある

3.4.1.1 InitializingBean / initメソッド

org.springframework.beans.factory.InitializingBeanを実装すると、BeanFactoryが必要なプロパティを全て設定した後にビーンに初期化を実行させることができるようになる。InitializingBeanインタフェースにはメソッドが1つだけ指定されている。

* Invoked by a BeanFactory after it has set all bean properties supplied
* (and satisfied BeanFactoryAware and ApplicationContextAware).
* <p>This method allows the bean instance to perform initialization only
* possible when all bean properties have been set and to throw an
* exception in the event of misconfiguration.
* @throws Exception in the event of misconfiguration (such
* as failure to set an essential property) or if initialization fails.
*/
void afterPropertiesSet() throws Exception;

注意:一般に、InitializingBeanマーカーインタフェースは使わないで済ませることができる(Springとの不必要な結合性を作るので使うことをよしとしない)。ビーン定義は指定された汎用的な初期化メソッドのサポートを提供する。XmlBeanFactoryの場合には、これはinit-method属性により実行される。例えば下記のような定義があるとしよう。

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

public class ExampleBean {
    public void init() {
        // do some initialization work
    }
}
<bean id="exampleInitBean" class="examples.ExampleBean" \
    init-method="init"/>

これは、厳密に以下と同じだ。

<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements InitializingBean {
    public void afterPropertiesSet() {
        // do some initialization work
    }
}

しかし、Springと密接に結合はしていないのである。

3.4.1.2 DisposableBean / destroyメソッド

org.springframework.beans.factory.DisposableBeanインタフェースを実装すると、そのビーンが含まれているBeanFactoryがデストロイされたときにビーンがコールバックを受け取ることができるようになる。このDisposableBeanインタフェースにはメソッドが1つ指定されている。

 /**
  * Invoked by a BeanFactory on destruction of a singleton.
  * @throws Exception in case of shutdown errors.
  * Exceptions will get logged but not rethrown to allow
  * other beans to release their resources too.
  */
 void destroy() throws Exception;

注:一般に、DisposableBeanマーカーインターフェースを使わずに済む手がある(がコードがSpringと不必要に密接になるのであまりお勧めできない)。ビーン定義では汎用のデストロイメソッドの指定がサポートされている。XmlBeanFactoryの場合、これはdestory-method属性で指定される。例えば下記のような定義があるとしよう。

<bean id="exampleInitBean" class="examples.ExampleBean" \
    destroy-method="cleanup"/>
public class ExampleBean {
    public void cleanup() {
        // do some destruction work (like closing connection)
    }
}

これは、厳密に以下と同じだ。

<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements DisposableBean {
    public void destroy() {
        // do some destruction work
    }
}

しかし、Springとコードが密接に結合しないのである。

重要な注意: プロトタイプモードでビーンをデプロイする場合、そのビーンのライフサイクルは少し変わる。定義により、Springでは非シングルトン/プロトタイプビーンの完全なライフサイクルを管理することはできない。というのは、生成後、もはや全く把握していないクライアントやコンテナに渡されるからだ。非シングルトン/プロトタイプビーンをnewオペレータの代わりとして話すときのSpringの役割について考えることができる。そのポイントを過ぎたアスペクトはなんでもクライアントにより扱われなければならないのだ。BeanFactoryにおけるビーンのライフサイクルについてのより詳細は、3.4.1章 "ライフサイクルインタフェース"で述べられている。

3.4.2 あなたが誰だかを知る

3.4.2.1 BeanFactoryAware

org.springframework.beans.factory.BeanFactoryAwareインタフェースを実装したクラスはそれを生成したBeanFactoryへのリファレンスをもっており、それはBeanFactoryによって生成されたときに渡される。

public interface BeanFactoryAware {
   /**
    * Callback that supplies the owning factory to a bean instance.
    * <p>Invoked after population of normal bean properties but before an init
    * callback like InitializingBean's afterPropertiesSet or a custom init-method.
    * @param beanFactory owning BeanFactory (may not be null).
    * The bean can immediately call methods on the factory.
    * @throws BeansException in case of initialization errors
    * @see BeanInitializationException
    */
    void setBeanFactory(BeanFactory beanFactory) throws BeansException;
}

これにより、org.springframework.beans.factory.BeanFactoryインタフェースによって、あるいは付加機能を公開する既知のサブクラスへのリファレンスへキャストすることによって、ビーンから、そのビーンをプログラム的に生成したBeanFactoryを操作することができるようになる。まず、これはプログラミングによって他のビーンの検索から構成される。この機能が有効な場合、Springとコードが密接になってしまい、コラボレータがプロパティとしてビーンに設定されるのでInversion of Controlスタイルに従わなくなり、ので一般的には避けるべきである。

3.4.2.2 BeanNameAware

org.springframework.beans.factory.BeanNameAwareインタフェースを実装するビーンがあって、それが、BeanFactory中にデプロイされている場合、そのBeanFactoryはそのビーンをこのインタフェース経由で呼び出し、デプロイ下にいるという印にそのビーンのIDを設定する。このコールバックは、InitializingBeanafterPropertiesSetもしくは独自のinit-methodのように通常のビーンプロパティが構成された後、コールバックが初期化される前に起動される。

3.4.3 ファクトリビーン

org.springframework.beans.factory.FactoryBeanインタフェースは、自分自身がファクトリであるオブジェクトで実装されるインタフェースである。BeanFactoryインタフェースには3つのメソッドが用意されている。

3.5 アブストラクトビーン、チャイルドビーン定義

ビーン定義には、大量の設定情報が含まれており、これにはコンテナに依存した情報(つまり、初期化メソッド、静的なファクトリメソッド名など)やコンストラクタ引数、プロパティ値も含まれる。チャイルドビーン定義というのは設定データを親の定義から継承したビーン定義であり、その際値をオーバライドしたり、必要に応じて値を追加といったことが可能である。親子ビーン定義を使うことにより、型が増えるのを抑えることができる。実際には、これはテンプレートの形式だ。

BeanFactoryをプログラム的に使う場合、チャイルドビーン定義はChildBeanDefinitionクラスによってあらわされる。ほとんどのユーザにとっては、このレベルの処理をする必要はなく、代わりにXmlBeanFactoryのような設定ビーン定義を宣言的に用いる。XmlBeanFactoryビーン定義では、チャイルドビーン定義はparent属性を用いて簡単に示されており、この属性の値として親のビーンを指定する。

<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">
  <property name="name" value="override"/>
  <!-- age should inherit value of 1 from parent -->
</bean>

チャイルドビーンの定義が指定されていなければ親の定義で用いられるビーンクラスを用いるが、オーバライドすることも可能だ。後者の場合、チャイルドビーンクラスは親クラスと互換性を持たなければならない。つまり、親のプロパティの値が設定可能である必要がある。

チャイルドビーン定義は、コンストラクタ引数の値、プロパティの値、メソッドのオーバライドを親から継承し、さらに新しい値を追加する。初期化メソッド、デストロイメソッド、そして/あるいは静的なファクトリメソッドが指定されていれば対応する親の設定をオーバライドする。

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

上記の例では、親のビーン定義に、abstractというふうに、abstract属性を使って明示的にマーキングを行った。下記では、親の定義はクラスを指定していない

<bean id="inheritedTestBeanWithoutClass">
    <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 should inherit value of 1 from parent -->
</bean>

この親ビーン自身不完全であり、アブストラクトなのでなのでインスタンス化することはできない。このように定義がアブストラクトとされている場合(明示的にであろうと暗黙的にであろうと)、純粋なテンプレートもしくは、子定義に対し親として提供するアブストラクトビーン定義として使うことができる。(他のビーンのrefプロパティで参照することによって)そのようなアブストラクト親ビーンを使おうとしたり、あるいは明示的に親ビーンIDを指定してgetBean()を呼んでもエラーになってしまう。同様に、コンテナ内部のpreInstantiateSingletonsメソッドはアブストラクトのビーン定義は完全に無視する。

重要:アプリケーションコンテキスト(単純なビーンファクトリではなく)はデフォルトで全てのシングルトンをあらかじめインスタンス生成を行う。従って、(少なくともシングルトンビーンでは)、テンプレートとしてしか使うつもりのないビーン定義があって、その定義にクラスが指定してあると、abstract属性をtrueに設定しないといけない。さもないと、アプリケーションコンテキストは実際にインスタンス生成を実行してしまう。

3.6 BeanFactoryとやり取りする

BeanFactoryは本質的にには、異なるビーンの登録とそれらの依存関係の管理ができる高機能なファクトリのためのインタフェースに過ぎない。このBeanFactoryでは、ビーン定義を読み込んでビーンファクトリ経由でアクセスすることができる。BeanFactoryであれば、下記のようなXMLフォーマットのビーン定義を読み込んでビーンを生成することができる。

InputStream is = new FileInputStream("beans.xml");
XmlBeanFactory factory = new XmlBeanFactory(is);

基本的には全部用意されている。getBean(String)を使って自分のビーンのインスタンスを検索することができる。もしそれをシングルトン(これがデフォルト)として定義してあれば、同じビーンへの参照を取得したり、シングルトンにfalseが設定してあれば毎回新しいインスタンスを取得できる。クライアント側のBeanFactoryの見え方は驚くほど単純だ。BeanFactoryインタフェースにはクライアントが呼ぶためのメソッドは5つしか用意されていない。

3.6.1 FactoryBeanが生成したものではなくそのものを取得する

時には、BeanFactoryが生成したビーンではなく、BeanFactoryインスタンスそのものをBeanFactoryに対して問い合わせる必要が生じることがある。これは BeanFactorygetBeanメソッドを呼んだとき(ApplicationContextの場合も含む)にビーンIDの頭に&をつけることで行われる。したがって、myBeanというIDがついているFactoryBeanの場合は、BeanFactorygetBean("myBean")を実行するとそのFactoryBeanが生成したものが返されるが、getBean("&myBean")を実行するとFactoryBeanのインスタンスそのものが返される。

3.7 BeanPostProcessorsを使ってBeanFactoryをカスタマイズする

ビーンポストプロセッサは、org.springframework.beans.factory.config.BeanPostProcessorインタフェースを実装したjavaクラスであり、2つのコールバックメソッドで構成される。このクラスがpost-processorとしてBeanFactoryに登録されると、そのBeanFactoryで生成された各ビーンインスタンスに対し、そのポストプロセッサは初期化メソッド(afterPropertiesSetや何か宣言されている初期化メソッド)が呼ばれる前、さらに呼ばれた後にBeanFactoryからコールバックを得る。このpost-processorは、コールバックを完全に無視するのも含めてビーンに対して実行したいことであれば何でも可能だ。典型的なビーンポストプロセッサでは、マーカインタフェースをチェックするか、あるいはビーンをプロキシでラップしたりする。Springのヘルパークラスにはビーンポストプロセッサとして実装されているものもある。

BeanFactoryのビーンポストプロセッサに対する扱うはApplicationContextとはわずかに異なるということを知っておくことは重要だ。ApplicationContextBeanPostProcessorインタフェースが実装されたビーンがデプロイされるのを自動的に認識し、ビーン生成時にファクトリから適切に呼ばれるようにそれをpost-processorとして登録する。ポストプロセッサは他のビーンと同じやり方でデプロイする以外には何もする必要はないのだ。一方、素のBeanFactoryを使う場合、ビーンポストプロセッサは下記のようなコードを使って明示的に手動で登録しなければならないのだ。

ConfigurableBeanFactory bf = new .....;     // create BeanFactory
   ...                       // now register some beans
// now register any needed BeanPostProcessors
MyBeanPostProcessor pp = new MyBeanPostProcessor();
bf.addBeanPostProcessor(pp);

// now start using the factory
  ...

本マニュアルの登録手順は不便であり、ApplicationContextは機能的にはBeanFactoryのスーパーセットなので、ApplicationContextの派生はポストプロセッサを必要とする場合に使用するのをお勧めする。

3.8 BeanFactoryPostprocessorsを用いてビーンファクトリをカスタマイズする

ビーンファクトリポストプロセッサはorg.springframework.beans.factory.config.BeanFactoryPostProcessorインタフェースを実装したJavaクラスである。コンストラクタ実行後、BeanFactory全体へのなんらかの変更が(BeanFactoryの場合には)マニュアルで、(ApplicationContextの場合には)自動で実行される。Springには先に述べたPropertyResourceConfigurerPropertyPlaceHolderConfigurerのような生成前ビーンファクトリポストプロセッサが多く含まれており、本マニュアルで後述しているようにBeanNameAutoProxyCreatorは他のビーンや、他の種類のプロキシでラップするのに役に立つ。BeanFactoryPostProcessorは(章[3.9 カスタマイズしたPropertyEditorsを追加で登録する]で述べているように)カスタムエディタを追加するのに用いることができる。

BeanFactoryでは、BeanFactoryPostProcessorを適用する手順は手動であり、下記と似たようなものである。

XmlBeanFactory factory = new XmlBeanFactory(new FileSystemResource("beans.xml"));
// create placeholderconfigurer to bring in some property
// values from a Properties file
PropertyPlaceholderConfigurer cfg = new PropertyPlaceholderConfigurer();
cfg.setLocation(new FileSystemResource("jdbc.properties"));
// now actually do the replacement
cfg.postProcessBeanFactory(factory);

ApplicationContextBeanFactoryPostProcessorインタフェースを実装したビーンがアプリケーションコンテキスト内にデプロイされるのを検出し、適切なタイミングで自動的にビーンファクトリポストプロセッサとして利用する。このポストプロセッサをデプロイするには他のビーンと同じやり方でデプロイする以外には何も必要ない。

本マニュアルの手順は不便だし、ApplicationContextは機能的にBeanFactoryのスーパーセットなので、一般的には、ビーンファクトリポストプロセッサを必要とする場合はApplicationContextの派生を用いることをお勧めする。

PropertyPlaceholderConfigurer

ビーンファクトリポストプロセッサとして実装されたPropertyPlaceholderConfigurerはプロパティ値をBeanFactory定義を外に出してJavaプロパティのフォーマットで書かれた別のファイルに取り込むのに用いる。これは、メインのXML定義ファイルやBeanFactoryのファイルを編集するという複雑でリスクなことをせずに、ユーザにプロパティをいくつかいじってデプロイできるようにする(例えば、データベースのURL,ユーザ名、パスワード)のに便利だ。

placeholderの値をもつデータソースが定義されているBeanFactory定義の断片を見てみよう。

下記の例にあるように、データソースが定義されており、外部プロパティファイルからあるプロパティ値を設定する。実行時に、データソースのプロパティを書き換えるBeanFactoryPropertyPlaceholderConfigurerを適用する。

<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形式の別のファイルから取得する

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

これをBeanFactoryと一緒に使うために、ビーンファクトリポストプロセッサを手動で実行する。

XmlBeanFactory factory = new XmlBeanFactory(new \
    FileSystemResource("beans.xml"));
PropertyPlaceholderConfigurer cfg = new PropertyPlaceholderConfigurer();
cfg.setLocation(new FileSystemResource("jdbc.properties"));
cfg.postProcessBeanFactory(factory);

ApplicationContextでは、BeanFactoryPostProcessorを実装したビーンがデプロイされたらそれを自動的に認識して適用することが可能であることに注意して欲しい。これはつまり、ここで記述されているように、ApplicationContextを使う場合はPropertyPlaceholderConfigurerを適用する方がもっと便利だ、ということだ。PropertyPlaceholderConfiguerやあるいはその他のビーンファクトリポストプロセッサを使う場合は、BeanFactoryよりもApplicationContextを使うほうをお勧めする。

このPropertyPlaceholderConfigurerは指定したPropertiesのファイルにあるプロパティを探すだけでなく、使おうとするプロパティが見つからない場合にJavaのシステムプロパティへのチェックも行う。この振る舞いはconfigurersystemPropertiesModeというプロパティを設定することでカスタマイズが可能だ。このプロパティには3通りの値があり、1つは常にオーバライドする、1つはオーバライド禁止、そしてもうひとつは、プロパティが指定されたプロパティファイルに見つからない場合のみオーバライドするというものだ。より詳細な情報は、PropertiesPlaceholderConfigurerのJavaDocを参照して欲しい。

PropertyOverrideConfigurer

別のビーンファクトリポストプロセッサであるPropertyOverrideConfigurerPropertyPlaceholderConfigurerと似ているが、後者と違う点は、元の定義にあるビーンプロパティ全てにデフォルト値が用意されているかいないかである。オーバライドしたPropertiesファイルにあるビーンプロパティのエントリがなかったら、デフォルトのコンテキスト定義が使われる。

ビーンファクトリの定義はそれがオーバライドされるかどうかは関知しない、という点に注意してほしい。よって、オーバライドconfigurerを用いているXML定義ファイルを見てもすぐにはわからないと思う。PropertyOverrideConfigurersが複数異なる値で同じビーンプロパティに定義されている場合、(オーバライドのメカニズムにより)一番最後の設定が有効となる。

Propertiesファイルの設定行は、下記のような形式で記述されていることが求められる。

beanName.property=value

プロパティファイルの例はこのようなものだ。

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

この例で挙げたファイルは、driverurlといったプロパティを持つdataSourceと呼ばれるビーンを持つBeanFactoryの定義に用いることができる。

オーバライドされてすでに非ヌルである(おそらくコンストラクタで初期化された)ファイナルプロパティを除くすべてのコンポネントと同様、複合プロパティ名もサポートされている点に注意してほしい。下記の例:

foo.fred.bob.sammy=123

では、fooビーンのfredプロパティのbobプロパティのsammyプロパティがスカラー値123に設定されている。

3.9 カスタマイズしたPropertyEditorsを追加で登録する

ビーンプロパティを文字列値として設定する場合、その文字列をプロパティの複雑な型に変換するために、BeanFactoryは最終的に標準のJavaBeans PropertyEditorsを使う。Springではあらかじめ多くの独自PropertyEditors(例えば、文字列で表現されたクラス名を実際のClassオブジェクトに変換するため)を登録する。さらに、Java標準のJavaBeans PropertyEditorのルックアップメカニズムでは、PropertyEditorが適当な名前がつけられ、サポートすべきクラスと同じパッケージに配置されるように自動的に見つけられる。

他にもカスタマイズしたPropertyEditorを登録する必要がある場合は、いくつかの方法を使うことができる。

一番手間のかかる方法(通常は不便でお勧めしない)は、BeanFactoryへのリファレンスがあると仮定して、ConfigurableBeanFactoryインタフェースのregisterCustomEditor()メソッドを使うだけだ。

これよりも便利な方法は、CustomEditorConfigurerと呼ばれる特別なビーンファクトリポストプロセッサを使うというものだ。ビーンファクトリポストプロセッサはBeanFactoryとともに半手動で使うことができるが、これはネストされたプロパティ設定を持っているので、ここで述べたように、他のビーンと同じようにデプロイして自動的に検出、適用されるApplicationContextと一緒に使うことを強くお勧めする。

全てのビーンファクトリやアプリケーションコンテキストはプロパティ変換を行うBeanWrapperと呼ばれるものを使って、多くの組み込みプロパティエディタを利用するという点に注意してほしい。BeanWrapperが登録する標準のプロパティエディタは次章に列記してある。さらに、ApplicationContextも特定のアプリケーションのコンテキストのタイプに応じてリソースのルックアップを行うために3つのエディタをオーバライド、もしくは追加を行う。その3つとは、InputStreamEditor,ResourceEditor,URLEditorである。

3.10 既存のビーンに別名を追加するためにalias要素を利用する

あるビーン定義の中で、id属性を使って1つの名前を指定し、alias属性を使って他の名前を使う、という組み合わせによりそのビーンに複数の名前をつけることは可能だ。この名前はすべて同じビーンの等価な別名とみなされ、あるアプリケーションで用いられている各コンポネントがそのコンポネント自身をさすビーン名を使って共通の依存性を参照するような状況においては便利である。

しかしながら、そのビーンが実際に定義されている場合にすべての別名を指名しなければならないのは必ずしも適当ではない。他の場所で定義されているビーンに別名を導入するのは望ましいこともあるが、これは、スタンドアロンなalias要素を使って指定されることもある。

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

この場合、fromNameという名前の同じコンテキストのビーンがこのalias定義を利用することでtoNameで参照される。

具体的な例として、コンポネントAがそのXML定義ファイルの中でcomponentA-dataSourceというDataSourceビーンを定義する場合を考えてみる。しかしながらコンポネントBがそのXML定義ファイルの中でそのDataSourcecomponentB-dataSourceとして参照したい。また、メインのアプリケーションMyAppがそのXML定義ファイルの中でこれらのXMLファイルから最終的にアプリケーションコンテキストを定義構築し、そのDataSourcemyApp-dataSourceという名前で参照したい。このシナリオは、MyAppのXMLファイルに下記のスタンドアロンエイリアスを追加することで簡単に扱うことができる。

<alias name="componentA-dataSource" alias="componentB-dataSource"/>
<alias name="componentA-dataSource" alias="myApp-dataSource"/>

ここで、各コンポネントとメインのappは一意で他の定義とぶつからないことが保障された(名前空間という便利なものがある)、でも同じビーンを参照する名前を使って、dataSourceを参照することができる。

3.11 ApplicationContextを導入する

ビーンパッケージでは、ビーンを維持管理するための基本的な機能が用意されているが、しばしばプログラム的なやり方で、コンテキストパッケージがBeanFactoryをもっとフレームワーク指向な方法で機能を拡張するApplicationContextを追加する。多くのユーザはApplicationContextを完全に宣言的なやり方で、手動で生成する必要がなく、その代わりJ2EEウェブアプリの通常の起動手順の一部としてApplicationContextを自動的に起動するためのContextLoaderのようなサポートクラスに依存して利用する。もちろん、プログラム的にApplicationContextを生成することも可能だ。

コンテキストパッケージの基礎となるのはorg.springframework.contextパッケージに配置されているApplicationContextインタフェースだ。BeanFactoryインタフェースにより、BeanFactoryの全機能を提供する。レイヤリングや階層コンテキストを使ってよりフレームワーク指向なやり方で使うために、コンテキストパッケージでは下記の機能も提供されている。

ApplicationContextBeanFactoryの全機能を含んでいるように、通常は、おそらくアプレットのようにメモリの消費がクリティカルで、余計な数百キロバイトで違いが生じる状況のようないくつかの制限のある状況を除いて、BeanFactory上で用いられることが一般的に推奨される。次のセクションでは、ApplicationContextが基本的なBeanFactoryでできることに追加する機能について述べてある。

3.12 ApplicationContextに機能を追加する

前のセクションで既に述べたように、ApplicationContextにはBeanFactoryとは異なる機能が2,3用意されている。それを1つ1つ見ていくことにしよう。

3.12.1 MessageSourceを使う

ApplicationContextインタフェースはMessageSourceと呼ばれるインタフェースを拡張したものなので、(i18nもしくは国際化)メッセージング機能を持つ。階層化されたメッセージを解決することができるNestingMessageSourceとともに、Springが提供するメッセージ解決を行う基本インタフェースである。このインタフェースに定義されているメソッドを見ていくことにしよう。

ApplicationContextがロードされるとき、自動的にそのコンテキストで定義されているMessageSourceビーンを探しにいく。このビーンはnameというmessageSourceを持っている必要がある。もし、そのようなビーンが見つかれば、上記で説明したメソッドの呼び出しは全て見つかったメッセージソースに委譲される。もしメッセージソースが見つからなかった場合、ApplicationContextは親が同様の名前を持ったビーンを持っているとみなす。もし持っていれば、そのビーンをMessageSourceとして利用する。もしメッセージソースが見つからなかった場合、上記で定義されたメソッドが呼ばれてもいいように、空のStaticMessageSourceがインスタンス化される。

Springでは現在、2種類のMessageSourceの実装が提供されている。その2つとはResourceBundleMEssageSourceStaticMessageSourceだ。共に、ネストされたメッセージを処理するためにNestingMessageSourceを実装している。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> 

ここでは、format、例外、そしてウィンドウと呼ばれるクラスパスに定義されている2つのリソースバンドルがあると仮定している。JDKのメッセージをResourceBundlesに解決する標準的な方法を使って、メッセージを解決する要求がバンドルされる。TODO:SHOW AN EXAMPLE

3.12.2 イベントを伝播させる

ApplicationContextにおけるイベントハンドリングはApplicationEventクラスとApplicationListenerインタフェースで提供されている。ApplicationListenerインタフェースが実装されたビーンがそのコンテキストにデプロイされると、ApplicationEventApplicationcontextに対して毎回発行される度にそのビーンにそれが通知される。これは標準的なオブザーバデザインパターンだ。Springでは標準のイベントが3つ用意されている。

イベント 説明
ContextRefreshedEvent ApplicationContextが初期化、もしくはリフレッシュされたときに発行されるイベント。ここでいう初期化とは全てのビーンがロードされ、シングルトンはあらかじめインスタンス化され、ApplicationContextが使えるように準備が整ったということだ。
ConetxtClosedEvent ApplicationContextが、ApplicationContextに定義されているclose()メソッドを使ってクローズされたときに発行されるイベント。ここでいうクローズとは、シングルトンがデストロイされたということである。
RequestHandlerEvent HTTPリクエストを受けつける全てのビーンが喋るWebに特化したイベント(つまり、リクエストが完了したら発行されるものだ)。このイベントは、SpringのDispatcherServletを使ったウェブアプリケーションでのみ当てはまる、という点にご注意を。)

独自のイベントを実装することももちろん可能だ。単に、ApplicationEventを実装した独自のイベントクラスのインスタンスをパラメータに指定してApplicationContextpublishEvent()メソッドを叩くだけだ。イベントリスナは同期的にイベントを受信する。これはpublishEvent()メソッドが、すべてのリスナーがイベント処理を完了するまでブロックするということだ。さらに言うと、あるリスナがイベントを受信したとき、もしトランザクションコンテキストが有効であれば、これは送信側のトランザクションコンテキストの内部で処理が行われる。

例を見てみよう。まずはApplicationContextだ。

<bean id="emailer" class="example.EmailBean">
    <property name="blackList">
        <list>
            <value>black@list.org</value>
            <value>white@list.org</value>
            <value>john@doe.org</value>
        </list>
    </property>
</bean>

<bean id="blackListListener" class="example.BlackListNotifier">
  <property name="notificationAddress" value="spam@list.org"/>
</bean>

次は、実際のビーンだ。

public class EmailBean implements ApplicationContextAware {

    /** the blacklist */
    private List blackList;
    
    public void setBlackList(List blackList) {
        this.blackList = blackList;
    }
    
    public void setApplicationContext(ApplicationContext ctx) {
        this.ctx = ctx;
    }
    
    public void sendEmail(String address, String text) {
        if (blackList.contains(address)) {
            BlackListEvent evt = new BlackListEvent(address, text);
            ctx.publishEvent(evt);
            return;
        }
        // send email
    }
}

public class BlackListNotifier implement ApplicationListener {

    /** notification address */
    private String notificationAddress;
    
    public void setNotificationAddress(String notificationAddress) {
        this.notificationAddress = notificationAddress;
    }

    public void onApplicationEvent(ApplicationEvent evt) {
        if (evt instanceof BlackListEvent) {
            // notify appropriate person
        }
    }
}

Of course, this particular example could probably be implemented in better ways (perhaps by using AOP features), but it should be sufficient to illustrate the basic event mechanism.

もちろん、この例については、おそらくもっといい方法で実装することは可能だろう(AOP機能を使えば)。だが、基本的なイベントの仕組みについて示すにはこれで十分のはずだ。

3.12.3

アプリケーションコンテキストを使いこなし、理解するには、4章「低レベルのリソースへのアクセスを抽象化する」に記述されているように、Springのリソース抽象化に習熟すべきだ。

アプリケーションコンテキストは、ResourceLoaderで、Resourcesをロードするのに利用することができる。Resourceは、本質的には、java.net.URLであり(実際、適切なURLを使ってラップする)、クラスパスから、ファイルシステム中の位置、標準URLで記述可能な場所やその他さまざまな場所を含め、任意の場所から透過的な方法で低レベルリソースを取得するのに用いることができる。もし、そのリソースの位置をあらわす文字列が特別なプレフィックスがついていない単純なパスであれば、その資源の位置は実際のアプリケーションコンテキストの型に応じて適切に指定される。

アプリケーションコンテキストにデプロイされたビーンは初期化時にResourceLoaderとして解析されたアプリケーションコンテキストから自動的にコールバックされるように特別なmarkerインタフェース、ResourceLoaderAwareを実装する。

ビーンは、さらにResource型のプロパティを外部に公開する。これはスタティックなリソースへアクセスするのに用いられ、また他のプロパティのように注入されることを期待する。このビーンをデプロイする人は、そのResourceプロパティに単純な文字列パスを指定する。そしてそのテキスト文字列を実際のResourceオブジェクトに変換するために、自動的にコンテキストによって登録された特別なJavaBean PropertyEditorをあてにするかもしれない。

ロケーションパスやApplicationContextのコンストラクタで提供されるパスは実際にはリソース文字列であり、これらは単純な形式で特定のコンテキストの実装として適切に扱われる(つまり、ClassPathXmlApplicationContextは単純なロケーションパスをクラスパスロケーションとして扱う)が、さらに特別なプレフィクスとともに、実際のコンテキスト型に関わらずクラスパスやURLから定義を強制的にロードするのに用いられることもある。

前述した章ではこれらの話題についてさらに詳しく述べられている。

3.12.3 Springのリソースを使う

3.13 ApplicationContextの振る舞いのカスタマイズ

BeanFactoryにはすでに(XmlBeanFactory configにあるinit-methoddestroy-method属性やビーンポストプロセッサのような評価しか設定で行わないInitializingBeanDisposableBeanのようなマーカーインタフェースのように)デプロイされたビーンのライフサイクルを制御するための機構が多くされている。ApplicationContextではこの全部が動作するが、さらにビーンやコンテナの振る舞いをカスタマイズするメカニズムが追加されている。

3.13.1 ApplicationContextAwareマーカーインタフェース

BeanFactoryで利用可能なマーカーインタフェースは全てまだ動作する。ApplicationContextは、org.springframework.context.ApplicationContextAwareを実装する特別なマーカーインタフェースを追加する。このインタフェースを実装し、コンテキストへデプロイされたビーンは、このインタフェースのsetApplicationContext()メソッドを使ってビーン生成時にコールバックされ、コンテキストへのリファレンスを受け取り、以降、コンテキストとのインタラクション用に格納される。

3.13.2 BeanPostProcessor

org.springframework.beans.factory.config.BeanPostProcessorインタフェースを実装したjavaクラスであるビーンポストプロセッサについてはすでに述べたが、ポストプロセッサは、素のBeanFactoryで使うよりもApplicationContextで使った方がとても便利で、再度取り上げるだけの価値はあるだろう。ApplicationContextでは上述のマーカーインタフェースを実装したビーンをデプロイすると、ファクトリ内部で生成させるたびに適切に呼び出されるように自動的に検出されビーンポストプロセッサとして登録される。

3.13.3 BeanFactoryPostProcessor

org.springframework.beans.factory.config.BeanFactoryPostProcessorインタフェースを実装したJavaクラスであるビーンファクトリポストプロセッサについてはすでに述べたが、ビーンファクトリポストプロセッサをApplicationContextで使うのは素のBeanFactoryよりもとても便利なので再度取り上げるだけの価値はあるだろう。ApplicationContextでは、上述したマーカーインタフェースが実装されたビーンがデプロイされると適切なときに呼び出されるように自動的にビーンファクトリポストプロセッサとして検出される。

3.13.4 PropertyPlaceholderConfigurer

PropertyPlaceholderConfigurerBeanFactoryと一緒に使う旨すでに述べたが、ApplicationContextで利用するとより便利なのでここで取り上げる価値があると思う。というのは、コンテキストは、他のビーンと同じようにデプロイされるだけで自動的にこのようなビーンファクトリポストプロセッサを認識し適用することができるからである。これを実行するのに、特別な手動操作をなんら必要としない。

<!- property placeholder post-processor ->
<bean id="placeholderConfig"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="location"> value="dbc.properties"/>
</bean>

3.14 追加の独自PropertyEditorを登録する

前述したように、標準のJavaBeansのPropertyEditorは文字列で表現されているプロパティの値を実際のプロパティの複雑な型に変換するのに用いられる。CustomEditorConfigurerというビーンファクトリポストプロセッサはApplicationContextに追加のPropertyEditorをサポートできるように便利に使うことができる。

ExoticTypeというユーザクラスと、プロパティとしてExoticTypeのセットを必要とする別のクラスDependsOnExoticTypeを考えてみよう。

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

これらが適切にセットアップされると、この型プロパティに文字列で割り当てて、背後で実際のExoticTypeオブジェクトに変換されるのを望む。

<bean id="sample" class="example.DependsOnExoticType">
    <property name="type"><value>aNameForExoticType</value></property>
</bean>

このPropertyEditorは下記に似ているように見ることができる。

// converts string representation to ExoticType object
public class ExoticTypeEditor extends PropertyEditorSupport {

    private String format;

    public void setFormat(String format) {
        this.format = format;
    }
    
    public void setAsText(String text) {
        if (format != null && format.equals("upperCase")) {
            text = text.toUpperCase();
        }
        ExoticType type = new ExoticType(text);
        setValue(type);
    }
}

最後に、新しいPropertyEditorApplicationContextに登録するのにCustomEditorConfigurerを使う。これで、必要に応じて使うことができるようになる。

<bean id="customEditorConfigurer" 
class="org.springframework.beans.factory.config.CustomEditorConfigurer">
    <property name="customEditors">
        <map>
            <entry key="example.ExoticType">
                <bean class="example.ExoticTypeEditor">
                    <property name="format" value="upperCase" />
                </bean>
            </entry>
        </map>
    </property>
</bean>

3.15 プロパティ表現からビーンプロパティやコンストラクタ引数を設定する

PropertyPathFactoryBeanは与えられたターゲットオブジェクトのプロパティパスを評価するFactoryBeanである。このターゲットオブジェクトは、直接もしくは、ビーン名で指定することができる。これで設定された値は他のビーン定義でプロパティ値もしくはコンストラクタ引数として用いられる。

ここで、他のビーンに名前として使われるパスの例をみてみよう。

// target bean to be referenced by name
<bean id="person" class="org.springframework.beans.TestBean" \
    singleton="false">
  <property name="age"><value>10</value></property>
  <property name="spouse">
    <bean class="org.springframework.beans.TestBean">
      <property name="age"><value>11</value></property>
    </bean>
  </property>
</bean>

// will result in 11, which is the value of property 'spouse.age' of bean \
    'person'
<bean id="theAge" \
    class="org.springframework.beans.factory.config.PropertyPathFactoryBean">
  <property name="targetBeanName"><value>person</value></property>
  <property name="propertyPath"><value>spouse.age</value></property>
</bean>

この例では、パスは内部ビーンに対して評価される。

// will result in 12, which is the value of property 'age' of the inner \
    bean
<bean id="theAge" \
    class="org.springframework.beans.factory.config.PropertyPathFactoryBean">
  <property name="targetObject">
    <bean class="org.springframework.beans.TestBean">
      <property name="age"><value>12</value></property>
    </bean>
  </property>
   <property name="propertyPath"><value>age</value></property>
</bean>

他にもショートカットの形式があり、ビーン名がプロパティパスとなる。

// will result in 10, which is the value of property 'age' of bean 'person'
<bean id="person.age" \
    class="org.springframework.beans.factory.config.PropertyPathFactoryBean"/>

この形式では、選択がビーンの名前になく、このビーンへの参照もパスである同じIDを使わないといけないということを意味する。もちろん、内部ビーンとして使う場合は、いづれにしても参照する必要はない。

<bean id="..." class="...">
  <proprty name="age">
    <bean id="person.age"
class="org.springframework.beans.factory.config.PropertyPathFactoryBean"/>
  </property>
</bean>

この結果の型は特別に実際の定義に設定される。これはほとんどの状況では使う必要ないと思われるが、役に立つこともあるだろう。詳細については、本機能に関するJavaDocを参照してほしい。

3.16 フィールド値からビーンプロパティやコンストラクタ引数を設定する

FieldRetrievingFactoryBeanはstaticあるいは非staticなフィールド値を検索するFactoryBeanである。これは、public static finalな定数を検索するのによく用いられ、また他のビーンのプロパティ値やコンストラクタ引数を設定するのに用いられる。

以下はstaticFieldを使うことによって、どのようにstaticフィールドが取り出されるのかを示す例である。

<bean id="myField"
class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean">
<property \
    name="staticField"><value>java.sql.Connection.TRANSACTION_SERIALIZABLE</value></property> \
</bean>

他にもstaticフィールドがビーン名として指定する便利な使い方もある。

<bean id="java.sql.Connection.TRANSACTION_SERIALIZABLE"
class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean"/>

これはつまり、ビーンIDになにも選択されていない(従って参照している他のビーンに長い名前が使われていないといけない)ということだが、この方法は、定義するのがとても簡潔で、ビーン参照のためにIDを指定する必要がないので、内部ビーンとして使うのに非常に便利なのだ。

<bean id="..." class="...">
  <proprty name="isolation">
    <bean id="java.sql.Connection.TRANSACTION_SERIALIZABLE"
class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean"/>
  </property>
</bean>

JavaDocにも記述されているように、これは他のビーンの非staticなフィールドにもアクセスが可能だ。

3.16 他のメソッドを起動し、その返り値を使う

他のクラスが使われる前に、ある種の初期化を実行するために、あるクラスのstaticメソッドもしくは非staticメソッドを呼ぶ必要があることがある。さらに、コンテナにあるほかのビーンのメソッドもしくは、任意のクラスのstaticメソッドを呼んでその結果をビーンにあるプロパティに設定する必要が生じることがある。このような目的のために、MethodInvokingFactoryBeanと呼ばれるヘルパークラスが用いられる。これはstaticメソッドもしくは非staticメソッドを起動した結果の値を返すFactoryBeanである。

でも、この2つめの用途には、以前にも述べたが、factory-methodの方をどんな場合においてもお勧めする。

下記は、なんらかのstaticな初期化を無理やり行うのにこのクラスを使った(XMLベースのBeanFactory定義にある)ビーン定義の例だ。

<bean id="force-init" \
    class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"> \
<property \
    name="staticMethod"><value>com.example.MyClass.initialize</value></property> \
</bean>

<bean id="bean1" class="..." depends-on="force-init">
  ...
</bean>

このbean1の定義では、force-initビーンを参照するのにdepends-on属性を使っている。これはまず最初force-initの初期化を起動する。staticの初期化メソッドが呼ばれ、bena1がまず最初に初期化される。

下記は、staticなファクトリメソッドを呼ぶのにこのクラスを用いるビーン定義の例である。

<bean id="myClass" \
    class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"> \
<property \
    name="staticMethod"><value>com.whatever.MyClassFactory.getInstance</value></property> \
</bean>

これはJavaシステムプロパティを取得するためにスタティックなメソッドを叩いて、そしてインスタンスメソッドを叩く例だ。ちょっと冗長だけど、でもちゃんと動くよ。

<bean id="sysProps" \
    class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"> \
  <property name="targetClass"><value>java.lang.System</value></property>
  <property name="targetMethod"><value>getProperties</value></property>
</bean>
<bean id="javaVersion" \
    class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"> \
  <property name="targetObject"><ref local="sysProps"/></property>
  <property name="targetMethod"><value>getProperty</value></property>
  <property name="arguments">
    <list>
      <value>java.version</value>
    </list>
  </property>
</bean>

ファクトリメソッドにアクセスするほとんどに利用されると思われるが、MethodInvokingFactoryBeanはシングルトンの場合はデフォルトで動作するということに注意して欲しい。ファクトリにオブジェクトを生成するようにコンテナから最初のリクエストがあると、指定されたメソッドが叩かれ、その返り値がキャッシュされると同時にそのリクエストと後続のリクエストに返される。ターゲットメソッドをオブジェクトへ問い合わせがあるたびに起動させるためには、ファクトリ内部のsingletonプロパティにfalseが設定されていないといけない。

スタティックなターゲットメソッドは、targetMethodプロパティに設定されているスタティックメソッドの名前を表す文字列により指定され、そのスタティックメソッドが定義されているクラスがtargetClassに指定される。反対に、ターゲットインスタンスメソッドはtargetObjectプロパティにターゲットオブジェクトとして設定されることで指定され、そのターゲットオブジェクト上叩くメソッド名としてtargetMethodプロパティに設定される。これらのメソッド起動の引数はargumentsプロパティの設定で指定される。

3.18 あるファイルから別のファイルへビーン定義をインポートする

コンテナの定義を複数のXMLファイルに分割するのが便利なことがしばしばある。 このXML断片全てによって設定されているアプリケーションコンテキストをロードするひとつの方法は、複数のリソースロケーションを指定するアプリケーションコンテキストコンストラクタを用いる方法だ。ビーンファクトリを使って、各ファイルから定義を繰り返し読み込むのに、ビーン定義リーダが複数回使われる。

一般に、Springチームは上記のアプローチを好んでいる。というのは、コンテナの設定ファイルが今のように他のファイルと組み合わされるということを気にしないでいいからだ。しかしながら、これとは異なる方法は、1つのXMLビーン定義ファイルから、いくつかのimport要素のインスタンスを使って、いくつかの別のファイルから定義をロードするというものだ。このimport要素は、ファイル中のインポートを行うbean要素よりも前にないといけない。ではサンプルを見てみよう。

  	 <beans>
  	   <import resource="services.xml"/>
  	   <import resource="resources/messageSource.xml"/>
  	   <import resource="/resources/themeSource.xml"/>
  	   <bean id="bean1" class="..."/>
  	   <bean id="bean2" class="..."/>
  	   . . .

この例では、外部ビーン定義は3つのファイル、services.xml、messageSource.xml、themeSource.xmlからロードされる。このファイルが置かれているパスはインポートを行うファイルから見て相対的なもので、この例では、services.xmlはインポートを行うファイルと同じディレクトリ、もしくはクラスパスになければならないが、messageSource.xmlやthemeSource.xmlはインポートするファイルの前のresourcesの場所になければならない。見てわかるように、先頭のスラッシュは実際には無視されるが、相対パスとみなされるので、おそらくスラッシュは付与しないほうがいい。

インポートされるファイルの中身は、一番上位のbeans要素も含めて、DTD上完全にバリッドなXMLビーン定義ファイルでなければならない。

3.19 ウェブアプリケーションからApplicationContextを生成する

BeanFactoryとは逆に、プログラム的に生成されることもあるだろうが、例えばContextLoaderを使ってApplicationContextを宣言的に生成することもできる。もちろん、ApplicationContextApplicationContextの実装を使ってプログラム的に生成することも可能だ。まずは、ContextLoaderとその実装について調べていこう。

ContextLoaderには2つの実装がある。ContextLoaderListenerContextLoaderServletだ。双方は機能的には全く同じだが、リスナーの方はサーブレット2.2準拠なコンテナでは使えないという違いがある。サーブレット2.4仕様からは、ウェブアプリケーションの起動直後に初期化するためにリスナーを必要とされている。多くの2.3準拠のコンテナでは既にこの機能は実装されている。これは、あなたがどのバージョンを使うか次第ではあるが、どれでも同じになるように、ContextLoaderListenerを使うようにすべきだと思う。互換性に関する詳細については、ContextLoaderServletJavaDocを見て欲しい。

ApplicationContextContextLoaderListenerを使って以下のように登録することができる。

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

<!-- OR USE THE CONTEXTLOADERSERVLET INSTEAD OF THE LISTENER
<servlet>
    <servlet-name>context</servlet-name>
<servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>
-->

このリスナーはContextConfigLocationパラメータを調べる。もし、このパラメータがなければ、/WEB-INF/applicationContext.xmlがデフォルトとして使用される。存在する場合は、あらかじめ定義されたデリミタ(コンマとセミコロン、スペース)で文字列を分割し、アプリケーションコンテキストを探す位置としてその値が用いられる。前に言ったようにContextLoaderListenerの代わりにContextLoaderServletを使うこともできる。このサーブレットもリスナーと同じようにcontextConfigLocationパラメータを使う。

3.20 糊となるコードと害となるシングルトン

アプリケーション内のコードの大多数は最もDependency Injection(Inversion of Control)スタイルで書かれていて、そのコードはBeanFactoryApplicationContextコンテナにあって、依存関係は生成時にコンテナにより注入される。またコンテナには完全に依存しない。しかしながら、他のコードと一緒に結合される必要があるような小さな結合層のコードでは、BeanFactoryApplicationContextへシングルトン(もしくは擬似シングルトン)スタイルのアクセスが必要になることがたまにある。例えば、サードパーティ製のコードが新しいオブジェクトをBeanFactoryの外でこのオブジェクトを取得できないのに直接(つまり、Class.forName()スタイルで)生成しようとするかもしれない。もし、このサードパーティのコードにより生成されたオブジェクトが小さなスタブかあるいはプロキシで、デリゲートする本当のオブジェクトを取得するのにシングルトンスタイルでBeanFactory/ApplicationContextにアクセスする場合でも、inversion of controlはほとんどのコード(BeanFactoryからできたオブジェクト)で保たれている。つまりほとんどのコードはコンテナや、あるいはどうやってアクセスされるかに依存せず、他のコードと分離されており、全ての利点が生かされている。EJBでもBeanFactoryで生成されたプレインなJavaで実装されたオブジェクトへのデリケートに、このスタブ/プロキシアプローチを使っている。BeanFactoryが理想的にシングルトンである必要がない場合は、メモリの使い方や初期化の回数(HibernateのSessionFactoryのようなBeanFactoryでビーンを使う場合)という点で、各ビーンがそれぞれ非シングルトンのBeanFactoryを使うのは非現実的かもしれない。

別の例として、複雑な多層(つまり、様々なJARファイル、EJB、EARとしてパッケージ化されたWARファイル)のJ2EEアプリケーションにおいて、各層がそれぞれ自前のApplicationContext定義を(効率よく階層をなして)持っている場合、一番上の層がWebアプリ(WAR)が1つしかない場合の好ましいアプローチは、単に、各層の複数のXML定義ファイルから合成ApplicationContextを1つだけ生成するというものだ。ApplicationContextの派生は全てこの方法で複数のXML定義ファイルから生成される。しかしながら、一番上の階層に同様のWebアプリが複数ある場合、その下の層からのほとんど同一のビーン定義で構成される各WebアプリごとにApplicationContextを生成することは、メモリ使用量が増えることで問題や、初期化に時間がかかるビーン(つまり、HibernateのSessionFactory)を複数コピーを生成する問題や、副作用による問題を引き起こすかもしれないので問題といえるもしれない。それに対して、ContextSingletonBeanFactoryLocatorSingletonBeanFactoryLocatorのようなクラスは多階層な(つまり、あるものが別の親になっている)BeanFactoryApplicationContextを効率よくシングルトン方式でオンデマンドで読み込まれるのに利用される。これは、そのウェブアプリ用ApplicationContextの親として用いられる。その結果、この下位層のビーン定義は必要なときしかロードされず、また高々1回しかロードされないのだ。

3.20.1 SingletonBeanFactoryLocatorとContextSingletonBeanFactoryLocatorを使う

SingletonBeanFactoryLocatorContextSingletonBeanFactoryLocatorを使った詳しい例はhttp://www.springframework.org/docs/api/org/springframework/beans/factory/access/SingletonBeanFactoryLocator.htmlhttp://www.springframework.org/docs/api/org/springframework/context/access/ContextSingletonBeanFactoryLocator.htmlにあるのでJavaDocで見ることができる。

EJBの章で触れられているように、Springの便利なEJBのための基底クラスは通常、非シングルトンBeanFactoryLocatorの実装を使う。これは、必要であればSingletonBeanFactoryLocatorや、ContextSingletonFactoryLocatorを使って簡単に差し替えることができる。