#CDI とは
Contexts and Dependency Injection の略。
Java EE 7 には ver 1.1 が含まれている。
JSR は 346。
DI (依存性の注入)に加えて、管理しているインスタンスのスコープの管理まで行ってくれる。
##CDI 誕生の経緯と JBoss Seam の変遷
CDI は、 JBoss が提供していた独自フレームワークである Seam が前身となっている。
Seam は日本語で「継ぎ目」という意味。
Java EE 5 の頃の JSF と EJB をシームレスに連携させることを目的に作られたのが、この Seam というフレームワーク。
この Seam の中で、 DI やコンテキストの管理を担っていたコアの部分が抽出され、 JSR として標準化されたものが CDI 1.0 (JSR299)になる。
参照実装は Weld で、 Red Hat によって開発されている。
JSR299 は Java EE 6 に取り込まれ、標準仕様となった。
コア部分が標準仕様として EE に取り込まれたあと、 Seam は CDI の拡張機能を提供するフレームワークとして開発されることになった(Seam 3)。
しかし、同じように CDI を拡張するサードパーティのフレームワークは、他にも存在していた(Apache MyFaces CODI、CDISource)。
せっかく CDI という形で仕様を一本化したのに、このままだと結局また分岐が進んでいくことになる。
このことを危惧した各フレームワークのコミュニティは、これらのフレームワークを1つにまとめ、新しく Apache DeltaSpike という Apache プロジェクトを立ち上げることにした。
現在は、この Apache DeltaSpike というプロジェクトで CDI の拡張機能が実装されている(例えば、 EE 6 環境でも EE 7 相当の機能 @Transactional
とかが使えるようになる拡張機能とか)。
##今後の CDI
はじめは JSF と EJB を繋ぐ目的で導入された CDI だが、 EE 7 ではそれ以外にも様々なコンポーネントを連携させる役割を担うようになっている(JAX-RS、Bean Validation などなど)。
今後も、 CDI を利用した連携の強化は進められていくらしい(Java Day Tokyo 2015 より)。
###JSR 365
次期 CDI のバージョンは 2.0 で、 Java EE 8 に取り入れられる予定になっている。
JSR は 365。
2.0 では、大きく次の機能強化が掲げられている。
- Java SE 環境でも使えるようにする。
- Java EE の他の仕様との、さらなる連携強化。
2016 年の 1Q でファイナルリリースを出す計画で開発が進んでいるらしい。
2.12 Please describe the anticipated schedule for the development of this specification.
- August 2014 Expert Group formed
- Q1 2015 Early Draft
- Q3 2015 Public Review
- Q1 2016 Final Release
#Hello World
実装
package sample.cdi.bean;
public interface MyInterface {
void method();
}
package sample.cdi.bean;
import javax.enterprise.context.Dependent;
@Dependent
public class HelloBean implements MyInterface {
@Override
public void method() {
System.out.println("Hello CDI!!");
}
}
package sample.cdi.ejb;
import javax.ejb.Stateless;
import javax.inject.Inject;
import sample.cdi.bean.HelloBean;
@Stateless
public class CdiEjb {
@Inject
private HelloBean helloBean;
public void hello() {
this.helloBean.method();
}
}
動作確認
情報: Hello CDI!!
説明
- Java EE 7 の場合、CDI を使いはじめるために特別な設定などをする必要はない。
- スコープを定義するアノテーション(
@Dependent
など)でクラスをアノテートすると、そのクラスは CDI コンテナの管理対象になる。 - コンテナに管理されているクラス上で、
@Inject
を使ってインスタンスをインジェクションできる。- EJB のインスタンスはコンテナで管理されているので、インジェクションできる。
#基本
##CDI 管理ビーン
CDI コンテナに管理されたクラスのことを、CDI 管理ビーン (CDI Managed Bean
)と呼ぶ。
###CDI 管理ビーンの条件
クラスを管理ビーンにするためには、次の条件を全て満たしている必要がある。
- static ではないインナークラス ではない こと。
- 具象クラス、または
@Decorator
でアノテートされていること。 - EJB でないこと。
-
javax.enterprise.inject.spi.Extension
インターフェースを実装していない。 - クラスまたは、そのクラスが属するパッケージが
@Vetoed
でアノテートされていない。 - 以下のいずれかのコンストラクタを持つこと。
- 引数なしのコンストラクタ。
-
@Inject
でアノテートされたコンストラクタ。
まぁ、要は普通に定義するクラスなら、コンストラクタにだけ注意すればだいたい CDI 管理ビーンにすることができる。
###CDI 管理ビーンを作成する
以下のいずれかのアノテーションでクラスをアノテートすることで、クラスを CDI 管理ビーンにできる。
-
@NormalScope
を継承したスコープアノテーション。@ApplicationScoped
@SessionScoped
@ConversationScoped
@RequestScoped
- その他自作アノテーション
@Dependent
@Interceptor
@Decorator
-
@Stereotype
を使った自作アノテーション
##インジェクション方法
CDI 管理ビーンをインジェクションするには、 @Inject
アノテーションを使用する。
また、具体的なインジェクション場所として、以下の3つの方法が存在する。
- フィールドに直接インジェクションする
- 初期化メソッドでインジェクションする
- コンストラクタでインジェクションする
実装
package sample.cdi.bean.injectpoint;
import javax.enterprise.context.Dependent;
import javax.inject.Inject;
@Dependent
public class Hoge {
@Inject
private Fuga fieldInjection;
private Fuga initializerMethodInjection;
private Fuga constructorInjection;
@Inject
public Hoge(Fuga fuga) {
System.out.println("constructor injection!!");
this.constructorInjection = fuga;
}
@Inject
public void setFuga(Fuga fuga) {
System.out.println("initializer method injection!!");
this.initializerMethodInjection = fuga;
}
@Override
public String toString() {
return "Hoge{" + "fieldInjection=" + fieldInjection + ", initializerMethodInjection=" + initializerMethodInjection + ", constructorInjection=" + constructorInjection + '}';
}
}
package sample.cdi.bean.injectpoint;
import javax.enterprise.context.Dependent;
@Dependent
public class Fuga {
@Override
public String toString() {
return "Fuga{" + this.hashCode() + "}";
}
}
package sample.cdi.ejb;
import javax.ejb.Stateless;
import javax.inject.Inject;
import sample.cdi.bean.injectpoint.Hoge;
@Stateless
public class InjectionPointEjb {
@Inject
private Hoge hoge;
public void execute() {
System.out.println(hoge);
}
}
###実行結果
情報: initializer method injection!!
情報: constructor injection!!
情報: Hoge{fieldInjection=Fuga{311564470}, initializerMethodInjection=Fuga{1165724746}, constructorInjection=Fuga{1765497502}}
説明
####フィールドに直接インジェクションする
@Dependent
public class Hoge {
@Inject
private Fuga fieldInjection;
- フィールドを
@Inject
でアノテートすることで、インスタンスをインジェクションできる。 - 可視性は
private
でも問題ない。 - セッターメソッドなどを定義する必要がない分実装がスッキリするが、単体テストはしづらくなる。
####初期化メソッドでインジェクションする
@Inject
public void setFuga(Fuga fuga) {
System.out.println("initializer method injection!!");
this.initializerMethodInjection = fuga;
}
- メソッドを
@Inject
でアノテートすることで、そのメソッド経由でインスタンスをインジェクションできる。 - メソッドの引数には、 CDI 管理ビーンを定義する。
####コンストラクタでインジェクションする
@Inject
public Hoge(Fuga fuga) {
System.out.println("constructor injection!!");
this.constructorInjection = fuga;
}
- コンストラクタを
@Inject
でアノテートすることで、そのコンストラクタ経由でインスタンスをインジェクションできる。 -
@Inject
でアノテートできるコンストラクタは、1つまで。 -
@Inject
でアノテートされたコンストラクタが存在する場合、 CDI のコンテナはそのコンストラクタ経由でインスタンスを生成する。 -
@Inject
でアノテートされたコンストラクタが存在しない場合は、デフォルトコンストラクタでインスタンスが生成される。 - イミュータブルなビーンを定義したい場合は、コンストラクタインジェクションを利用すると良い(寺田さん推奨)。
#スコープ
CDI 管理ビーンのインスタンスには生存期間が存在する。これを スコープ と呼ぶ。
標準では、以下のスコープが存在する。
スコープ | アノテーション | 生存期間 |
---|---|---|
Request | @RequestScoped |
1回 の HTTP リクエストの間のみ。 |
Session | @SessionScoped |
1つの HttpSession の間のみ。 |
Application | @Application |
Web アプリが起動している間ずっと。 |
Dependent | @Dependent |
インジェクション先のスコープを継承する。 |
Conversation | @ConversationScoped |
スコープの開始と終了をプログラマが指定する。 |
1回のリクエストの中で共有管理したいデータなどがあった場合、これまでは ThreadLocal
などを使ってドキドキしながら管理していたが、 @RequestScoped
を使えばそのへんの管理を全て CDI コンテナに任せられるようになる。
また、 @SessionScoped
を使えば、 HttpSession
などのサーブレット API に依存することなく、セッションに紐づく情報を管理できるようになる。
##基本動作の検証
実装
package sample.cdi.bean.scope;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
@ApplicationScoped
public class ApplicationScopedBean {
@Inject
private SessionScopedBean sessionBean;
@Inject
private DependentBean dependentBean;
@Override
public String toString() {
return "{this=" + hashCode() + ", sessionBean=" + sessionBean + ", dependentBean=" + dependentBean + "}";
}
}
package sample.cdi.bean.scope;
import java.io.Serializable;
import javax.enterprise.context.SessionScoped;
import javax.inject.Inject;
@SessionScoped
public class SessionScopedBean implements Serializable {
@Inject
private RequestScopedBean requestBean;
@Inject
private DependentBean dependentBean;
@Override
public String toString() {
return "{this=" + hashCode() + ", requestBean=" + requestBean + ", dependentBean=" + dependentBean + "}";
}
}
package sample.cdi.bean.scope;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
@RequestScoped
public class RequestScopedBean {
@Inject
private DependentBean dependentBean;
@Override
public String toString() {
return "{this=" + hashCode() + ", dependentBean=" + dependentBean + "}";
}
}
package sample.cdi.bean.scope;
import java.io.Serializable;
import javax.enterprise.context.Dependent;
@Dependent
public class DependentBean implements Serializable {
@Override
public String toString() {
return "{this=" + hashCode() + "}";
}
}
package sample.cdi.ejb;
import javax.ejb.Stateless;
import javax.inject.Inject;
import sample.cdi.bean.scope.ApplicationScopedBean;
import sample.cdi.bean.scope.RequestScopedBean;
import sample.cdi.bean.scope.SessionScopedBean;
@Stateless
public class ScopeEjb {
@Inject private ApplicationScopedBean asb;
@Inject private SessionScopedBean ssb;
@Inject private RequestScopedBean rsb;
public void execute() {
String msg =
"application scoped : " + asb + "\n"
+ "session scoped : " + ssb + "\n"
+ "request scoped : " + rsb + "\n"
;
System.out.println(msg);
}
}
- それぞれのスコープでアノテートしたクラスを用意している。
- 上位のスコープのクラスには、下位のスコープのクラスを
@Inject
でインジェクションしている。 - さらに、それぞれに
@Dependent
スコープのクラスもインジェクションしている。 -
toString()
メソッドで、自身とインジェクションされたインスタンスのハッシュ値を出力するようにしている。
動作確認
ブラウザから2回アクセス。
一旦 JSESSIONID を破棄し、再度2回アクセスする。
情報: application scoped : {this=1638042708, sessionBean={this=1913631888, requestBean={this=614601466, dependentBean={this=547193526}}, dependentBean={this=1081491714}}, dependentBean={this=1339217576}}
session scoped : {this=1913631888, requestBean={this=614601466, dependentBean={this=547193526}}, dependentBean={this=1081491714}}
request scoped : {this=614601466, dependentBean={this=547193526}}
情報: application scoped : {this=1638042708, sessionBean={this=1913631888, requestBean={this=353608602, dependentBean={this=1738509931}}, dependentBean={this=1081491714}}, dependentBean={this=1339217576}}
session scoped : {this=1913631888, requestBean={this=353608602, dependentBean={this=1738509931}}, dependentBean={this=1081491714}}
request scoped : {this=353608602, dependentBean={this=1738509931}}
※ここで、ブラウザ側の JSESSIONID を破棄
情報: application scoped : {this=1638042708, sessionBean={this=1412439363, requestBean={this=418688321, dependentBean={this=95479694}}, dependentBean={this=1792900043}}, dependentBean={this=1339217576}}
session scoped : {this=1412439363, requestBean={this=418688321, dependentBean={this=95479694}}, dependentBean={this=1792900043}}
request scoped : {this=418688321, dependentBean={this=95479694}}
情報: application scoped : {this=1638042708, sessionBean={this=1412439363, requestBean={this=1809844357, dependentBean={this=1876918561}}, dependentBean={this=1792900043}}, dependentBean={this=1339217576}}
session scoped : {this=1412439363, requestBean={this=1809844357, dependentBean={this=1876918561}}, dependentBean={this=1792900043}}
request scoped : {this=1809844357, dependentBean={this=1876918561}}
ちょっと見づらいので、表にまとめる。
ApplicationScopedBean
リクエスト数 | this |
session |
dependent |
---|---|---|---|
1 | 1638042708 | 1913631888 | 1339217576 |
2 | 1638042708 | 1913631888 | 1339217576 |
3 | 1638042708 | 1412439363 | 1339217576 |
4 | 1638042708 | 1412439363 | 1339217576 |
SessionScopedBean
リクエスト数 | this |
request |
dependent |
---|---|---|---|
1 | 1913631888 | 614601466 | 1081491714 |
2 | 1913631888 | 353608602 | 1081491714 |
3 | 1412439363 | 418688321 | 1792900043 |
4 | 1412439363 | 1809844357 | 1792900043 |
RequestScopedBean
リクエスト数 | this |
dependent |
---|---|---|
1 | 614601466 | 547193526 |
2 | 353608602 | 1738509931 |
3 | 418688321 | 95479694 |
4 | 1809844357 | 1876918561 |
説明
-
@ApplicationScoped
は、アプリケーションが起動している間が生存期間なので、常に同じインスタンスが利用される。- つまり、シングルトンということになる。
-
@SessionScoped
は、1つの HTTP セッションが有効な間だけ、同じインスタンスが利用される。- セッションごとにインスタンスが切り換わる。
-
@SessionScoped
でアノテートしたクラスは、Serializable
を実装しなければならない。
-
@RequestScoped
は、1つの HTTP リクエストごとに新しいインスタンスが生成され、利用される。 -
@Dependent
は、インジェクションした先と同じスコープが適用される。-
SessionScopedBean
にインジェクションする場合DependentBean
もセッションスコープになるので、Serializable
を実装しておく必要がある。
-
###プロキシがインジェクションされている
よくよく上記の表を見ると、 this
のハッシュ値は変わっていないのに、インジェクションされているインスタンスのハッシュ値だけが切り替わっていることに気づく。
リクエストの度にインジェクションされているインスタンスが差し替わっているのかというと、そういうわけではない。
実際にインジェクションされているインスタンスは、本物のインスタンスではなく、それをラップしたプロキシオブジェクトになる。
@Stateless
public class ScopeEjb {
@Inject private ApplicationScopedBean asb;
@Inject private SessionScopedBean ssb;
@Inject private RequestScopedBean rsb;
...
public void proxy() {
System.out.println("application scoped : " + this.asb.getClass());
System.out.println("session scoped : " + this.ssb.getClass());
System.out.println("request scoped : " + this.rsb.getClass());
}
}
情報: application scoped : class sample.cdi.bean.scope.ApplicationScopedBean$Proxy$_$$_WeldClientProxy
情報: session scoped : class sample.cdi.bean.scope.SessionScopedBean$Proxy$_$$_WeldClientProxy
情報: request scoped : class sample.cdi.bean.scope.RequestScopedBean$Proxy$_$$_WeldClientProxy
-
@Inject
でインジェクションしたインスタンスは、本体ではなくプロキシになる。 - プロキシはメソッドが呼ばれた時点で実際のインスタンスを取得し、本体の処理を呼び出している。
- この仕組のおかげで、広いスコープのインスタンスに、狭いスコープのインスタンスをインジェクションできるようになっている。
##Conversation Scope
@ConversationScoped
は、スコープの開始と終了をプログラムで指定する。
@RequestScoped
より長くて、 @SessionScoped
よりは短いスコープがほしい場合に利用する。
主に、 JSF で複数画面に跨った情報の管理をしたい場合に利用される。
実装
package sample.cdi.bean.scope;
import java.io.Serializable;
import javax.enterprise.context.ConversationScoped;
@ConversationScoped
public class ConversationScopedBean implements Serializable {
@Override
public String toString() {
return "{this=" + hashCode() + "}";
}
}
package sample.cdi.ejb;
import javax.ejb.Stateless;
import javax.enterprise.context.Conversation;
import javax.inject.Inject;
import sample.cdi.bean.scope.ConversationScopedBean;
...
@Stateless
public class ScopeEjb {
...
@Inject
private ConversationScopedBean csb;
@Inject
private Conversation conv;
public void beginConversation() {
this.conv.begin();
System.out.println("begin conversation scope. cid = " + this.conv.getId());
System.out.println("csb=" + this.csb);
}
public void conversation() {
System.out.println("cid = " + this.conv.getId());
System.out.println("csb=" + this.csb);
}
public void endConversation() {
System.out.println("end conversation scope. cid = " + this.conv.getId());
System.out.println("csb=" + this.csb);
this.conv.end();
}
}
ScopeEjb
の各メソッドには、 http://localhost:8080/cdi/scope/<メソッド名>
でアクセスできるようにしておく。
動作確認
Web ブラウザを開き、まずは http://localhost:8080/cdi/scope/beginConversation
にアクセスする。
情報: begin conversation scope. cid = 1
情報: csb={this=246187740}
次に、同じ Web ブラウザのウィンドウから http://localhost:8080/cdi/scope/conversation?cid=1
に何回かアクセスする(セッションが引き継がれるようにする)。
情報: cid = 1
情報: csb={this=246187740}
情報: cid = 1
情報: csb={this=246187740}
情報: cid = 1
情報: csb={this=246187740}
http://localhost:8080/cdi/scope/endConversation?cid=1
にアクセスする。
情報: end conversation scope. cid = 1
情報: csb={this=246187740}
最後に http://localhost:8080/cdi/scope/conversation?cid=1
にアクセスする。
警告: javax.ejb.EJBException
...
Caused by: org.jboss.weld.context.NonexistentConversationException: WELD-000321: No conversation found to restore for id 1
...
説明
-
@ConversationScoped
の開始と終了は、Conversation
のbegin()
とend()
メソッドで制御する。-
Conversation
のインスタンス自体は CDI のコンテナによって生成されるので@Inject
を使って取得する。
-
- Conversation スコープには、スコープの状態が2種類存在する。
- 1つが「一時的(
transient
)なスコープ」で、もう1つが「長期間(long-running
)有効なスコープ」。 -
transient
なスコープは、@RequestScoped
と同じで HTTP リクエストが終了すると破棄される。 - 一方、
long-running
なスコープは複数のリクエスト間で維持される。
- 1つが「一時的(
- デフォルトは
transient
なスコープが採用される。 -
long-running
にするためには、Conversation#begin()
メソッドを使用する。 -
begin()
メソッドでlong-running
なスコープを開始すると、一意な ID が発行される。 - 次回のリクエストのときに、その ID を
cid
というパラメータと共にサーバーに送ると、前回リクエストしたときのスコープが引き継がれる。- Conversation スコープは HTTP セッションごとに管理される。よって、別のセッションで発行された
cid
を異なるセッションで指定しても、スコープが引き継がれることはない。 -
cid
を指定しなかった場合、デフォルトのtransient
扱いになり、新たなスコープが生成される。 - 仕様上、
cid
で指定されなくなった過去のlong-running
状態のスコープは、コンテナが任意に破棄しても構わないことになっている。
- Conversation スコープは HTTP セッションごとに管理される。よって、別のセッションで発行された
6.7.4. Conversation context lifecycle
The container is permitted to arbitrarily destroy any long-running conversation that is associated
with no current Servlet request, in order to conserve resources.
-
long-running
なスコープの終了は、Conversation#end()
メソッドを使用する。 - 一度終了した
cid
は、当然再利用できない。
####JSF で利用した場合
JSF で @ConversationScoped
を利用した場合、この cid
のやり取りが自動で行われるようになる。
なので、実装者は特に cid
の存在を意識する必要はない。
#インスタンスが生成されるときと破棄されるときに処理を挟む
例えば、外部リソースを保持するビーンが存在したとする。
この仕組を利用すれば、そのビーンのインスタンスが生成されるときにリソースとの接続を確立させ、破棄されるときに接続を解放するように実装できる。
実装
package sample.cdi.bean.scope;
import java.io.Serializable;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.enterprise.context.SessionScoped;
@SessionScoped
public class SessionScopedBean implements Serializable {
@PostConstruct
public void postConstruct() {
System.out.println("[Session Scope] post construct : " + hashCode());
}
@PreDestroy
public void preDestroy() {
System.out.println("[Session Scope] pre destroy : " + hashCode());
}
}
package sample.cdi.bean.scope;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.enterprise.context.RequestScoped;
@RequestScoped
public class RequestScopedBean {
@PostConstruct
public void postConstruct() {
System.out.println("[Request Scope] post construct : " + hashCode());
}
@PreDestroy
public void preDestroy() {
System.out.println("[Request Scope] pre destroy : " + hashCode());
}
}
package sample.cdi.ejb;
import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.servlet.http.HttpSession;
import sample.cdi.bean.scope.RequestScopedBean;
import sample.cdi.bean.scope.SessionScopedBean;
@Stateless
public class LifeCycleCallbackMethodEjb {
@Inject private HttpSession session;
@Inject private SessionScopedBean ssb;
@Inject private RequestScopedBean rsb;
public void execute() {
ssb.toString();
rsb.toString();
System.out.println("LifeCycleCallbackMethodEjb#execute()");
}
public void endSession() {
System.out.println("LifeCycleCallbackMethodEjb#endSession()");
this.session.invalidate();
}
}
動作確認
Web ブラウザで LifeCycleCallbackMethodEjb#execute()
が実行される URL に何回かアクセスする。
情報: [Session Scope] post construct : 216269762
情報: [Request Scope] post construct : 1146100374
情報: LifeCycleCallbackMethodEjb#execute()
情報: [Request Scope] pre destroy : 1146100374
情報: [Request Scope] post construct : 1929012709
情報: LifeCycleCallbackMethodEjb#execute()
情報: [Request Scope] pre destroy : 1929012709
LifeCycleCallbackMethodEjb#endSession()
が実行される URL にアクセスする。
情報: LifeCycleCallbackMethodEjb#endSession()
情報: [Session Scope] pre destroy : 216269762
説明
-
@PostConstruct
でアノテートされたメソッドは、インスタンスが生成されたときにコールバックされる。 -
@PreDestroy
でアノテートされたメソッドは、インスタンスが破棄されるときにコールバックされる。
##CDI 管理ビーンの初期化処理は、 @PostConstruct
でアノテートしたメソッドで行わなければならない
コンテナは、コンストラクタを使って CDI 管理ビーンのインスタンスを生成する。
ということは、わざわざ @PostConstruct
を使わなくても、コンストラクタで初期化処理を書くこともできるように思える。
しかし、 CDI 管理ビーンの初期化処理は、コンストラクタに書いてはいけない。
なぜなら、コンストラクタが実行された段階では、まだ依存性の注入が完了していない可能性があるからだ。
もし、 @Inject
でフィールドインジェクションする予定のインスタンスをコンストラクタ内で使用してしまうと、最悪 NullPointerException
が発生する。
一方、 @PostConstruct
は、全ての依存関係の注入が完了した後でコールバックされるので、こういった問題が起きない。
よって、 CDI 管理ビーンの初期化処理は、コンストラクタではなく @PostConstruct
でアノテートしたメソッドで行うのが良い。
#Producer メソッド
- スコープアノテーションでクラスを直接アノテートできず、コンテナに自動登録できない。
- 同じクラスのインスタンスを複数生成し、コンテナに登録したい。
こんな場合は、 Producer メソッドが利用できる。
※後者の場合は、限定子を利用する必要がある。
##基本
実装
package sample.cdi.bean.producer;
public class Hoge {
private String name;
public Hoge(String name) {
this.name = name;
}
@Override
public String toString() {
return "Hoge{" + "name=" + name + ", hash=" + hashCode() + "}";
}
}
package sample.cdi.bean.producer;
import javax.ejb.Stateless;
import javax.enterprise.inject.Produces;
@Stateless
public class HogeFactory {
@Produces
public Hoge getHoge() {
System.out.println("getHoge()");
return new Hoge("hoge factory");
}
}
package sample.cdi.ejb;
import javax.ejb.Stateless;
import javax.inject.Inject;
import sample.cdi.bean.producer.Hoge;
@Stateless
public class ProducerEjb {
@Inject
private Hoge hoge;
public void execute() {
System.out.println(hoge);
System.out.println(hoge.getClass());
}
}
動作確認
ProducerEjb#execute()
が実行されるように何回かアクセスする。
情報: getHoge()
情報: Hoge{name=hoge factory, hash=309349054}
情報: class sample.cdi.bean.producer.Hoge
情報: Hoge{name=hoge factory, hash=309349054}
情報: class sample.cdi.bean.producer.Hoge
情報: Hoge{name=hoge factory, hash=309349054}
情報: class sample.cdi.bean.producer.Hoge
説明
@Produces
public Hoge getHoge() {
System.out.println("getHoge()");
return new Hoge("hoge factory");
}
-
@Produces
でメソッドをアノテートすることで、そのメソッドが返却した値をインジェクションさせられるようになる。- このメソッドを Producer メソッドと呼ぶ。
- セッションビーンだけでなく、 CDI 管理ビーンでも Producer メソッドを定義できる。
- 単純に
@Produces
でアノテートしただけの Producer メソッドから生成されたビーンは、プロキシでラップされない。
##スコープを指定する
Producer メソッドで定義したインスタンスに、スコープを持たせることができる。
実装
package sample.cdi.bean.producer;
import javax.ejb.Stateless;
import javax.enterprise.inject.Produces;
+ import javax.enterprise.context.RequestScoped;
@Stateless
public class HogeFactory {
- @Produces
+ @Produces @RequestScoped
public Hoge getHoge() {
System.out.println("getHoge()");
return new Hoge("hoge factory");
}
}
package sample.cdi.bean.producer;
public class Hoge {
private String name;
+ public Hoge() {}
public Hoge(String name) {
this.name = name;
}
@Override
public String toString() {
return "Hoge{" + "name=" + name + ", hash=" + hashCode() + "}";
}
}
動作確認
情報: getHoge()
情報: Hoge{name=hoge factory, hash=32172459}
情報: class sample.cdi.bean.roducer.Hoge$Proxy$_$$_WeldClientProxy
情報: getHoge()
情報: Hoge{name=hoge factory, hash=1343265552}
情報: class sample.cdi.bean.producer.Hoge$Proxy$_$$_WeldClientProxy
情報: getHoge()
情報: Hoge{name=hoge factory, hash=773997409}
情報: class sample.cdi.bean.producer.Hoge$Proxy$_$$_WeldClientProxy
説明
-
@Produces
に加えて、スコープアノテーションでメソッドをアノテートすることで、ビーンのスコープを指定できる。 - この場合、
@Inject
でインジェクションされるインスタンスは、プロキシオブジェクトになる。- コンテナがプロキシを生成できるようにするため、デフォルトコンストラクタを追加している。
##Producer フィールド
初期化処理が存在せず、インスタンスを new
するだけで良いなら、 Producer フィールドを使う方法もある。
実装
package sample.cdi.bean.producer;
public class Fuga {
private String name;
public Fuga(String name) {
this.name = name;
}
@Override
public String toString() {
return "Fuga{" + "name=" + name + ", hash=" + hashCode() + "}";
}
}
package sample.cdi.bean.producer;
import javax.enterprise.context.Dependent;
import javax.enterprise.inject.Produces;
@Dependent
public class FugaFactory {
@Produces
private Fuga fuga = new Fuga("fuga factory");
}
package sample.cdi.ejb;
import javax.ejb.Stateless;
import javax.inject.Inject;
import sample.cdi.bean.producer.Fuga;
@Stateless
public class ProducerEjb {
...
@Inject
private Fuga fuga;
public void field() {
System.out.println(fuga);
System.out.println(fuga.getClass());
}
}
動作確認
情報: Fuga{name=fuga factory, hash=415012830}
情報: class sample.cdi.bean.producer.Fuga
- フィールドを
@Produces
でアノテートすることで、そのフィールドの値を他のビーンなどにインジェクションできるようになる。 - だいたい Producer フィールドと同じ振る舞いをする。
##Disposer メソッド
Producer メソッド(フィールド)によって生成されたビーンは、 @PreDestroy
などのコールバックメソッドが利用できない。
代わりに、 Disposer メソッドというのが利用できる。
実装
package sample.cdi.bean.producer;
import javax.annotation.PreDestroy;
public class Piyo {
@PreDestroy
public void preDestroy() {
System.out.println("Piyo#preDestroy()");
}
@Override
public String toString() {
return "Piyo{" + hashCode() + "}";
}
}
package sample.cdi.bean.producer;
import javax.ejb.Stateless;
import javax.enterprise.context.RequestScoped;
import javax.enterprise.inject.Disposes;
import javax.enterprise.inject.Produces;
@Stateless
public class PiyoFactory {
@Produces @RequestScoped
public Piyo getPiyo() {
return new Piyo();
}
public void disposerMethod(@Disposes Piyo piyo) {
System.out.println("PiyoFactory#disposerMethod()");
System.out.println("piyo = " + piyo);
System.out.println("piyo.class = " + piyo.getClass());
}
}
package sample.cdi.ejb;
import javax.ejb.Stateless;
import javax.inject.Inject;
import sample.cdi.bean.producer.Piyo;
@Stateless
public class ProducerEjb {
...
@Inject
private Piyo piyo;
public void disposer() {
System.out.println("ProducerEjb#disposer()");
System.out.println("piyo = " + piyo);
System.out.println("piyo.class = " + piyo.getClass());
}
}
動作確認
情報: ProducerEjb#disposer()
情報: piyo = Piyo{1985500044}
情報: piyo.class = class sample.cdi.bean.producer.Piyo$Proxy$_$$_WeldClientProxy
情報: PiyoFactory#disposerMethod()
情報: piyo = Piyo{1985500044}
情報: piyo.class = class sample.cdi.bean.producer.Piyo
説明
- Producer メソッド(フィールド)で生成したインスタンスは、
@PreDestroy
でアノテートされたメソッドを持っていてもコールバックされない。 - Producer メソッド(フィールド)を定義したのと同じクラス内に、 Disposer メソッドを定義できる。
- Disposer メソッドは、以下の条件を全て満たすメソッドがコンテナによって自動で検出される。
- Producer メソッド(フィールド)によって生成されたクラスと同じ型を引数に受け取る。
- その引数が、
@Disposes
でアノテートされている。
- Disposer メソッドは、 Producer メソッド(フィールド)で生成されたインスタンスが破棄されるときにコールバックされる。
- Disposer メソッドに渡されるインスタンスは、プロキシでラップされていない。素の状態になっている。
##メタ情報を参照する
インジェクション先のメタ情報を、 Producer メソッドで参照することができる。
よく利用される方法として、ロガーのインスタンスを Producer メソッドで生成するときに、インジェクション先の Class
オブジェクトを取得してロガーに渡す、というものがある。
実装
package sample.cdi.bean.metadata;
public class Hoge {
}
package sample.cdi.bean.metadata;
import java.lang.reflect.Member;
import javax.enterprise.context.Dependent;
import javax.enterprise.inject.Produces;
import javax.enterprise.inject.spi.InjectionPoint;
@Dependent
public class HogeFactory {
@Produces
public Hoge getHoge(InjectionPoint ip) {
System.out.println("getHoge()");
Member member = ip.getMember();
System.out.println("member = " + member);
System.out.println("member.class = " + member.getClass());
return new Hoge();
}
}
package sample.cdi.ejb;
import javax.ejb.Stateless;
import javax.inject.Inject;
import sample.cdi.bean.metadata.Hoge;
@Stateless
public class MetadataEjb {
@Inject
private Hoge hoge;
public void execute() {
}
}
動作確認
情報: getHoge()
情報: member = private sample.cdi.bean.metadata.Hoge sample.cdi.ejb.MetadataEjb.hoge
情報: member.class = class java.lang.reflect.Field
説明
-
InjectionPoint
を引数で受け取るようにして Producer メソッドを定義する。 -
InjectionPoint
からは、インジェクション先の情報やインジェクションしているビーンのメタ情報を取得できる。
#限定子
同じインターフェースを実装した CDI 管理ビーンが複数存在したとする。
そのインターフェースの型で @Inject
を使い依存性の注入をした場合、コンテナは複数存在する管理ビーンの内、どのビーンを採用すればいいのか判断できない(エラーになる)。
これを解決するには、限定子(Qualifier
)と呼ばれるアノテーションを利用する。
##基本
実装
ビーン関連
package sample.cdi.bean.qualifier;
public interface MyInterface {
}
package sample.cdi.bean.qualifier;
import javax.enterprise.context.Dependent;
@Dependent @HogeHoge
public class Hoge implements MyInterface {
@Override
public String toString() {
return "Hoge{" + hashCode() + '}';
}
}
package sample.cdi.bean.qualifier;
import javax.enterprise.context.Dependent;
@Dependent @FugaFuga
public class Fuga implements MyInterface {
@Override
public String toString() {
return "Fuga{" + hashCode() + '}';
}
}
限定子
package sample.cdi.bean.qualifier;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.inject.Qualifier;
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({
ElementType.TYPE,
ElementType.FIELD,
ElementType.METHOD,
ElementType.PARAMETER
})
public @interface HogeHoge {
}
package sample.cdi.bean.qualifier;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.inject.Qualifier;
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({
ElementType.TYPE,
ElementType.FIELD,
ElementType.METHOD,
ElementType.PARAMETER
})
public @interface FugaFuga {
}
動作確認用EJB
package sample.cdi.ejb;
import javax.ejb.Stateless;
import javax.inject.Inject;
import sample.cdi.bean.qualifier.FugaFuga;
import sample.cdi.bean.qualifier.HogeHoge;
import sample.cdi.bean.qualifier.MyInterface;
@Stateless
public class QualifierEjb {
@Inject @HogeHoge
private MyInterface hoge;
private MyInterface fuga;
@Inject
public void setFuga(@FugaFuga MyInterface fuga) {
this.fuga = fuga;
}
public void execute() {
System.out.println("hoge=" + hoge);
System.out.println("fuga=" + fuga);
}
}
動作確認
情報: hoge=Hoge{1614742928}
情報: fuga=Fuga{354651225}
説明
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({
ElementType.TYPE,
ElementType.FIELD,
ElementType.METHOD,
ElementType.PARAMETER
})
public @interface HogeHoge {
}
- 限定子は、普通にアノテーションを定義したうえで、以下のメタアノテーションでアノテートすることで作成できる。
@Qualifier
-
@Retention
-
value
には、RetentionPolicy.RUNTIME
を設定する。
-
-
@Target
-
TYPE
,FILED
,METHOD
,PARAMETER
は、限定子を使う可能性がある場所。
-
@Dependent @HogeHoge
public class Hoge implements MyInterface {
- まずは、 CDI 管理ビーンの方を作成した限定子でアノテートする。
- これで、
MyInterface
型でかつ@HogeHoge
限定子を指定された場合はHoge
型で解決する、ということをコンテナに教えることができる。
@Inject @HogeHoge
private MyInterface hoge;
private MyInterface fuga;
@Inject
public void setFuga(@FugaFuga MyInterface fuga) {
this.fuga = fuga;
}
- インジェクション先は、
@Inject
と作成したアノテーションで対象をアノテートする。 - メソッド(コンストラクタ)インジェクションするときは、引数を限定子でアノテートする。
##属性を含めて適用を判定させる
アノテーションが属性を持つ場合、その属性値も含めてインジェクション対象のビーン候補が選択される。
これを利用すれば、インジェクション対象を柔軟に、かつ型安全な方法で指定できるようになる。
実装
package sample.cdi.bean.qualifier.attribute;
public enum MyBeanType {
HOGE,
FUGA,
}
package sample.cdi.bean.qualifier.attribute;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.inject.Qualifier;
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({
ElementType.TYPE,
ElementType.FIELD,
ElementType.METHOD,
ElementType.PARAMETER
})
public @interface MyQualifier {
MyBeanType value() default MyBeanType.HOGE;
}
package sample.cdi.bean.qualifier.attribute;
import javax.enterprise.context.Dependent;
@Dependent @MyQualifier
public class Hoge implements MyInterface {
}
package sample.cdi.bean.qualifier.attribute;
import javax.enterprise.context.Dependent;
@Dependent @MyQualifier(MyBeanType.FUGA)
public class Fuga implements MyInterface {
}
package sample.cdi.ejb;
import javax.ejb.Stateless;
import javax.inject.Inject;
import sample.cdi.bean.qualifier.attribute.MyBeanType;
import sample.cdi.bean.qualifier.attribute.MyInterface;
import sample.cdi.bean.qualifier.attribute.MyQualifier;
@Stateless
public class QualifierAttributeEjb {
@Inject @MyQualifier
private MyInterface obj1;
@Inject @MyQualifier(MyBeanType.FUGA)
private MyInterface obj2;
public void execute() {
System.out.println("obj1.class = " + obj1.getClass().getSimpleName());
System.out.println("obj2.class = " + obj2.getClass().getSimpleName());
}
}
動作確認
情報: obj1.class = Hoge
情報: obj2.class = Fuga
説明
public @interface MyQualifier {
MyBeanType value() default MyBeanType.HOGE;
}
- 限定子を作成し、
value
にビーンの種類を指定するための列挙型を指定する。 - デフォルトで
HOGE
が適用されるようにしている。
// Hoge.java
@Dependent @MyQualifier
public class Hoge implements MyInterface {
// Fuga.java
@Dependent @MyQualifier(MyBeanType.FUGA)
public class Fuga implements MyInterface {
-
Hoge
をアノテートしている@MyQualifier
はデフォルトなのでMyBeanType.HOGE
が、
Fuga
をアノテートしている@MyQualifier
にはMyBeanType.FUGA
が適用されている。 - これによって、
@MyQualifier(MyBeanType.HOGE)
とすればHoge
がインジェクションされ、
@MyQualifier(MyBeanType.FUGA)
とすればFuga
がインジェクションされるようになる。 - 列挙型を使うことで、選択肢がどれだけ存在するかが明確になり、間違った値を渡すこともなくなるメリットがある(
String
とかを使っちゃうと、何でも渡せてしまうので危険)。
##ビルトインの限定子
CDI には、次の限定子が標準で組み込まれている。
@Default
@Any
@Named
@New
CDI を使っていると、依存解決に失敗したときにエラーメッセージでお目にかかることの多いこれらの限定子について、その用法をまとめる。
###@Named
@Named
は、文字列による名前指定でインジェクションするビーンを解決するための限定子。
@Named
を使うことで、 CDI 管理ビーンに名前を付けることができる。
実装
ビーン関連
package sample.cdi.bean.qualifier.builtin.named;
public interface MyInterface {
}
package sample.cdi.bean.qualifier.builtin.named;
import javax.enterprise.context.Dependent;
import javax.inject.Named;
@Dependent @Named("HOGE")
public class Hoge implements MyInterface {
}
package sample.cdi.bean.qualifier.builtin.named;
import javax.enterprise.context.Dependent;
import javax.inject.Named;
@Dependent @Named
public class Fuga implements MyInterface{
}
動作確認用EJB
package sample.cdi.ejb;
import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.inject.Named;
import sample.cdi.bean.qualifier.builtin.named.MyInterface;
@Stateless
public class NamedEjb {
@Inject @Named("HOGE")
private MyInterface hoge;
@Inject @Named("fuga")
private MyInterface fuga;
public void execute() {
System.out.println(" hoge.class = " + hoge.getClass());
System.out.println(" fuga.class = " + fuga.getClass());
}
}
動作確認
情報: hoge.class = class sample.cdi.bean.qualifier.builtin.named.Hoge
情報: fuga.class = class sample.cdi.bean.qualifier.builtin.named.Fuga
説明
-
@Named
でアノテートし、value
にビーンの名前を指定する。 -
value
を省略した場合は、クラス名の先頭を小文字にしたものが名前になる(Fuga
→fuga
)。 -
@Inject
でインジェクションするときに、同じく@Named
で対象をアノテートし、value
でビーンを特定するための名前を指定する。 -
@Named
をインジェクション先で使用するのは推奨されていない。- 通常は、 JSF などで EL 式を書くときにのみ、参照名として
@Named
で設定した名前を使用する。
- 通常は、 JSF などで EL 式を書くときにのみ、参照名として
###@Any
と @Default
CDI 回りのエラーメッセージで一度はお目にかかる限定子。
あまりにもよく見るうえによく分からないので、これを見ただけで嫌な気分になる(自分だけ?)。
これらの限定子の関係は、ややこしいようで理解してしまえばそんなに複雑ではない。
まず、それぞれの定義から。
####@Any
- 限定子の有無に関わらず、全ての CDI 管理ビーンに自動で設定される限定子。
####@Default
- 限定子を付けていない場合に、 CDI 管理ビーンに自動で設定される限定子。
- ただし、
@Named
だけは例外で、これを設定していても@Default
が一緒に自動設定される。 - 明示的に設定することも可能。
####例
||
の上下は、意味的に同じになる。
@Dependent
public class Hoge {
||
@Depndent @Any @Default
public class Hoge {
@Dependent @Named
public class Fuga {
||
@Dependent @Named @Any @Default
public class Fuga {
@Dependent @MyQualifier
public class Piyo {
||
@Dependent @MyQualifier @Any
public class Piyo {
※MyQualifier
は自作の限定子。
図に表すと、以下のような感じ。
※この状態になると、 Hoge
クラスはインジェクションできなくなる(@Default
だけでは Fuga
も候補になってしまい、ビーンを特定できないため)。
####実験
package sample.cdi.bean.qualifier.defaultany;
public interface MyInterface {
}
package sample.cdi.bean.qualifier.defaultany;
import javax.enterprise.context.Dependent;
@Dependent
public class DefaultBean implements MyInterface {
@Override
public String toString() {
return "DefaultBean{" + hashCode() + '}';
}
}
package sample.cdi.bean.qualifier.defaultany;
import javax.enterprise.context.Dependent;
@Dependent @MyQualifier
public class QualifiedBean implements MyInterface {
@Override
public String toString() {
return "QualifiedBean{" + hashCode() + '}';
}
}
各ビーンの関係は、
こんな感じ。
package sample.cdi.ejb;
import javax.ejb.Stateless;
import javax.enterprise.inject.Any;
import javax.enterprise.inject.Default;
import javax.inject.Inject;
import sample.cdi.bean.qualifier.defaultany.MyInterface;
import sample.cdi.bean.qualifier.defaultany.MyQualifier;
@Stateless
public class DefaultAnyEjb {
@Inject
private MyInterface onlyInject;
@Inject @Default
private MyInterface withDefault;
@Inject @Default @Any
private MyInterface withDefaultAndAny;
@Inject @MyQualifier
private MyInterface withQualifier;
public void execute() {
System.out.println("onlyInject.class = " + onlyInject.getClass().getSimpleName());
System.out.println("withDefault.class = " + withDefault.getClass().getSimpleName());
System.out.println("withDefaultAndAny.class = " + withDefaultAndAny.getClass().getSimpleName());
System.out.println("withQualifier.class = " + withQualifier.getClass().getSimpleName());
}
}
情報: onlyInject.class = DefaultBean
情報: withDefault.class = DefaultBean
情報: withDefaultAndAny.class = DefaultBean
情報: withQualifier.class = QualifiedBean
###@New
このアノテーションをインジェクションポイントで利用すると、スコープが強制的に Dependent になる。
実装
package sample.cdi.bean.qualifier;
import javax.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class ApplicationScopedBean {
@Override
public String toString() {
return "ApplicationScopedBean{" + hashCode() + '}';
}
}
package sample.cdi.bean.qualifier;
import javax.enterprise.context.RequestScoped;
import javax.enterprise.inject.New;
import javax.inject.Inject;
@RequestScoped
public class RequestScopedBean {
@Inject
private ApplicationScopedBean normalBean;
@Inject @New
private ApplicationScopedBean newBean;
@Override
public String toString() {
return "RequestScopedBean{" + hashCode() + ", normalBean=" + normalBean + ", newBean=" + newBean + '}';
}
}
package sample.cdi.ejb;
import javax.ejb.Stateless;
import javax.inject.Inject;
import sample.cdi.bean.qualifier.RequestScopedBean;
@Stateless
public class NewQualifierEjb {
@Inject
private RequestScopedBean bean;
public void execute() {
System.out.println(bean);
}
}
動作確認
情報: RequestScopedBean{1322601645, normalBean=ApplicationScopedBean{1740782693}, newBean=ApplicationScopedBean{927056158}}
情報: RequestScopedBean{325806349, normalBean=ApplicationScopedBean{1740782693}, newBean=ApplicationScopedBean{246048323}}
情報: RequestScopedBean{1410382906, normalBean=ApplicationScopedBean{1740782693}, newBean=ApplicationScopedBean{453643025}}
説明
-
@New
を付けてインジェクションした方は、 RequestScoped になっている(なにこれこわい)。
#インスタンスを動的に取得する
Instance
インターフェースを介することで、プログラムで動的にインスタンスを取得することができる。
##基本
実装
package sample.cdi.bean.instance;
import javax.enterprise.context.RequestScoped;
@RequestScoped
public class Hoge implements MyInterface {
}
package sample.cdi.ejb;
import javax.ejb.Stateless;
import javax.enterprise.inject.Instance;
import javax.inject.Inject;
import sample.cdi.bean.instance.MyInterface;
@Stateless
public class InstanceEjb {
@Inject
private Instance<MyInterface> instance;
public void execute() {
MyInterface obj = this.instance.get();
System.out.println(obj.getClass());
}
}
動作確認
情報: class sample.cdi.bean.instance.Hoge$Proxy$_$$_WeldClientProxy
説明
-
Instance
インターフェースを使って、 CDI 管理ビーンのインスタンスを動的に取得できる。 -
Instance
のインスタンス自体は、@Inject
でインジェクションする。
##限定子で取得するビーンを指定する
実装
package sample.cdi.bean.instance;
import javax.enterprise.context.RequestScoped;
@RequestScoped @MyQualifier
public class Fuga implements MyInterface {
}
package sample.cdi.ejb;
import javax.ejb.Stateless;
import javax.enterprise.inject.Instance;
import javax.inject.Inject;
import sample.cdi.bean.instance.MyInterface;
+ import javax.enterprise.inject.Any;
+ import javax.enterprise.inject.Default;
+ import javax.enterprise.util.AnnotationLiteral;
+ import sample.cdi.bean.instance.MyQualifier;
@Stateless
public class InstanceEjb {
+ @Any
@Inject
private Instance<MyInterface> instance;
public void execute() {
- MyInterface obj = this.instance.get();
- System.out.println(obj.getClass());
+ System.out.println("ambiguous = " + instance.isAmbiguous());
+
+ Instance<MyInterface> defaults = instance.select(new AnnotationLiteral<Default>() {});
+ System.out.println("defaults.class = " + defaults.get().getClass());
+
+ Instance<MyInterface> qualified = instance.select(new AnnotationLiteral<MyQualifier>() {});
+ System.out.println("qualified.class = " + qualified.get().getClass());
}
}
動作確認
情報: ambiguous = true
情報: defaults.class = class sample.cdi.bean.instance.Hoge$Proxy$_$$_WeldClientProxy
情報: qualified.class = class sample.cdi.bean.instance.Fuga$Proxy$_$$_WeldClientProxy
説明
-
Instance#select(Annotation...)
メソッドを使うことで、限定子でビーンの候補を絞り込むことができる。- 引数の
Annotation
には、AnnotationLiteral
クラスを利用する。
- 引数の
- 限定子を指定したい場合は、
Instance
のインジェクションポイントで@Any
を使わなければならない。-
@Any
を指定しない場合、@Default
が暗黙的に指定されたことになってしまい、 この図 からも分かるように限定子で指定した範囲が取得できなくなってしまう。
-
#@Alternative
でインジェクションするビーンを切り替える
通常は A というビーンを使うけど、テストのときは B という別のビーンを使いたい。
みたいなときは、 @Alternative
を利用する。
この仕組を利用すれば、クラスパスに jar を追加したら自動でデフォルトの処理が置き換わる、みたいなことができるようになる。
##beans.xml を使用して切り替える
実装
package sample.cdi.bean.alternative.beansxml;
import javax.enterprise.context.Dependent;
@Dependent
public class ProductionBean implements MyInterface {
}
package sample.cdi.bean.alternative.beansxml;
import javax.enterprise.context.Dependent;
import javax.enterprise.inject.Alternative;
@Dependent @Alternative
public class TestBean implements MyInterface {
}
package sample.cdi.ejb;
import javax.ejb.Stateless;
import javax.inject.Inject;
import sample.cdi.bean.alternative.beansxml.MyInterface;
@Stateless
public class AlternativeEjb {
@Inject
private MyInterface obj;
public void execute() {
System.out.println("obj.class = " + obj.getClass().getSimpleName());
}
}
動作確認
情報: obj.class = ProductionBean
次に、 WEB-INF
の直下に beans.xml
を作成する。
<?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">
<alternatives>
<class>sample.cdi.bean.alternative.beansxml.TestBean</class>
</alternatives>
</beans>
再度 AlternativeEjb#execute()
を実行させる。
情報: obj.class = TestBean
説明
-
MyInterface
を実装した CDI 管理ビーンを2つ作成している(ProductionBean
とTestBean
)。 -
TestBean
の方は、@Alternative
でアノテートされている。 - この状態で何も指定をしない場合、
@Alternative
でアノテートされていない方がインジェクションされる(ProductionBean
)。 - ここで
beans.xml
を作成し、<alternatives>
の<class>
タグで@Alternative
でアノテートしたビーンの FQCN を指定する。 - すると、ここで指定したビーンがインジェクションされるようになる。
##@Priority
で切り替える
beans.xml
を使わなくても、 @Priority
アノテーションを使ってコード上で順序を定義することもできる。
実装
package sample.cdi.bean.alternative.priority;
import javax.annotation.Priority;
import javax.enterprise.context.Dependent;
import javax.enterprise.inject.Alternative;
@Dependent @Alternative @Priority(10)
public class DefaultBean implements MyInterface{
}
package sample.cdi.bean.alternative.priority;
import javax.annotation.Priority;
import javax.enterprise.context.Dependent;
import javax.enterprise.inject.Alternative;
@Dependent @Alternative @Priority(100)
public class PluginBean implements MyInterface {
}
package sample.cdi.ejb;
import javax.ejb.Stateless;
import javax.inject.Inject;
import sample.cdi.bean.alternative.priority.MyInterface;
@Stateless
public class PriorityEjb {
@Inject
private MyInterface obj;
public void execute() {
System.out.println("obj.class = " + obj.getClass().getSimpleName());
}
}
動作確認
情報: obj.class = PluginBean
説明
- CDI 管理ビーンをそれぞれ
@Alternative
でアノテートし、さらに@Priority
でアノテートする。 -
@Priority
のvalue
に渡した整数値が 大きい方 が、実行時に採用される。 - jar の中に含まれていても有効なので、デフォルトのビーンのプライオリティを低めにしておき、 jar に含まれるビーンをそれより高く設定しておけば、 jar を追加するだけで実装の切り替えができるようになる。
##@Specializes
で限定子付きのビーンもまとめて差し替える
###限定子が使用された場合の問題点
@Alternative
だけを使った差し替えは、限定子ごとに定義しなければならない。
例えば。。。
package sample.cdi.bean.alternative.priority.specialize;
import javax.enterprise.context.Dependent;
@Dependent
public class DefaultBean implements MyInterface {
}
package sample.cdi.bean.alternative.priority.specialize;
import javax.annotation.Priority;
import javax.enterprise.context.Dependent;
import javax.enterprise.inject.Alternative;
@Dependent @Alternative @Priority(10)
public class AlternativeBean implements MyInterface {
}
package sample.cdi.ejb;
import javax.ejb.Stateless;
import javax.inject.Inject;
import sample.cdi.bean.alternative.priority.specialize.MyInterface;
@Stateless
public class SpecializeEjb {
@Inject
private MyInterface obj;
public void execute() {
System.out.println("obj.class = " + obj.getClass().getSimpleName());
}
}
MyInterface
インターフェースに対して、デフォルト実装である DefaultBean
と、差し替え用の実装である AlternativeBean
が用意されている。
これを動かすと、以下のように出力される。
情報: obj.class = AlternativeBean
AlternativeBean
が適用されている。
ここで、限定子 @MyQualifier
を使用した新しいビーンが追加されたとする。
package sample.cdi.bean.alternative.priority.specialize;
import javax.enterprise.context.Dependent;
@Dependent @MyQualifier
public class QualifiedBean implements MyInterface {
}
package sample.cdi.ejb;
import javax.ejb.Stateless;
import javax.inject.Inject;
import sample.cdi.bean.alternative.priority.specialize.MyInterface;
+ import sample.cdi.bean.alternative.priority.specialize.MyQualifier;
@Stateless
public class SpecializeEjb {
@Inject
private MyInterface obj;
+ @Inject @MyQualifier
+ private MyInterface qualified;
public void execute() {
System.out.println("obj.class = " + obj.getClass().getSimpleName());
+ System.out.println("qualified.class = " + qualified.getClass().getSimpleName());
}
}
これを実行すると、次のように出力される。
情報: obj.class = AlternativeBean
情報: qualified.class = QualifiedBean
@MyQualifier
でアノテートされた方は、デフォルトの実装(QualifiedBean
)が適用され、 AlternativeBean
には差し替わっていない。
これを解決する単純な手段として、以下のような差し替え用のビーンを新規に作成するという方法がある。
package sample.cdi.bean.alternative.priority.specialize;
import javax.annotation.Priority;
import javax.enterprise.context.Dependent;
import javax.enterprise.inject.Alternative;
@Dependent @MyQualifier @Alternative @Priority(10)
public class QualifiedAlternativeBean implements MyInterface {
}
これで、実行結果は次のように変化する。
情報: obj.class = AlternativeBean
情報: qualified.class = QualifiedAlternativeBean
一応差し替えることはできた。
しかし、この方法だと限定子が増えるたびに差し替え用のビーンを追加しなければならず、かなりイケてない。
そこで、限定子付きのビーンも一括で差し替える方法として、 @Specializes
アノテーションが用意されている。
###@Specializes
で差し替える
実装
AlternativeBean
と QualifiedAlternariveBean
は削除して、以下のビーンを作成する。
package sample.cdi.bean.alternative.priority.specialize;
import javax.annotation.Priority;
import javax.enterprise.context.Dependent;
import javax.enterprise.inject.Alternative;
import javax.enterprise.inject.Specializes;
@Dependent @Alternative @Priority(10) @Specializes @MyQualifier
public class SpecializedQualifiedBean extends DefaultBean {
}
実行結果は以下のようになる。
情報: obj.class = SpecializedQualifiedBean
情報: qualified.class = SpecializedQualifiedBean
限定子ありなし、両方が差し替わっている。
説明
-
@Specializes
でアノテートするクラスは、差し替え元となるクラスをextends
するようにして作成する。 - 限定子付きのビーンを差し替えたい場合は、その限定子を
@Specializes
でアノテートしたクラスにも設定する。
#インターセプター
インターセプターを定義することで、ロギングなどの横断的関心事をビジネスロジックから分離できるようになる。
##基本(beans.xml
を使用する場合)
実装
package sample.cdi.bean.interceptor;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.interceptor.InterceptorBinding;
@InterceptorBinding
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface MyIntercept {
}
package sample.cdi.bean.interceptor;
import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;
@Interceptor
@MyIntercept
public class MyInterceptor {
@AroundInvoke
public Object intercept(InvocationContext ic) throws Exception {
System.out.println("before proceed");
Object result = ic.proceed();
System.out.println("after proceed");
return result;
}
}
package sample.cdi.bean.interceptor;
import javax.enterprise.context.Dependent;
@Dependent
public class Hoge {
@MyIntercept
public void method() {
System.out.println("Hoge#method()");
}
}
package sample.cdi.ejb;
import javax.ejb.Stateless;
import javax.inject.Inject;
import sample.cdi.bean.interceptor.Hoge;
@Stateless
public class InterceptorEjb {
@Inject
private Hoge hoge;
public void execute() {
this.hoge.method();
}
}
<?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">
<interceptors>
<class>sample.cdi.bean.interceptor.MyInterceptor</class>
</interceptors>
</beans>
動作確認
情報: before proceed
情報: Hoge#method()
情報: after proceed
説明
@InterceptorBinding
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface MyIntercept {
- まずは、インターセプター用のアノテーションを作成する。
-
@InterceptorBinding
でアノテートすることで、インターセプター用のアノテーションとなる。
@Interceptor
@MyIntercept
public class MyInterceptor {
@AroundInvoke
public Object intercept(InvocationContext ic) throws Exception {
System.out.println("before proceed");
Object result = ic.proceed();
System.out.println("after proceed");
return result;
}
}
- インターセプター本体は、
@Interceptor
と先ほど作成した自作アノテーションでアノテートする。 - 処理本体は、
@AroundeInvoke
でアノテートしたメソッドに記述する。 - このメソッドは、次のようなシグネチャになるように作成する。
- 引数に
InvocationContext
を受け取る。 - 戻り値の型を
Object
にする。 -
Exception
をthrows
句に宣言する。
- 引数に
public class Hoge {
@MyIntercept
public void method() {
System.out.println("Hoge#method()");
}
- 実際に使うときは、メソッドまたはクラスに対して自作したアノテーションを設定する。
- クラスに設定した場合は、全てのメソッドがインターセプターの対象になる。
<interceptors>
<class>sample.cdi.bean.interceptor.MyInterceptor</class>
</interceptors>
- 最後に、
beans.xml
に<interceptors>
タグを使ってインターセプタークラスを登録する。 -
<class>
タグは複数指定可能。 - 複数のインターセプターが1つのメソッドに対して適用される場合、
<class>
タグで列挙した順番でインターセプターが適用される。
##@Priority
を使って適用順序を指定する
@Priority
を使用すれば、 beans.xml
の記述が不要になる。
実装
package sample.cdi.bean.interceptor;
import javax.annotation.Priority;
import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;
@Interceptor
@MyIntercept
+ @Priority(10)
public class MyInterceptor {
@AroundInvoke
public Object intercept(InvocationContext ic) throws Exception {
System.out.println("before proceed");
Object result = ic.proceed();
System.out.println("after proceed");
return result;
}
}
@Interceptor
@MyIntercept
@Priority(11)
public class MyInterceptor2 {
@AroundInvoke
public Object intercept(InvocationContext ic) throws Exception {
System.out.println("before proceed 2");
Object result = ic.proceed();
System.out.println("after proceed 2");
return result;
}
}
動作確認
情報: before proceed
情報: before proceed 2
情報: Hoge#method()
情報: after proceed 2
情報: after proceed
説明
-
@Priority
でインターセプタークラスをアノテートして、value
に優先順位を表す整数を設定する。 - 数値が小さい方が先に適用される。
- マイナス値も可。
-
@Priority
を設定した場合、beans.xml
の記述は不要になる。
##特定のメソッドだけインターセプターを適用させないようにする
インターセプター用に自作したアノテーションが属性を保つ場合、その属性を含めてインターセプターの適用が決定される。
これを利用すれば、特定のメソッドだけインターセプターを適用させないようにできる。
###実装
package sample.cdi.bean.interceptor;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.interceptor.InterceptorBinding;
@InterceptorBinding
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface OptionalIntercept {
boolean value() default true;
}
package sample.cdi.bean.interceptor;
import javax.annotation.Priority;
import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;
@Interceptor
@OptionalIntercept(true)
@Priority(100)
public class OptionalInterceptor {
@AroundInvoke
public Object intercept(InvocationContext ic) throws Exception {
System.out.println("[OptionalInterceptor] before proceed");
Object result = ic.proceed();
System.out.println("[OptionalInterceptor] after proceed");
return result;
}
}
package sample.cdi.bean.interceptor;
import javax.enterprise.context.Dependent;
@Dependent
@OptionalIntercept
public class Fuga {
public void method1() {
System.out.println("method1");
}
@OptionalIntercept(false)
public void method2() {
System.out.println("method2");
}
}
package sample.cdi.ejb;
import javax.ejb.Stateless;
import javax.inject.Inject;
import sample.cdi.bean.interceptor.Fuga;
@Stateless
public class InterceptorEjb {
@Inject
private Fuga fuga;
public void optional() {
this.fuga.method1();
this.fuga.method2();
}
}
動作確認
情報: [OptionalInterceptor] before proceed
情報: method1
情報: [OptionalInterceptor] after proceed
情報: method2
説明
public @interface OptionalIntercept {
boolean value() default true;
}
- アノテーションに boolean 型の
value
を追加し、デフォルト値としてtrue
を設定している。
@Interceptor
@OptionalIntercept(true)
@Priority(100)
public class OptionalInterceptor {
- インターセプターの方に自作アノテーションを付けるときに、値に
true
を指定する(省略可)。 - こうすることで、
value
にtrue
が設定されているときに限り、このインターセプターを適用させることができるようになる。
@Dependent
@OptionalIntercept
public class Fuga {
public void method1() {
System.out.println("method1");
}
@OptionalIntercept(false)
public void method2() {
System.out.println("method2");
}
-
Fuga
クラスは、クラス自体が@OptionalIntercept
でアノテートされている。よって、デフォルトでは全てのメソッドにインターセプターが適用される。 - しかし、
method2()
は@OptionalIntercept(false)
でアノテートされているので、インターセプターの適用対象外になる。
特定の属性はインターセプターを適用させるかどうかの判定に使用したくない場合は、属性を @Nonbinding
でアノテートする。
public @interface OptionalIntercept {
- boolean value() default true;
+ @Nonbinding boolean value() default true;
}
#@Stereotype
でアノテーションをまとめる
「複数のクラスに同じアノテーションの組み合わせをいくつも設定している」
なんてことになると、毎回設定するのも面倒だし、組み合わせが変更になったときに修正も大変になる。
そういう場合は、 @Stereotype
でアノテーションをまとめることができる。
実装
package sample.cdi.bean.stereotype;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.enterprise.context.RequestScoped;
import javax.enterprise.inject.Stereotype;
import sample.cdi.bean.interceptor.OptionalIntercept;
@Stereotype
@RequestScoped
@OptionalIntercept
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyStereotype {
}
※OptionalIntercept
は、 以前 作った自作のインターセプター用アノテーション。
package sample.cdi.bean.stereotype;
@MyStereotype
public class Hoge {
public void method() {
System.out.println("Hoge.hash = " + hashCode());
}
}
package sample.cdi.ejb;
import javax.ejb.Stateless;
import javax.inject.Inject;
import sample.cdi.bean.stereotype.Hoge;
@Stateless
public class StereotypeEjb {
@Inject
private Hoge hoge;
public void execute() {
this.hoge.method();
}
}
動作確認
情報: [OptionalInterceptor] before proceed
情報: Hoge.hash = 2111292230
情報: [OptionalInterceptor] after proceed
情報: [OptionalInterceptor] before proceed
情報: Hoge.hash = 1615190599
情報: [OptionalInterceptor] after proceed
情報: [OptionalInterceptor] before proceed
情報: Hoge.hash = 1861411388
情報: [OptionalInterceptor] after proceed
説明
@Stereotype
@RequestScoped
@OptionalIntercept
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyStereotype {
- ひとまとめにしたいアノテーションと
@Stereotype
を、自作したアノテーションに設定する(@MyStereotype
)。 - すると、自作したアノテーションを設定するだけで、ひとまとめにしたアノテーションが適用されるようになる。
- 例では、
@RequestScoped
と@OptionalIntercept
が適用されている。
- 例では、
#デコレーター
デコレーターとは、デザインパターンのデコレーターパターンのこと。
CDI には、デコレーターパターンを簡単に実現するための仕組みが用意されている。
デコレーターパターンについては、
この辺を参照のこと。
これを利用すれば、既存の CDI 管理ビーンに対して、柔軟な機能拡張を施すことができるようになる。
実装
package sample.cdi.bean.decorator;
import javax.enterprise.context.Dependent;
@Dependent
public class Hoge implements MyInterface {
@Override
public void method() {
System.out.println("Hoge#method1()");
}
}
package sample.cdi.bean.decorator;
import javax.annotation.Priority;
import javax.decorator.Decorator;
import javax.decorator.Delegate;
import javax.inject.Inject;
@Decorator @Priority(100)
public class MyDecorator1 implements MyInterface {
@Inject @Delegate
private MyInterface delegate;
@Override
public void method() {
System.out.println("my decorator 1");
this.delegate.method();
}
}
package sample.cdi.bean.decorator;
import javax.annotation.Priority;
import javax.decorator.Decorator;
import javax.decorator.Delegate;
import javax.inject.Inject;
@Decorator @Priority(101)
public class MyDecorator2 implements MyInterface {
@Inject @Delegate
private MyInterface delegate;
@Override
public void method() {
System.out.println("my decorator 2");
this.delegate.method();
}
}
package sample.cdi.ejb;
import javax.ejb.Stateless;
import javax.inject.Inject;
import sample.cdi.bean.decorator.MyInterface;
@Stateless
public class DecoratorEjb {
@Inject
private MyInterface obj;
public void execute() {
this.obj.method();
}
}
動作確認
情報: my decorator 1
情報: my decorator 2
情報: Hoge#method1()
説明
@Decorator @Priority(100)
public class MyDecorator1 implements MyInterface {
- デコレーターパターンを適用する対象となるインターフェースを実装する形で、デコレーターのクラスを作成する。
- このクラスを
@Decorator
でアノテートする。 -
@Priority
を指定することで、デコレーターを有効にできる。
同じビーンに複数のデコレーターが適用された場合、value
の値が小さいほうが先に適用される。 -
インターセプターの場合と同じで、
beans.xml
を使って順序などを定義することもできる。 -
beans.xml
で定義する場合は、以下のようになる。
<?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">
<decorators>
<class>sample.cdi.bean.decorator.MyDecorator1</class>
<class>sample.cdi.bean.decorator.MyDecorator2</class>
</decorators>
</beans>
- 上から順番に適用される。
public class MyDecorator1 implements MyInterface {
@Inject @Delegate
private MyInterface delegate;
- デコレーターには、 Delegate Injection Potint を定義しなければならない。
- Delegate Injection Point とは、デコレート対象をインジェクションする場所のことで、
@Inject
と@Delegate
を使って宣言する。 - 例ではフィールドインジェクションを利用しているが、イニシャライザメソッドやコンストラクタでのインジェクションも可能。
@Override
public void method() {
System.out.println("my decorator 1");
this.delegate.method();
}
- 最後にデコレーターのメソッドを実装する。
- メソッドの中で Delegate Injection Point でインジェクションしたインスタンスを使うことで、デコレート対象の実装を呼び出すことができる。
#イベント
イベントとは、 CDI に用意されている、デザインパターンのオブザーバーパターンを実現するための仕組みのこと。
オブザーバーパターンについては、
この辺を参考のこと。
##基本
実装
package sample.cdi.bean.event;
public class MyEvent {
private String name;
public MyEvent(String name) {
this.name = name;
}
@Override
public String toString() {
return "MyEvent{" + "name=" + name + '}';
}
}
package sample.cdi.bean.event;
import javax.enterprise.context.Dependent;
import javax.enterprise.event.Observes;
@Dependent
public class MyObserver {
public void notifyMyEvent(@Observes MyEvent event) {
System.out.println("event = " + event);
}
}
package sample.cdi.ejb;
import javax.ejb.Stateless;
import javax.enterprise.event.Event;
import javax.inject.Inject;
import sample.cdi.bean.event.MyEvent;
@Stateless
public class EventEjb {
@Inject
private Event<MyEvent> event;
public void execute() {
this.event.fire(new MyEvent("Hello CDI Event!!"));
}
}
動作確認
情報: event = MyEvent{name=Hello CDI Event!!}
説明
- イベントを利用するときは、大きく以下の3つの要素を使用する。
- イベントオブジェクト(
MyEvent
)。 - オブザーバー(
MyObserver
)。 - イベント発火用のオブジェクト(
Event
)。
- イベントオブジェクト(
- イベントオブジェクトは、任意の Java オブジェクトを利用できる。
- オブザーバーは、 CDI 管理ビーンに次のメソッドを定義することで作成できる。
- 引数にイベントオブジェクトを受け取る。
- イベントオブジェクトが
@Observes
でアノテートされている。
- イベント発火用のオブジェクトは、
@Inject
でインジェクションして取得する。- 型引数には、イベントオブジェクトの型を指定する。
-
Event#fire()
メソッドで、イベントを発火することができる。 - イベントが発火されると、オブザーバーのメソッドがコールバックされる。
##メタデータを取得する
package sample.cdi.bean.event;
import javax.enterprise.context.Dependent;
import javax.enterprise.event.Observes;
import javax.enterprise.inject.spi.EventMetadata;
@Dependent
public class MyObserver {
- public void notifyMyEvent(@Observes MyEvent event) {
+ public void notifyMyEvent(@Observes MyEvent event, EventMetadata metadata) {
System.out.println("event = " + event);
+ System.out.println(metadata.getInjectionPoint().getMember());
}
}
情報: event = MyEvent{name=Hello CDI Event!!}
情報: private javax.enterprise.event.Event sample.cdi.ejb.EventEjb.event
- オブザーバーのメソッド引数に
EventMetadata
を追加すると、イベントを発火した場所の情報などにアクセスできる。
##その他の使い方
こちらを参照。
#参考
- JSR-000346 Contexts and Dependency Injection for Java EE 1.1 Final Release for Evaluation
- Java Platform, Enterprise Edition: The Java EE Tutorial Release 7 - Contents
- Apache DeltaSpike 1.0 | なるほど!ザ・Weld
- Seamの今後についての発表 – DeltaSpikeプロジェクト誕生の理由 | なるほど!ザ・Weld
- JBoss SeamとApache DeltaSpikeの今後
- seam - Is Seam3 and Weld CDI the same thing? - Stack Overflow
- Introduction to JBoss Seam
- Java Day Tokyo 2015 の振り返り - Programming Studio
- EJB and CDI Alignment and Strategy - 2-4.pdf
- CDI Essential Recipes at Java Day Tokyo 2015
- TOTD #161: Java EE 6 CDI Qualifiers explained - @Default, @Any, @New, @Named (Arun Gupta, Miles to go ...)
- Java EE 6: Understanding Contexts and Dependency Injection (CDI), Part 2 (Nishigaya's Weblog)
- Chapter 4. Dependency injection and programmatic lookup
- 速報:CDI 2.0 の JSR Review が開始されました - GlassFish Japan
- CDIによるデコレータ - Challenge Java EE !