CDIプログラミングの基本(その2)
Producerメソッドによるインスタンス生成の制御
@Producesアノテーションをメソッド宣言に付加することで、メソッドの戻り値を指定したコンテキスト上にバインドすることができます。このようなメソッドをProducerメソッドと呼びます。Producerメソッドはインジェクション時に呼び出されます。Producerメソッドには限定子とスコープを指定することができます。
このProducerメソッドを使えば、実行時にTextTranslatorのBeanを切り替えるようなことも可能です。以下の例は、TextTranslator実装のクラス定義に@Japaneseや@Latinのような限定子を付ける代わりにProducerメソッドを使ってBeanインスタンスを生成しています。
上の TranslatorFactoryクラスは@ApplicationScopedで、Producerメソッドは@RequestScopedとなっています。TranslatorFactoryのインスタンスを管理するコンテキストとProducerが生成したインスタンスをバインドするコンテキストは異なりますので注意してください。
WeldサンプルにおけるProducerメソッドの例
Producerメソッドを使えば、Bean以外にも、プリミティブなデータ型やコレクションのような任意のオブジェクトをCDIのコンテキストにバインドすることができます。
Weldのjsf examplesにあるnumberguessサンプルでは以下のようなProducerメソッドを定義しています。これはProducerメソッドで@MaxNumber限定子を指定しています。スコープは明示的に指定していないのでデフォルトスコープである@Dependentが使われます(注意:@Dependentはインスタンスの共有をしない擬似コンテキストなので、ここにバインドされる値は毎回生成されることになります)。
Producerメソッドが生成した値をインジェクションするには、次のように@Injectと一緒に限定子@MaxNumberを指定します。
Producerの別のサンプルも紹介しましょう。Weldのjsf examplesにあるloginサンプルではDB検索の結果であるListをRequestコンテキストにバインドしています。この例では、@Namedを指定していますのでJSFのViewから名前でアクセス可能です(この場合、JSFからEL式でアクセスできる名前は"users"になります)。これはDB検索の結果をメモリ上にキャッシュしているのと同じ効果があります。スコープを@SessionScopedに変更すれば検索結果はセッションが存続する間メモリ上で保持され続けることになります。
これらのサンプルでは、インジェクションのときに@Injectと限定子の組み合わせで実行時にインスタンスを生成し、Producerが指定したスコープでライフサイクルの制御が可能なことを示してします。このProducerは様々な応用が可能で、とても強力な機能です。例えば、Java EE上で利用するリソースをProducerで実現しておけば、JSFのEL式や@Inject宣言によって簡単にリソースにアクセスできるようになります。そして利用者はリソースの生成や解放に関わる雑多な方法についてはまったく意識する必要はありません。
CDIのテーマはタイプセーフな疎結合
CDIでは、型や限定子を使ってインジェクションのときにインスタンスの解決をします。プログラム上、型や限定子のスペルが間違っていたらそれは当然コンパイル時にエラーになります。このようにCDIではできるだけインジェクションのエラーを早期に検出できるように設計されています。そして、インジェクションをするアプリケーションはインジェクトされるインスタンスの型や限定子しか意識しないので、疎結合連携が可能になります。
EJB3やJPAなどのJava EE 5以降に登場した仕様ではアノテーションが多用されています。CDIはアプリケーションがこのJava EEの世界を拡張できる枠組みを提供してくれます。
今回の記事ではアプリケーションが限定子という形でカスタムアノテーションを定義し、Producerを使うことによって、アプリケーションがインジェクション時に生成するインスタンスを制御できることを説明しました。連載の最終回となる次回は、CDIをベースにしたSeam3フレームワークについて概要を説明します。