JavaEE使い方メモ（CDI）

[環境構築](http://qiita.com/opengl-8080/items/aeda0d8bad42af3113bd)

[コード](https://github.com/opengl8080-javaee-samples/cdi)

#CDI とは
Contexts and Dependency Injection の略。
Java EE 7 には ver 1.1 が含まれている。
JSR は [346][1]。

DI （依存性の注入）に加えて、管理しているインスタンスのスコープの管理まで行ってくれる。

##CDI 誕生の経緯と JBoss Seam の変遷
CDI は、 JBoss が提供していた独自フレームワークである Seam が前身となっている。

Seam は日本語で「継ぎ目」という意味。
Java EE 5 の頃の JSF と EJB をシームレスに連携させることを目的に作られたのが、この Seam というフレームワーク。

この Seam の中で、 DI やコンテキストの管理を担っていたコアの部分が抽出され、 JSR として標準化されたものが CDI 1.0 （[JSR299][2]）になる。
参照実装は [Weld][3] で、 Red Hat によって開発されている。

JSR299 は Java EE 6 に取り込まれ、標準仕様となった。

コア部分が標準仕様として EE に取り込まれたあと、 Seam は CDI の拡張機能を提供するフレームワークとして開発されることになった（Seam 3）。
しかし、同じように CDI を拡張するサードパーティのフレームワークは、他にも存在していた（[Apache MyFaces CODI][5]、[CDISource][6]）。

せっかく CDI という形で仕様を一本化したのに、このままだと結局また分岐が進んでいくことになる。
このことを危惧した各フレームワークのコミュニティは、これらのフレームワークを１つにまとめ、新しく [Apache DeltaSpike][4] という 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][7]。

2.0 では、大きく次の機能強化が掲げられている。

1. Java SE 環境でも使えるようにする。
1. 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
**実装**

```java:MyInterface.java
package sample.cdi.bean;

public interface MyInterface {
    void method();
}
```

```java:HelloBean.java
package sample.cdi.bean;

import javax.enterprise.context.Dependent;

@Dependent
public class HelloBean implements MyInterface {

    @Override
    public void method() {
        System.out.println("Hello CDI!!");
    }
}
```

```java:CdiEjb.java
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();
    }
}
```

**動作確認**

```text:GlassFishコンソール出力
情報:   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` アノテーションを使用する。

また、具体的なインジェクション場所として、以下の３つの方法が存在する。

- フィールドに直接インジェクションする
- 初期化メソッドでインジェクションする
- コンストラクタでインジェクションする

**実装**

```java:Hoge.java
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 + '}';
    }
}
```

```java:Fuga.java
package sample.cdi.bean.injectpoint;

import javax.enterprise.context.Dependent;

@Dependent
public class Fuga {

    @Override
    public String toString() {
        return "Fuga{" + this.hashCode() + "}";
    }
}
```

```java:InjectionPointEjb.java
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);
    }
}
```

###実行結果
```text:GlassFishコンソール出力
情報:   initializer method injection!!
情報:   constructor injection!!
情報:   Hoge{fieldInjection=Fuga{311564470}, initializerMethodInjection=Fuga{1165724746}, constructorInjection=Fuga{1765497502}}
```

**説明**

####フィールドに直接インジェクションする
```java:Hoge.java
@Dependent
public class Hoge {
    @Inject
    private Fuga fieldInjection;
```

- フィールドを `@Inject` でアノテートすることで、インスタンスをインジェクションできる。
- 可視性は `private` でも問題ない。
- セッターメソッドなどを定義する必要がない分実装がスッキリするが、単体テストはしづらくなる。

####初期化メソッドでインジェクションする
```java:Hoge.java
    @Inject
    public void setFuga(Fuga fuga) {
        System.out.println("initializer method injection!!");
        this.initializerMethodInjection = fuga;
    }
```

- メソッドを `@Inject` でアノテートすることで、そのメソッド経由でインスタンスをインジェクションできる。
- メソッドの引数には、 CDI 管理ビーンを定義する。

####コンストラクタでインジェクションする
```java:Hoge.java
    @Inject
    public Hoge(Fuga fuga) {
        System.out.println("constructor injection!!");
        this.constructorInjection = fuga;
    }
```

- コンストラクタを `@Inject` でアノテートすることで、そのコンストラクタ経由でインスタンスをインジェクションできる。
- `@Inject` でアノテートできるコンストラクタは、１つまで。
- `@Inject` でアノテートされたコンストラクタが存在する場合、 CDI のコンテナはそのコンストラクタ経由でインスタンスを生成する。
- `@Inject` でアノテートされたコンストラクタが存在しない場合は、デフォルトコンストラクタでインスタンスが生成される。
- イミュータブルなビーンを定義したい場合は、コンストラクタインジェクションを利用すると良い（寺田さん推奨）。

#スコープ
CDI 管理ビーンのインスタンスには生存期間が存在する。これを **スコープ** と呼ぶ。

標準では、以下のスコープが存在する。

|   スコープ   |    アノテーション     |                   生存期間                   |
|--------------|-----------------------|----------------------------------------------|
| Request      | `@RequestScoped`      | １回 の HTTP リクエストの間のみ。            |
| Session      | `@SessionScoped`      | １つの `HttpSession` の間のみ。              |
| Application  | `@Application`        | Web アプリが起動している間ずっと。           |
| Dependent    | `@Dependent`          | インジェクション先のスコープを継承する。     |
| Conversation | `@ConversationScoped` | スコープの開始と終了をプログラマが指定する。 |

１回のリクエストの中で共有管理したいデータなどがあった場合、これまでは `ThreadLocal` などを使ってドキドキしながら管理していたが、 `@RequestScoped` を使えばそのへんの管理を全て CDI コンテナに任せられるようになる。

また、 `@SessionScoped` を使えば、 `HttpSession` などのサーブレット API に依存することなく、セッションに紐づく情報を管理できるようになる。

##基本動作の検証
**実装**

```java:ApplicationScopedBean.java
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 + "}";
    }
}
```

```java:SessionScopedBean.java
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 + "}";
    }
}
```

```java:RequestScopedBean.java
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 + "}";
    }
}
```

```java:DependentBean.java
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() + "}";
    }
}
```

```java:ScopeEjb.java
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()` メソッドで、自身とインジェクションされたインスタンスのハッシュ値を出力するようにしている。

**動作確認**

ブラウザから２回アクセス。
一旦 JSESSIONID を破棄し、再度２回アクセスする。

```text:GlassFishコンソール出力
情報:   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` は、１つの HTTP セッションが有効な間だけ、同じインスタンスが利用される。
    - セッションごとにインスタンスが切り換わる。
    - `@SessionScoped` でアノテートしたクラスは、 `Serializable` を実装しなければならない。
- `@RequestScoped` は、１つの HTTP リクエストごとに新しいインスタンスが生成され、利用される。
- `@Dependent` は、インジェクションした先と同じスコープが適用される。
    - `SessionScopedBean` にインジェクションする場合 `DependentBean` もセッションスコープになるので、 `Serializable` を実装しておく必要がある。

###プロキシがインジェクションされている
よくよく上記の表を見ると、 `this` のハッシュ値は変わっていないのに、インジェクションされているインスタンスのハッシュ値だけが切り替わっていることに気づく。

リクエストの度にインジェクションされているインスタンスが差し替わっているのかというと、そういうわけではない。
実際にインジェクションされているインスタンスは、本物のインスタンスではなく、それをラップしたプロキシオブジェクトになる。

```java:ScopeEjb.java
@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());
    }
}
```

```text:GlassFishコンソール出力
情報:   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 で複数画面に跨った情報の管理をしたい場合に利用される。

**実装**

```java:ConversationScopedBean.java
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() + "}";
    }
}
```

```java:ScopeEjb.java
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` にアクセスする。

```text:GlassFishコンソール出力
情報:   begin conversation scope. cid = 1
情報:   csb={this=246187740}
```

次に、同じ Web ブラウザのウィンドウから `http://localhost:8080/cdi/scope/conversation?cid=1` に何回かアクセスする（セッションが引き継がれるようにする）。

```text:GlassFishコンソール出力
情報:   cid = 1
情報:   csb={this=246187740}

情報:   cid = 1
情報:   csb={this=246187740}

情報:   cid = 1
情報:   csb={this=246187740}
```

`http://localhost:8080/cdi/scope/endConversation?cid=1` にアクセスする。

```text:GlassFishコンソール出力
情報:   end conversation scope. cid = 1
情報:   csb={this=246187740}
```

最後に `http://localhost:8080/cdi/scope/conversation?cid=1` にアクセスする。

```text:GlassFishコンソール出力
警告:   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 スコープには、スコープの状態が２種類存在する。
    - １つが「**一時的（`transient`）なスコープ**」で、もう１つが「**長期間（`long-running`）有効なスコープ**」。
    - `transient` なスコープは、 `@RequestScoped` と同じで HTTP リクエストが終了すると破棄される。
    - 一方、 `long-running` なスコープは複数のリクエスト間で維持される。
- デフォルトは `transient` なスコープが採用される。
- `long-running` にするためには、 `Conversation#begin()` メソッドを使用する。
- `begin()` メソッドで `long-running` なスコープを開始すると、一意な ID が発行される。
- 次回のリクエストのときに、その ID を `cid` というパラメータと共にサーバーに送ると、前回リクエストしたときのスコープが引き継がれる。
    - Conversation スコープは HTTP セッションごとに管理される。よって、別のセッションで発行された `cid` を異なるセッションで指定しても、スコープが引き継がれることはない。
    - `cid` を指定しなかった場合、デフォルトの `transient` 扱いになり、新たなスコープが生成される。
    - 仕様上、 `cid` で指定されなくなった過去の `long-running` 状態のスコープは、コンテナが任意に破棄しても構わないことになっている。

> 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` の存在を意識する必要はない。

#インスタンスが生成されるときと破棄されるときに処理を挟む
例えば、外部リソースを保持するビーンが存在したとする。
この仕組を利用すれば、そのビーンのインスタンスが生成されるときにリソースとの接続を確立させ、破棄されるときに接続を解放するように実装できる。

**実装**

```java:SessionScopedBean.java
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());
    }
}
```

```java:RequestScopedBean.java
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());
    }
}
```

```java:LifeCycleCallbackMethodEjb.java
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 に何回かアクセスする。

```text:GlassFishコンソール出力
情報:   [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 にアクセスする。

```text:GlassFishコンソール出力
情報:   LifeCycleCallbackMethodEjb#endSession()
情報:   [Session Scope] pre destroy : 216269762
```

**説明**

- `@PostConstruct` でアノテートされたメソッドは、インスタンスが生成されたときにコールバックされる。
- `@PreDestroy` でアノテートされたメソッドは、インスタンスが破棄されるときにコールバックされる。

##CDI 管理ビーンの初期化処理は、 `@PostConstruct` でアノテートしたメソッドで行わなければならない
コンテナは、コンストラクタを使って CDI 管理ビーンのインスタンスを生成する。
ということは、わざわざ `@PostConstruct` を使わなくても、コンストラクタで初期化処理を書くこともできるように思える。

しかし、 CDI 管理ビーンの初期化処理は、コンストラクタに書いてはいけない。
なぜなら、コンストラクタが実行された段階では、まだ依存性の注入が完了していない可能性があるからだ。
もし、 `@Inject` でフィールドインジェクションする予定のインスタンスをコンストラクタ内で使用してしまうと、最悪 `NullPointerException` が発生する。

一方、 `@PostConstruct` は、全ての依存関係の注入が完了した後でコールバックされるので、こういった問題が起きない。

よって、 CDI 管理ビーンの初期化処理は、コンストラクタではなく `@PostConstruct` でアノテートしたメソッドで行うのが良い。

#Producer メソッド
- スコープアノテーションでクラスを直接アノテートできず、コンテナに自動登録できない。
- 同じクラスのインスタンスを複数生成し、コンテナに登録したい。

こんな場合は、 Producer メソッドが利用できる。

※後者の場合は、限定子を利用する必要がある。

##基本
**実装**

```java:Hoge.java
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() + "}";
    }
}
```

```java:HogeFactory.java
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");
    }
}
```

```java:ProducerEjb.java
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()` が実行されるように何回かアクセスする。

```text:GlassFishコンソール出力
情報:   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
```

**説明**

```java:HogeFactory.java
    @Produces
    public Hoge getHoge() {
        System.out.println("getHoge()");
        return new Hoge("hoge factory");
    }
```

- `@Produces` でメソッドをアノテートすることで、そのメソッドが返却した値をインジェクションさせられるようになる。
    - このメソッドを Producer メソッドと呼ぶ。
- セッションビーンだけでなく、 CDI 管理ビーンでも Producer メソッドを定義できる。
- 単純に `@Produces` でアノテートしただけの Producer メソッドから生成されたビーンは、プロキシでラップされない。

##スコープを指定する
Producer メソッドで定義したインスタンスに、スコープを持たせることができる。

**実装**

```diff:HogeFactory.java
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");
    }
}
```

```diff:Hoge.java
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() + "}";
    }
}
```

**動作確認**

```text:GlassFishコンソール出力
情報:   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 フィールドを使う方法もある。

**実装**

```java:Fuga.java
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() + "}";
    }
}
```

```java:FugaFactory.java
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");
}
```

```java:ProducerEjb.java
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());
    }
}
```

**動作確認**

```text:GlassFishコンソール出力
情報:   Fuga{name=fuga factory, hash=415012830}
情報:   class sample.cdi.bean.producer.Fuga
```

- フィールドを `@Produces` でアノテートすることで、そのフィールドの値を他のビーンなどにインジェクションできるようになる。
- だいたい Producer フィールドと同じ振る舞いをする。

##Disposer メソッド
Producer メソッド（フィールド）によって生成されたビーンは、 `@PreDestroy` などのコールバックメソッドが利用できない。
代わりに、 Disposer メソッドというのが利用できる。

**実装**

```java:Piyo.java
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() + "}";
    }
}
```

```java:PiyoFactory.java
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());
    }
}
```

```java:ProducerEjb.java
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());
    }
}
```

**動作確認**

```text:GlassFishコンソール出力
情報:   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` オブジェクトを取得してロガーに渡す、というものがある。

**実装**

```java:Hoge.java
package sample.cdi.bean.metadata;

public class Hoge {
}
```

```java:HogeFactory.java
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();
    }
}
```

```java:MetadataEjb.java
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() {
    }
}
```

**動作確認**

```text:GlassFishコンソール出力
情報:   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`）と呼ばれるアノテーションを利用する。

##基本
**実装**

ビーン関連

```java:MyInterface.java
package sample.cdi.bean.qualifier;

public interface MyInterface {
}
```

```java:Hoge.java
package sample.cdi.bean.qualifier;

import javax.enterprise.context.Dependent;

@Dependent @HogeHoge
public class Hoge implements MyInterface {

    @Override
    public String toString() {
        return "Hoge{" + hashCode() + '}';
    }
}
```

```java:Fuga.java
package sample.cdi.bean.qualifier;

import javax.enterprise.context.Dependent;

@Dependent @FugaFuga
public class Fuga implements MyInterface {

    @Override
    public String toString() {
        return "Fuga{" + hashCode() + '}';
    }
}
```

限定子

```java:HogeHoge.java
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 {
}
```

```java:FugaFuga.java
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

```java:QualifierEjb.java
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);
    }
}
```

**動作確認**

```text:GlassFishコンソール出力
情報:   hoge=Hoge{1614742928}
情報:   fuga=Fuga{354651225}
```

**説明**

```java:HogeHoge.java
@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` は、限定子を使う可能性がある場所。

```java:Hoge.java
@Dependent @HogeHoge
public class Hoge implements MyInterface {
```

- まずは、 CDI 管理ビーンの方を作成した限定子でアノテートする。
- これで、 `MyInterface` 型でかつ `@HogeHoge` 限定子を指定された場合は `Hoge` 型で解決する、ということをコンテナに教えることができる。

```java:QualifierEjb.java
    @Inject @HogeHoge
    private MyInterface hoge;

    private MyInterface fuga;
    
    @Inject
    public void setFuga(@FugaFuga MyInterface fuga) {
        this.fuga = fuga;
    }
```

- インジェクション先は、 `@Inject` と作成したアノテーションで対象をアノテートする。
- メソッド（コンストラクタ）インジェクションするときは、引数を限定子でアノテートする。

##属性を含めて適用を判定させる
アノテーションが属性を持つ場合、その属性値も含めてインジェクション対象のビーン候補が選択される。
これを利用すれば、インジェクション対象を柔軟に、かつ型安全な方法で指定できるようになる。

**実装**

```java:MyBeanType.java
package sample.cdi.bean.qualifier.attribute;

public enum MyBeanType {
    HOGE,
    FUGA,
}
```

```java:MyQualifier.java
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;
}
```

```java:Hoge.java
package sample.cdi.bean.qualifier.attribute;

import javax.enterprise.context.Dependent;

@Dependent @MyQualifier
public class Hoge implements MyInterface {
}
```

```java:Fuga.java
package sample.cdi.bean.qualifier.attribute;

import javax.enterprise.context.Dependent;

@Dependent @MyQualifier(MyBeanType.FUGA)
public class Fuga implements MyInterface {
}
```

```java:QualifierAttributeEjb.java
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());
    }
}
```

**動作確認**

```text:GlassFishコンソール出力
情報:   obj1.class = Hoge
情報:   obj2.class = Fuga
```

**説明**

```java:MyQualifier.java
public @interface MyQualifier {
    MyBeanType value() default MyBeanType.HOGE;
}
```

- 限定子を作成し、 `value` にビーンの種類を指定するための列挙型を指定する。
- デフォルトで `HOGE` が適用されるようにしている。

```java:Hoge.java,Fuga.java
// 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 管理ビーンに名前を付けることができる。

**実装**

ビーン関連

```java:MyInterface.java
package sample.cdi.bean.qualifier.builtin.named;

public interface MyInterface {
}
```

```java:Hoge.java
package sample.cdi.bean.qualifier.builtin.named;

import javax.enterprise.context.Dependent;
import javax.inject.Named;

@Dependent @Named("HOGE")
public class Hoge implements MyInterface {
}
```

```java:Fuga.java
package sample.cdi.bean.qualifier.builtin.named;

import javax.enterprise.context.Dependent;
import javax.inject.Named;

@Dependent @Named
public class Fuga implements MyInterface{
}
```

動作確認用EJB

```java:NamedEjb.java
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());
    }
}
```

**動作確認**

```text:GlassFishコンソール出力
情報:   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` をインジェクション先で使用するのは<ins>推奨されていない</ins>。
    - 通常は、 JSF などで EL 式を書くときにのみ、参照名として `@Named` で設定した名前を使用する。

###`@Any` と `@Default`
CDI 回りのエラーメッセージで一度はお目にかかる限定子。
あまりにもよく見るうえによく分からないので、これを見ただけで嫌な気分になる（自分だけ？）。

これらの限定子の関係は、ややこしいようで理解してしまえばそんなに複雑ではない。

まず、それぞれの定義から。

####`@Any`
- 限定子の有無に関わらず、全ての CDI 管理ビーンに自動で設定される限定子。

####`@Default`
- 限定子を付けていない場合に、 CDI 管理ビーンに自動で設定される限定子。
- ただし、 `@Named` だけは例外で、これを設定していても `@Default` が一緒に自動設定される。
- 明示的に設定することも可能。

####例
`||` の上下は、意味的に同じになる。

```java:
@Dependent
public class Hoge {

     ||

@Depndent @Any @Default
public class Hoge {
```

```java:
@Dependent @Named
public class Fuga {

     ||

@Dependent @Named @Any @Default
public class Fuga {
```

```java:
@Dependent @MyQualifier
public class Piyo {

     ||

@Dependent @MyQualifier @Any
public class Piyo {
```

※`MyQualifier` は自作の限定子。

図に表すと、以下のような感じ。

<img src="https://qiita-image-store.s3.amazonaws.com/0/28302/36d091bd-3191-5767-6cbe-133d3a42c5d8.jpeg" id="built-in-qualifier-image" />

※この状態になると、 `Hoge` クラスはインジェクションできなくなる（`@Default` だけでは `Fuga` も候補になってしまい、ビーンを特定できないため）。

####実験
```java:MyInterface.java
package sample.cdi.bean.qualifier.defaultany;

public interface MyInterface {
}
```

```java:DefaultBean.java
package sample.cdi.bean.qualifier.defaultany;

import javax.enterprise.context.Dependent;

@Dependent
public class DefaultBean implements MyInterface {

    @Override
    public String toString() {
        return "DefaultBean{" + hashCode() + '}';
    }
}
```

```java:QualifiedBean.java
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() + '}';
    }
}
```

各ビーンの関係は、

![cdi.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/51f173b2-71c4-0d3a-09e6-ccf18ed1cd43.jpeg)

こんな感じ。

```java:DefaultAnyEjb.java
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());
    }
}
```

```text:GlassFishコンソール出力
情報:   onlyInject.class = DefaultBean
情報:   withDefault.class = DefaultBean
情報:   withDefaultAndAny.class = DefaultBean
情報:   withQualifier.class = QualifiedBean
```

###`@New`
このアノテーションをインジェクションポイントで利用すると、スコープが強制的に Dependent になる。

**実装**

```java:ApplicationScopedBean.java
package sample.cdi.bean.qualifier;

import javax.enterprise.context.ApplicationScoped;

@ApplicationScoped
public class ApplicationScopedBean {

    @Override
    public String toString() {
        return "ApplicationScopedBean{" + hashCode() + '}';
    }
}
```

```java:RequestScopedBean.java
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 + '}';
    }
}
```

```java:NewQualifierEjb.java
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);
    }
}
```

**動作確認**

```text:GlassFishコンソール出力
情報:   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` インターフェースを介することで、プログラムで動的にインスタンスを取得することができる。

##基本
**実装**

```java:Hoge.java
package sample.cdi.bean.instance;

import javax.enterprise.context.RequestScoped;

@RequestScoped
public class Hoge implements MyInterface {
}
```

```java:InstanceEjb.java
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());
    }
}
```

**動作確認**

```text:GlassFishコンソール出力
情報:   class sample.cdi.bean.instance.Hoge$Proxy$_$$_WeldClientProxy
```

**説明**

- `Instance` インターフェースを使って、 CDI 管理ビーンのインスタンスを動的に取得できる。
- `Instance` のインスタンス自体は、 `@Inject` でインジェクションする。

##限定子で取得するビーンを指定する
**実装**

```java:Fuga.java
package sample.cdi.bean.instance;

import javax.enterprise.context.RequestScoped;

@RequestScoped @MyQualifier
public class Fuga implements MyInterface {
}
```

```diff:InstanceEjb.java
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());
    }
}
```

**動作確認**

```text:GlassFishコンソール出力
情報:   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` が暗黙的に指定されたことになってしまい、 [この図](#built-in-qualifier-image) からも分かるように限定子で指定した範囲が取得できなくなってしまう。

#`@Alternative` でインジェクションするビーンを切り替える
通常は A というビーンを使うけど、テストのときは B という別のビーンを使いたい。
みたいなときは、 `@Alternative` を利用する。

この仕組を利用すれば、クラスパスに jar を追加したら自動でデフォルトの処理が置き換わる、みたいなことができるようになる。

##beans.xml を使用して切り替える
**実装**

```java:ProductionBean.java
package sample.cdi.bean.alternative.beansxml;

import javax.enterprise.context.Dependent;

@Dependent
public class ProductionBean implements  MyInterface {
}
```

```java:TestBean.java
package sample.cdi.bean.alternative.beansxml;

import javax.enterprise.context.Dependent;
import javax.enterprise.inject.Alternative;

@Dependent @Alternative
public class TestBean implements MyInterface {
}
```

```java:AlternativeEjb.java
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());
    }
}
```

**動作確認**

```text:GlassFishコンソール出力
情報:   obj.class = ProductionBean
```

次に、 `WEB-INF` の直下に `beans.xml` を作成する。

```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">
  
  <alternatives>
    <class>sample.cdi.bean.alternative.beansxml.TestBean</class>
  </alternatives>
</beans>
```

再度 `AlternativeEjb#execute()` を実行させる。

```text:GlassFishコンソール出力
情報:   obj.class = TestBean
```

**説明**

- `MyInterface` を実装した CDI 管理ビーンを２つ作成している（`ProductionBean` と `TestBean`）。
- `TestBean` の方は、 `@Alternative` でアノテートされている。
- この状態で何も指定をしない場合、 `@Alternative` でアノテート**されていない**方がインジェクションされる（`ProductionBean`）。
- ここで `beans.xml` を作成し、 `<alternatives>` の `<class>` タグで `@Alternative` でアノテートしたビーンの FQCN を指定する。
- すると、ここで指定したビーンがインジェクションされるようになる。

##`@Priority` で切り替える
`beans.xml` を使わなくても、 `@Priority` アノテーションを使ってコード上で順序を定義することもできる。

**実装**

```java:DefaultBean.java
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{
}
```

```java:PluginBean.java
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 {
}
```

```java:PriorityEjb.java
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());
    }
}
```

**動作確認**

```text:GlassFishコンソール出力
情報:   obj.class = PluginBean
```

**説明**

- CDI 管理ビーンをそれぞれ `@Alternative` でアノテートし、さらに `@Priority` でアノテートする。
- `@Priority` の `value` に渡した整数値が **大きい方** が、実行時に採用される。
- jar の中に含まれていても有効なので、デフォルトのビーンのプライオリティを低めにしておき、 jar に含まれるビーンをそれより高く設定しておけば、 jar を追加するだけで実装の切り替えができるようになる。

##`@Specializes` で限定子付きのビーンもまとめて差し替える
###限定子が使用された場合の問題点
`@Alternative` だけを使った差し替えは、限定子ごとに定義しなければならない。

例えば。。。

```java:DefaultBean.java
package sample.cdi.bean.alternative.priority.specialize;

import javax.enterprise.context.Dependent;

@Dependent
public class DefaultBean implements MyInterface {
}
```

```java:AlternativeBean.java
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 {
}
```

```java:SpecializeEjb.java
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` が用意されている。
これを動かすと、以下のように出力される。

```text:GlassFishコンソール出力
情報:   obj.class = AlternativeBean
```

`AlternativeBean` が適用されている。

ここで、限定子 `@MyQualifier` を使用した新しいビーンが追加されたとする。

```java:QualifiedBean.java
package sample.cdi.bean.alternative.priority.specialize;

import javax.enterprise.context.Dependent;

@Dependent @MyQualifier
public class QualifiedBean implements MyInterface {
}
```

```diff:SpecializeEjb.java
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());
    }
}
```

これを実行すると、次のように出力される。

```text:GlassFishコンソール出力
情報:   obj.class = AlternativeBean
情報:   qualified.class = QualifiedBean
```

`@MyQualifier` でアノテートされた方は、デフォルトの実装（`QualifiedBean`）が適用され、 `AlternativeBean` には差し替わっていない。

これを解決する単純な手段として、以下のような差し替え用のビーンを新規に作成するという方法がある。

```java:QualifiedAlternativeBean.java
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 {
}
```

これで、実行結果は次のように変化する。

```text:GlassFishコンソール出力
情報:   obj.class = AlternativeBean
情報:   qualified.class = QualifiedAlternativeBean
```

一応差し替えることはできた。
しかし、この方法だと限定子が増えるたびに差し替え用のビーンを追加しなければならず、かなりイケてない。

そこで、限定子付きのビーンも一括で差し替える方法として、 `@Specializes` アノテーションが用意されている。

###`@Specializes` で差し替える
**実装**

`AlternativeBean` と `QualifiedAlternariveBean` は削除して、以下のビーンを作成する。

```java:
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 {
}
```

実行結果は以下のようになる。

```text:GlassFishコンソール出力
情報:   obj.class = SpecializedQualifiedBean
情報:   qualified.class = SpecializedQualifiedBean
```

限定子ありなし、両方が差し替わっている。

**説明**

- `@Specializes` でアノテートするクラスは、差し替え元となるクラスを `extends` するようにして作成する。
- 限定子付きのビーンを差し替えたい場合は、その限定子を `@Specializes` でアノテートしたクラスにも設定する。

#インターセプター
インターセプターを定義することで、ロギングなどの横断的関心事をビジネスロジックから分離できるようになる。

##基本（`beans.xml` を使用する場合）
**実装**

```java:MyIntercept.java
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 {
}
```

```java:MyInterceptor.java
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;
    }
}
```

```java:Hoge.java
package sample.cdi.bean.interceptor;

import javax.enterprise.context.Dependent;

@Dependent
public class Hoge {
    
    @MyIntercept
    public void method() {
        System.out.println("Hoge#method()");
    }
}
```

```java:InterceptorEjb.java
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: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">
  
  <interceptors>
    <class>sample.cdi.bean.interceptor.MyInterceptor</class>
  </interceptors>
</beans>
```

**動作確認**

```text:GlassFishコンソール出力
情報:   before proceed
情報:   Hoge#method()
情報:   after proceed
```

**説明**

```java:MyIntercept.java
@InterceptorBinding
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface MyIntercept {
```

- まずは、インターセプター用のアノテーションを作成する。
- `@InterceptorBinding` でアノテートすることで、インターセプター用のアノテーションとなる。

```java:MyInterceptor.java
@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` 句に宣言する。

```java:Hoge.java
public class Hoge {
    
    @MyIntercept
    public void method() {
        System.out.println("Hoge#method()");
    }
```

- 実際に使うときは、メソッドまたはクラスに対して自作したアノテーションを設定する。
- クラスに設定した場合は、全てのメソッドがインターセプターの対象になる。

```xml:beans.xml
  <interceptors>
    <class>sample.cdi.bean.interceptor.MyInterceptor</class>
  </interceptors>
```

- 最後に、 `beans.xml` に `<interceptors>` タグを使ってインターセプタークラスを登録する。
- `<class>` タグは複数指定可能。
- 複数のインターセプターが１つのメソッドに対して適用される場合、 `<class>` タグで列挙した順番でインターセプターが適用される。

##`@Priority` を使って適用順序を指定する
`@Priority` を使用すれば、 `beans.xml` の記述が不要になる。

**実装**

```diff:MyInterceptor.java
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;
    }
}
```

```java:MyInterceptor2.java
@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;
    }
}
```

**動作確認**

```text:GlassFishコンソール出力
情報:   before proceed
情報:   before proceed 2
情報:   Hoge#method()
情報:   after proceed 2
情報:   after proceed
```

**説明**

- `@Priority` でインターセプタークラスをアノテートして、 `value` に優先順位を表す整数を設定する。
- 数値が**小さい方が先に適用される**。
- マイナス値も可。
- `@Priority` を設定した場合、 `beans.xml` の記述は不要になる。

##特定のメソッドだけインターセプターを適用させないようにする
インターセプター用に自作したアノテーションが属性を保つ場合、その属性を含めてインターセプターの適用が決定される。
これを利用すれば、特定のメソッドだけインターセプターを適用させないようにできる。

###<span href="optional-intercept">実装</span>

```java:OptionalIntercept.java
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;
}
```

```java:OptionalInterceptor.java
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;
    }
}
```

```java:Fuga.java
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");
    }
}
```

```java:InterceptorEjb.java
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();
    }
}
```

**動作確認**

```text:GlassFishコンソール出力
情報:   [OptionalInterceptor] before proceed
情報:   method1
情報:   [OptionalInterceptor] after proceed

情報:   method2
```

**説明**

```java:OptionalIntercept.java
public @interface OptionalIntercept {
    boolean value() default true;
}
```

- アノテーションに boolean 型の `value` を追加し、デフォルト値として `true` を設定している。

```java:OptionalInterceptor.java
@Interceptor
@OptionalIntercept(true)
@Priority(100)
public class OptionalInterceptor {
```

- インターセプターの方に自作アノテーションを付けるときに、値に `true` を指定する（省略可）。
- こうすることで、 `value` に `true` が設定されているときに限り、このインターセプターを適用させることができるようになる。

```java:Fuga.java
@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` でアノテートする。

```diff:OptionalIntercept.java
public @interface OptionalIntercept {
-   boolean value() default true;
+   @Nonbinding boolean value() default true;
}
```

#`@Stereotype` でアノテーションをまとめる
「複数のクラスに同じアノテーションの組み合わせをいくつも設定している」
なんてことになると、毎回設定するのも面倒だし、組み合わせが変更になったときに修正も大変になる。

そういう場合は、 `@Stereotype` でアノテーションをまとめることができる。

**実装**

```java:MyStereotype.java
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` は、 [以前](#特定のメソッドだけインターセプターを適用させないようにする) 作った自作のインターセプター用アノテーション。

```java:Hoge.java
package sample.cdi.bean.stereotype;

@MyStereotype
public class Hoge {
    
    public void method() {
        System.out.println("Hoge.hash = " + hashCode());
    }
}
```

```java:StereotypeEjb.java
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();
    }
}
```

**動作確認**

```text:GlassFishコンソール出力
情報:   [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
```

**説明**

```java:MyStereotype.java
@Stereotype
@RequestScoped
@OptionalIntercept
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyStereotype {
```

- ひとまとめにしたいアノテーションと `@Stereotype` を、自作したアノテーションに設定する（`@MyStereotype`）。
- すると、自作したアノテーションを設定するだけで、ひとまとめにしたアノテーションが適用されるようになる。
    - 例では、 `@RequestScoped` と `@OptionalIntercept` が適用されている。

#デコレーター
デコレーターとは、デザインパターンのデコレーターパターンのこと。
CDI には、デコレーターパターンを簡単に実現するための仕組みが用意されている。

デコレーターパターンについては、

- [Decorator パターン - Wikipedia](http://ja.wikipedia.org/wiki/Decorator_%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3)
- [12. Decorator パターン | TECHSCORE(テックスコア)](http://www.techscore.com/tech/DesignPattern/Decorator.html/)

この辺を参照のこと。

これを利用すれば、既存の CDI 管理ビーンに対して、柔軟な機能拡張を施すことができるようになる。

**実装**

```java:Hoge.java
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()");
    }
}
```

```java:MyDecorator1.java
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();
    }
}
```

```java:MyDecorator2.java
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();
    }
}
```

```java:DecoratorEjb.java
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();
    }
}
```

**動作確認**

```text:GlassFishコンソール出力
情報:   my decorator 1
情報:   my decorator 2
情報:   Hoge#method1()
```

**説明**

```java:MyDecorator1.java
@Decorator @Priority(100)
public class MyDecorator1 implements MyInterface {
```

- デコレーターパターンを適用する対象となるインターフェースを実装する形で、デコレーターのクラスを作成する。
- このクラスを `@Decorator` でアノテートする。
- `@Priority` を指定することで、デコレーターを有効にできる。  
同じビーンに複数のデコレーターが適用された場合、 `value` の値が小さいほうが先に適用される。
- [インターセプターの場合](#基本beansxml-を使用する場合)と同じで、 `beans.xml` を使って順序などを定義することもできる。
- `beans.xml` で定義する場合は、以下のようになる。

```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>
```

- 上から順番に適用される。

```java:MyDecorator1.java
public class MyDecorator1 implements MyInterface {
    @Inject @Delegate
    private MyInterface delegate;
```

- デコレーターには、 Delegate Injection Potint を定義しなければならない。
- Delegate Injection Point とは、デコレート対象をインジェクションする場所のことで、 `@Inject` と `@Delegate` を使って宣言する。
- 例ではフィールドインジェクションを利用しているが、イニシャライザメソッドやコンストラクタでのインジェクションも可能。

```java:MyDecorator1.java
    @Override
    public void method() {
        System.out.println("my decorator 1");
        this.delegate.method();
    }
```

- 最後にデコレーターのメソッドを実装する。
- メソッドの中で Delegate Injection Point でインジェクションしたインスタンスを使うことで、デコレート対象の実装を呼び出すことができる。

#イベント
イベントとは、 CDI に用意されている、デザインパターンのオブザーバーパターンを実現するための仕組みのこと。

オブザーバーパターンについては、

- [Observer パターン - Wikipedia](http://ja.wikipedia.org/wiki/Observer_%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3)
- [変態で学ぶObserverパターン - 裏紙](http://d.hatena.ne.jp/backpaper0/20111111/1321012118)

この辺を参考のこと。

##基本
**実装**

```java:MyEvent.java
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 + '}';
    }
}
```

```java:MyObserver.java
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);
    }
}
```

```java:EventEjb.java
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!!"));
    }
}
```

**動作確認**

```text:GlassFishコンソール出力
情報:   event = MyEvent{name=Hello CDI Event!!}
```

**説明**

- イベントを利用するときは、大きく以下の３つの要素を使用する。
    - イベントオブジェクト（`MyEvent`）。
    - オブザーバー（`MyObserver`）。
    - イベント発火用のオブジェクト（`Event`）。
- イベントオブジェクトは、任意の Java オブジェクトを利用できる。
- オブザーバーは、 CDI 管理ビーンに次のメソッドを定義することで作成できる。
    - 引数にイベントオブジェクトを受け取る。
    - イベントオブジェクトが `@Observes` でアノテートされている。
- イベント発火用のオブジェクトは、 `@Inject` でインジェクションして取得する。
    - 型引数には、イベントオブジェクトの型を指定する。
    - `Event#fire()` メソッドで、イベントを発火することができる。
    - イベントが発火されると、オブザーバーのメソッドがコールバックされる。

##メタデータを取得する
```diff:MyObserver.java
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());
    }
}
```

```text:GlassFishコンソール出力
情報:   event = MyEvent{name=Hello CDI Event!!}
情報:   private javax.enterprise.event.Event sample.cdi.ejb.EventEjb.event
```

- オブザーバーのメソッド引数に `EventMetadata` を追加すると、イベントを発火した場所の情報などにアクセスできる。

##その他の使い方
[こちらを参照](http://qiita.com/opengl-8080/items/73bddc7c744624c6ef9c)。

#参考
- [JSR-000346 Contexts and Dependency Injection for Java EE 1.1 Final Release for Evaluation][1]
- [Java Platform, Enterprise Edition: The Java EE Tutorial Release 7 - Contents](http://docs.oracle.com/javaee/7/tutorial/)
- [Apache DeltaSpike 1.0 | なるほど！ザ・Weld](https://tanoseam.wordpress.com/2014/08/03/apache-deltaspike-1-0/)
- [Seamの今後についての発表 – DeltaSpikeプロジェクト誕生の理由 | なるほど！ザ・Weld](https://tanoseam.wordpress.com/2012/01/16/seam_next/)
- [JBoss SeamとApache DeltaSpikeの今後](http://www.infoq.com/jp/news/2012/04/seam-deltaspike)
- [seam - Is Seam3 and Weld CDI the same thing? - Stack Overflow](http://stackoverflow.com/questions/16421142/is-seam3-and-weld-cdi-the-same-thing)
- [Introduction to JBoss Seam](http://docs.jboss.org/seam/2.3.1.Final/reference/html/Book-Preface.html)
- [Java Day Tokyo 2015 の振り返り - Programming Studio](http://www.coppermine.jp/docs/programming/2015/04/javadaytokyo2015.html)
- [EJB and CDI Alignment and Strategy - 2-4.pdf](http://www.oracle.co.jp/jdt2015/pdf/2-4.pdf)
- [CDI Essential Recipes at Java Day Tokyo 2015](http://www.slideshare.net/OracleMiddleJP/cdi-essential-receipe-at-java-day-tokyo-2015)
- [TOTD #161: Java EE 6 CDI Qualifiers explained - @Default, @Any, @New, @Named (Arun Gupta, Miles to go ...)](https://blogs.oracle.com/arungupta/entry/totd_161_java_ee_6)
- [Java EE 6: Understanding Contexts and Dependency Injection (CDI), Part 2 (Nishigaya's Weblog)](https://blogs.oracle.com/nishigaya/entry/javaee6_understanding_cdi_part_2)
- [Chapter 4. Dependency injection and programmatic lookup](https://docs.jboss.org/weld/reference/latest/en-US/html/injection.html#_obtaining_a_contextual_instance_by_programmatic_lookup)
- [速報：CDI 2.0 の JSR Review が開始されました - GlassFish Japan](http://www.coppermine.jp/docs/glassfish/2014/07/cdi2-jsr-review.html)
- [CDIによるデコレータ - Challenge Java EE !](http://kikutaro777.hatenablog.com/entry/2014/06/18/232219)

[1]: http://download.oracle.com/otndocs/jcp/cdi-1_1-fr-eval-spec/
[2]: https://jcp.org/en/jsr/detail?id=299
[3]: http://weld.cdi-spec.org/
[4]: https://deltaspike.apache.org/
[5]: http://myfaces.apache.org/extensions/cdi/
[6]: https://sites.google.com/site/cdipojo/get-started
[7]: https://www.jcp.org/en/jsr/detail?id=365