JavaEE7をはじめよう(11) - CDI Beanの定義
前回の記事では、CDIの概要とインジェクションのサンプルを紹介した。
今回から2回に分けて、CDIの基本的な利用方法を紹介する。前半の今回は、CDI Beanの定義で指定するスコープについて解説する。
CDI Beanに指定できるクラス
CDI Beanに指定できるクラスには以下のようなものがある。これらのクラスに@Inject
アノテーションを指定すると、CDI Beanとしてプロパティへの注入が可能になる。
- デフォルトコンストラクタを持ち、final ではない Java クラス
- EJB
- データソース、スレッドプール(Java EE7より)などサーバー管理のリソース
- JPAの
EntityManager
- (Java EE7以降)
HTTPServletRequest
、HTTPServletResponse
最初の条件を見ればわかるように、自作を含む大抵のクラスをCDI Beanにすることができる。 自作クラスの場合、次に説明するスコープを検討する必要がある。
スコープの種類
スコープはCDI Beanのライフサイクルを決定するもので、CDI コンテナはスコープの指定にしたがってインスタンスの生成と破棄を行う。
ライフサイクルの管理はコンテナが行うので、自分でコンストラクタを呼んで作成したオブジェクトについては、コンテナは一切管理しない(スコープの管理やインジェクションは一切行われない)ことに注意する必要がある。
Webアプリケーションでよく使用する以下のスコープがアノテーションとして用意されている。
アノテーション | パッケージ | スコープのライフサイクル | 備考 |
---|---|---|---|
@Dependent |
javax.enterprise.context |
注入先のBeanのスコープに従う | デフォルト設定 |
@RequestScoped |
javax.enterprise.context |
リクエスト | |
@SessionScoped |
javax.enterprise.context |
セッション | |
@ApplicationScoped |
javax.enterprise.context |
アプリケーションの開始から終了まで | |
@ConversationScoped |
javax.enterprise.context |
リクエスト以上セッション以下の範囲で、開始・終了をアプリケーションで明示する | |
@ViewScoped |
javax.faces.view |
1つのViewが表示されている間 | JSF専用 |
@FlowScoped |
javax.faces.flow |
複数Viewの中で範囲を明示する | JSF専用 |
JSF専用のスコープについては、JSFアプリケーションでのみ有効となる。
また、JSFでも同名の@RequestScoped
, @SessionScoped
, @ApplicationScoped
, @ViewScoped
が定義されているので、混同しないように注意する必要がある。
(特に、@ViewScoped
は CDI ではjavax.faces.view
で、JSF ではjavax.faces.bean
となっており、パッケージ名も似通っている。)
@ConversationScoped
をJSF以外で使用する場合は、開始・終了の指示以外にパラメータの指定が必要となる。これに関しては今後の記事で解説する。
また、CDI管理対象から除外する@javax.enterprise.inject.Vetoed
アノテーションも存在する。このVetoed
アノテーションの使い方は次節で説明する。
beans.xmlと@Dependent
, @Vetoed
beans.xmlは CDIの設定ファイルである。
Java EE7では beans.xmlを作成しなくても、CDIはデフォルトで有効化される。
また、このファイルに記載する内容は、メソッドの実行前後に特定の処理を実行するインターセプターや、
複数候補の注入対象の実装クラスを決定するAlternativeの設定であり、それらを使用しないのならファイルは作成する必要がない。
(一方 Java EE6では中身が空であってもbeans.xmlを作成しないと CDIは有効化されない)。
またJava EE7で提供されたCDI 1.1 から、beans.xml にbean-discovery-mode
という属性が追加されている。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd" bean-discovery-mode="annotated"> </beans>
この設定値は、以下の通り、CDI Bean とするクラスの条件を変えるものだ。
annotated
- スコープアノテーションが付与されたクラスのみを CDI Beanとする。all
- クラスパス上の全てのクラスをCDI Beanとする。スコープアノテーションがないクラスは@Dependent
とみなす。none
- 全てのクラスを CDI Beanとしない。
スコープを指定していないクラスがCDI Bean となるかどうかは、annotated
か all
かによって決まる。
Java EE7において、 beans.xmlが無い場合のこの属性のデフォルト値は、annotated
である。
よってannotated
の状態で、スコープを指定せずにCDIの対象としたいクラスには、@Dependent
を指定する必要がある。
(Java EE6のCDI 1.0には bean-discovery-mode
は存在せず、all
と同じ挙動となっている。このため、Java EE6からJava EE7にアップデートする際は、bean-discovery-mode
の設定に留意する必要がある。)
特定のクラスだけをCDI対象から外す設定も可能だ。その場合は、bean-discovery-mode
をall
にして、CDI対象外にしたいクラスに@Vetoed
アノテーションを指定すればよい。
このように、beans.xmlのbean-discovery-mode
の内容によって@Dependent
と@Vetoed
アノテーションの使い方が異なることに注意が必要だ。
初期処理と後処理
CDI Bean には初期処理と後処理を行う方法がある。
初期処理を行うメソッドに@PostConstruct
を、後処理を行うメソッドには@PreDestroy
アノテーションを指定する。
メソッド名は問わない。
@SessionScoped public class Bean implements Serializable{ @Inject private SomeBean someBean; @PostConstruct public void prepare() { someBean.setName(""); } @PreDestroy public void after(){ System.out.println("destroy"); } }
初期処理は、CDIが対象Beanのコンストラクタを呼び出し、フィールドのインジェクションを行った後でCDIから呼ばれる。 よって、Bean 内のインジェクションされたフィールドを操作できる。
逆に、コンストラクタの中でインジェクション対象のフィールドを操作しても意味が無いので、そうしたフィールドの初期処理(や後処理)を行う必要があるなら、この仕組みを利用する必要がある。
まとめ
今回は、CDI Beanを定義するためのスコープと、初期処理および後処理について解説した。
次回は、CDI Beanを利用方法を解説する。
[前多 賢太郎]