アノテーションを詳しく理解しよう
では、@PostConstructや@MaxNumberや@Randomのアノテーションについて、さらに追って行きましょう。
初期化処理を指定する@PostConstruct
あるクラスのインスタンスを取得するとき、変数の初期化や設定に利用するファイルの読み込みなど、あらかじめ行っておきたい処理もあります。初期化の処理であることを宣言する@PostConstructは依存性の注入後、ManagedBeanインスタンスの初期化の際に実行するメソッドに対して使用します。この初期化処理はManagedBeanの設定されたスコープに応じたタイミングで初期化処理が実行されます。
今回はセッションに指定しているため、セッションが生成された後、CDIコンテナによって自動的に実行されることになります。先程のNumberGameクラスではreset()メソッドで@PostConstructを設定しているため、セッション成立後、初回起動時にreset()メソッドが実行されます。
インスタンス生成をメソッドで指定する@Produces
NumberGameクラスで指定した@MaxNumberや@Randomの2つのアノテーションは今回のアプリケーション用に作成した限定子です。@Producesで指定されたメソッドによって、CDIコンテナがインスタンスを生成するロジックを指定します。
前回紹介した@Injectと限定子はJavaクラスに対してのみ指定できますが、@Producesは個別のメソッドに対して指定します。@Producesを使うと、以下の特徴があります。
- @Injectと限定子だけではできなかったJava標準のプリミティブ型やコレクション型を返せる
- 動的にインスタンス生成を切り替えることができる(後述)
次の例ではプリミティブ型であるintを返すnext、getMaxNumberメソッドに対して@Producesアノテーションを付与しています。
package sample.beans; import java.io.Serializable; import javax.enterprise.context.ApplicationScoped; import javax.enterprise.inject.Produces; @ApplicationScoped …(1) public class Generator implements Serializable { private java.util.Random random = new java.util.Random( System.currentTimeMillis()); private int maxNumber = 100; java.util.Random getRandom() { return random; } /** * 次の乱数を取得する。 * @return */ @Produces …(2) @Random …(2) int next() { return getRandom().nextInt(maxNumber); } @Produces …(3) @MaxNumber …(3) int getMaxNumber() { return maxNumber; } }
(1)ではこのクラスをアプリケーションスコープで管理することを宣言します。アプリケーションスコープにした理由は、アプリケーションの初期化時のみ乱数の初期化を行うためです。
(2)(3)では、@Injectで指定した2つのアノテーションの提供元となる内容を@Producesと@Random、@MaxNumberで指定しています。
NumberGameクラスと@Injectで管理された機能との関係図は、以下のようになります。
@Injectや@Producedの橋渡しとなる@Randomや@MaxNumber限定子は、前回同様に作成ウィザードから作成します。まずは@MaxNumberから見てみましょう。
package sample.beans; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; import static java.lang.annotation.ElementType.*; import static java.lang.annotation.RetentionPolicy.RUNTIME; import javax.inject.Qualifier; // アノテーション適用させる対象。型、メソッド、パラメータ、フィールド。 @Target( { TYPE, METHOD, PARAMETER, FIELD }) …(1) // アノテーション対象オブジェクトの寿命。 // RUNTIME指定=ランタイムが存命中はインスタンスが有効。 @Retention(RUNTIME) …(2) // アノテーション定義の利用を宣言。 @Qualifier …(3) public @interface MaxNumber { }
必須となるのは(1)(2)(3)のアノテーションです。
(1)@Targetでは@MaxNumberアノテーションが適用される対象を定義します。定義はクラスなどの型、メソッド、パラメータ、フィールドから選択できます。
(2) @Retentionでは対象のオブジェクトの寿命を設定できます。RUNTIMEを指定することでランタイムが存在している間は有効であることを設定します。
(3)@Qualifierを宣言することでこの限定子が有効になります。
以上で@MaxNumberに関する設定は完了します。同様に@Randomについても限定子の宣言は次のようになります。
package sample.beans; …(省略)… //アノテーション適用させる対象。型、メソッド、パラメータ、フィールド。 @Target( { TYPE, METHOD, PARAMETER, FIELD }) //アノテーション対象オブジェクトの寿命。 @Retention(RUNTIME) // アノテーション定義の利用を宣言。 @Qualifier public @interface Random { }
この2つの限定子の定義を使って、CDIコンテナは@Injectや@Producesの次に宣言された@MaxNumberと@Randomの管理を行います。@Producesでインスタンスの生成メソッドを指定すると、このようにプリミティブ型の変数を生成するロジックも扱うことができます。