サンプルプログラムの構造
JSF画面の構造
最初に、translatorプログラムをもとにJSFとEJBの連携方法について見ていきましょう。CDIでは、JSFからEJBやPOJOなどのBeanにアクセスするには、EL式(Expression Language)内でBeanの名前を指定します。
translatorサンプルはJSFの画面から、翻訳を行うEJBを呼び出すという単純な構成になっています。JSFの画面のソース (home.xhtml) の中からEJBを呼び出しているのは、以下の3行です。
1 | <!--//--><
|
図3:CDIのBean定義 |
このTranslatorControllerBeanはクラス定義に3つのアノテーションが付いています。
- @StatefulはEJB3のアノテーションで、これがステートフルセッションBeanであることを示しています。
- @RequestScopedはCDIのアノテーションで、このBeanのスコープがRequestスコープであることを示しています。
- @Named("translator")はCDIのアノテーションで、JSFからEL式でアクセスするときのBeanの名前が"translator"であることを示しています。
JSF画面のEL式からTranslatorControllerBeanのプロパティとしてアクセスしていたtextやtranslatedTextについては、クラスにsetter、getterメソッドを定義しておきます。
CDIスコープの動作
translatorのサンプルを見ればわかるように、CDIを使うとJSFとEJBの連携はとても簡単です。あらかじめEJBに@Namedで名前を付けておけば、JSFのEL式経由でEJBのプロパティやメソッドにアクセスできるようになります。JSFの画面開発者はEJBの作成方法等の詳細については知る必要がありません。
前回の記事でご紹介したように、CDIではEJBのインスタンスのライフサイクル(生成、削除)をスコープによって制御することができます。translator BeanではスコープがRequestなので、このEJBはJSFのリクエストが発行される度に生成され、リクエストが完了すると自動的に削除されます。このライフサイクル管理の自動化によって、クライアントがEJBの実装を意識する必要がなくなり、JSF画面とEJBとの間で疎結合連携が可能になったのです。
このライフサイクルの動作を確認するために、TranslatorControllerBeanのクラス定義に図4のメソッドを追加し、標準出力にログを出力します。

|
図4:ログ出力用メソッドの追加(赤字の部分) |
これらはBeanインスタンスが生成されるときに@PostConstructメソッドが、削除されるときに@PreDestroyメソッドが呼び出されるようになっています。さらに、"Translate"ボタンが押されたときにもログを書くようにしましょう。このような状態でJBossASのログに出力されたものが以下になります。
1 | <!--//--><![CDATA[// ><!-- |
3 | 23:16:33,757 INFO [STDOUT] TranslatorControllerBean created |
4 | 23:16:33,768 INFO [STDOUT] TranslatorControllerBean translate executed |
5 | 23:16:33,774 INFO [STDOUT] TranslatorControllerBean destroyed |
これらのログは"Translate"ボタンを押す度に出力されますので、RequestスコープでBeanの生成・削除が実行されていることが確認できます。
CDIのインジェクション
CDIのDI (Dependency Injection)の部分は、依存注入する変数をアノテーションで示します。図3では、TranslatorControllerBeanの中で、TextTranslatorをインジェクションしているところがありますので以下に抜粋します。
1 | <!--//--><![CDATA[// ><!-- |
4 | private TextTranslator translator |
この@Injectは、インジェクションポイントを示すCDIのアノテーションです。この変数translatorにTextTranslator型のBeanインスタンスが注入されます。インジェクションが起こるタイミングは、このTranslatorControllerBeanが生成されるときです。ここで、TranslatorControllerBeanにログのメソッドを追加したのと同様に、TextTranslatorについてもログ出力メソッドを追加してみると、以下のようなログ出力を得ることができます。
1 | <!--//--><![CDATA[// ><!-- |
3 | 23:42:26,105 INFO [STDOUT] TextTranslator created |
4 | 23:42:26,105 INFO [STDOUT] TranslatorControllerBean created |
5 | 23:42:26,119 INFO [STDOUT] TranslatorControllerBean translate executed |
6 | 23:42:26,128 INFO [STDOUT] TextTranslator destroyed |
7 | 23:42:26,128 INFO [STDOUT] TranslatorControllerBean destroyed |
今回のtranslatorのサンプルでは、JSFとEJBの連携、Beanスコープによるライフサイクル管理、インジェクションをご紹介しました。JSF上でUIボタンを押す度、実行時にBeanインスタンスが作成され、インジェクションが起動されるというCDIの動きをご理解いただけたでしょうか。
translatorのサンプルではRequestスコープを使っていたので、複数のリクエストが同じBeanのインスタンスにアクセスするということはありませんでした。しかし、スコープをSessionスコープやApplicationスコープのようなより広いスコープに変更することで複数のリクエストをまたがって同じBeanのインスタンスを共有できるようになります。
このように、CDIのスコープは、Beanインスタンスのライフサイクルだけではなく、Beanインスタンス(つまり、システムの状態)をクライアント間で共有できる範囲も決定します。CDIはシステムの状態が動的に変化し続けることを前提に設計されており、その状態変化を積極的に管理するのです。
次回もCDIの基本について引き続きご紹介します。