前回は「JSR 299: Web Beans」についてEarly Draftを元にその概要を紹介した。今回は同仕様の肝であるWeb Beanコンポーネントを構成する各要素について細かく見ていこうと思う。ソースコード例はEarly Draftより部分的に引用している。
コンポーネントタイプ
コンポーネントタイプは、コンテナがそのWeb Beanコンポーネントの実装クラスを特定するために使用される。また、コンポーネントの優先度を決定するためにも使われる。コンポーネントタイプはアノテーションによって宣言することができ、JSR 299には@Componentと@Standardという2種類のタイプがあらかじめ用意されている。Web Beansコンテナによって提供されるコンポーネントはすべて@Standardであり、アプリケーションで宣言するコンポーネントにはリスト1のように@Componentを指定する。
リスト1 コンポーネントタイプの使用例
@Component
public class MyComponent { ... }
オリジナルのコンポーネントタイプを定義することもできる。その場合、@ComponentType、@Target、@Retentionなどのアノテーションを使ってリスト2のように宣言する。
リスト2 コンポーネントタイプの定義
@ComponentType
@Target({TYPE, METHOD})
@Retention(RUNTIME)
public @interface MyComponentType {}
実装クラスとプロデューサメソッド
Web Beanの実装クラスは、Web Beanコンポーネントの状態や振る舞いを決定する。Web Beanコンポーネントのプロデューサメソッドは、インジェクトされるオブジェクトのソースとして振る舞うメソッドを指す。プロデューサ・メソッドは@Producesアノテーションを用いてリスト3のように定義する。@AllProductsはバインディングアノテーション(後述)である。
リスト3 プロデューサメソッドの定義
@Component
@Stateless
public class ShopBean implements Shop {
@Produces @AllProducts
public List getAllProducts() { ... }
}
プロデューサメソッドはリスト4のようにインジェクトできる。
リスト4 プロデューサメソッドコンポーネントのインジェクト
@In @AllProducts List catalog;
APIタイプ
Web BeanのAPIタイプとは、クライアントから見たWeb Beanコンポーネントの型を指す。Web Beanコンポーネントは複数のAPIタイプを持つことができる。各Web BeanコンポーネントのAPIタイプは以下のようにして決まる。
- 実装クラスがセッションBeanでない場合、その実装クラスおよびスーパークラス、実装インタフェースがすべてAPIタイプとなる
- 実装クラスがセッションBeanの場合、すべてのローカルインタフェースとそのスーパーインタフェースがAPIタイプになるが、リモートインタフェースは含まれない
- プロデューサメソッドコンポーネントの場合、メソッドの戻り値の型とすべての実装インタフェースがAPIタイプとなる。戻り値の型がコンクリートクラスの場合、そのスーパークラスもAPIタイプに含まれる
バインディングアノテーション
バインディングアノテーションは、必要とするWeb Beanコンポーネントを特定するために独自に定義するアノテーションである。@BindingTypeアノテーションを用いてリスト5のように定義する。
リスト5 バインディングアノテーションの定義
@BindingType
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD})
public @interface AllProducts {}
定義したバインディングアノテーションは、Web Beanの実装クラスやプロデューサメソッドに指定することができる。そしてそれらのWeb Beanコンポーネントをインジェクトする際には、バインディングアノテーションによってソースとなるオブジェクトが特定される(リスト3はリスト4参照)。したがって、同じAPIタイプでもバインディングアノテーションが異なればソースのオブジェクトも異なる。
スコープ
すべてのWeb Beanコンポーネントはスコープを持っており、それによってライフサイクルと他のコンポーネントからの可視性が決まる。Web BeansではServlet仕様で定義される各スコープ、すなわちリクエストスコープ、アプリケーションスコープ、セッションスコープに対応した@RequestScoped、@ApplicationScoped、@SessionScopedというアノテーションが用意されており、これをWeb Beanコンポーネントに指定すればよい。その他に対話型のコンテキストに対応した@ConversationScopedがある。
また、@ScopeTypeアノテーションによって独自のスコープを定義することもできる。リスト6のようにする。
リスト6 スコープの定義
@ScopeType
@Target({TYPE, METHOD})
@Retention(RUNTIME)
public @interface MethodScoped {}
コンポーネント名
通常、Web Beanコンポーネントはコンポーネント名を持つ。これはUnified EL式の中でコンポーネントを特定するために使用される。デフォルトではコンポーネントの実装クラス名がそのままコンポーネント名となる。プロデューサメソッドの場合はメソッド名か、またはメソッド名がJavaBeansプロパティのGetter形式ならばそのプロパティ名がコンポーネント名となる。たとえばメソッド名が「getProducts」ならばコンポーネント名は「products」だ。
デフォルト以外のコンポーネント名を付けたい場合には、リスト7のように@Namedアノテーションで指定すればよい。
リスト7 コンポーネント名の指定
@Component
@Named("products")
public class ProductList implements DataModel { ... }
Web Beans仕様にはもうひとつ、pseudo-scopeと呼ばれるスコープがある。通常、2つのコンポーネントX、YがそれぞれコンポーネントZをインジェクトしていた場合、それぞれのインスタンスx、yは同じZのインスタンスへの参照を保持する。しかしpseudo-scopeを持つコンポーネントは、複数のコンポーネントでインスタンスが共有されない。そしてpseudo-scopeのコンポーネントのライフサイクルは、インジェクトされた各コンポーネントのライフサイクルに依存することになる。poseudo-scopeの指定には@Dependentアノテーションを利用する。
今回紹介したソースコードはすべてアノテーションを用いた例だが、JSR 299では同様の定義をXMLを用いて記述する方法も定められている。詳細はEarly Draftを参照していただきたい。JSR 299は2008年4月に正式リリースされる予定となっていたが、現在のところスケジュールは遅れ気味のようだ。いずれにせよJava EE 6のリリース (2008年第四半期に予定) までには正式仕様が決まるはず。Java EE 6を導入する予定のあるユーザは早い段階でチェックしておくといいだろう。