@Dependent
CDIのスコープアノテーションの一つ。
スコープのライフサイクルは
「Inject先のBeanのスコープに従う」
陥ってしまう勘違い
上記の説明。
これ例えば、Inject先のBeanが@RequestScopedであれば、@DependentのBeanも@RequestScopedとして振る舞う。
とも取れると思います。
実はこれは間違いなのです!
実例
例えば、下記のようなコードがある。
@RequestScoped
public class MainBean {
@Inject
private HogeBean hogeBean;
@Inject
private FugaBean fugaBean;
public void execute(){
hogeBean.setMethod(); // 1
fugaBean.method(); // 3
hogeBean.printMethod(); // 6
}
}
@RequestScoped
public class HogeBean {
@Inject
private DependentBean depBean;
public void setMethod(){
depBean.setId("HogeBeanで設定したID"); // 2
}
public void printMethod(){
System.out.println(depBean.getId()); // 7
}
}
@RequestScoped
public class FugaBean {
@Inject
private DependentBean depBean;
public void method(){
System.out.println(depBean.getId()); // 4
depBean.setId("FugaBeanで設定したID"); // 5
}
}
@Data
@Dependent
class DependentBean{
private String id;
}
MainBeanのexecuteがどこからか実行されるとする。
- hogeBeanのsetMethod()が呼ばれる。
- hogeBeanのもつdepBeanにIDが設定される。
- fugaBeanのmethod()が呼ばれる。
- fugaBeanのもつdepBeanのIDを出力する。
- fugaBeanのもつdepBeanにIDが設定される。
- hogeBeanのprintMethod()が呼ばれる。
- hogeBeanのもつdepBeanのIDを出力する。
というような流れになります。
Inject先のスコープと同じスコープになるという解釈だと、DependentBeanは@RequestScopedのBeanと考えられる。
@RequestScopedのオブジェクトは1リクエストで同じインスタンスを使いまわすため
上記4.で出力されるのは2.で設定した値になり、7.で出力されるのは5.で設定した値になるはず。
だから出力結果は
HogeBeanで設定したID
FugaBeanで設定したID
となるはず。
しかし、実際には出力結果は
null
HogeBeanで設定したID
となります。
本当の@Dependentの振る舞い
@Dependentの生存期間は、
- Inject先の生存期間と同じ
が正解です。
hogeBeanが生成されるタイミングでdepBeanが生成されるし、
hogeBeanが破棄されるタイミングでdepBeanが破棄されます。
また、
hogeBeanにもつdepBeanとfugaBeanにもつdepBeanは別インスタンスとなります。
すごく乱暴な言い方をすると、フィールドにnewしてインスタンスを作成しているのと同じイメージです。
(もちろんCDI管理にならないため現実には扱いが異なりますが)
循環参照
CDI管理Bean同士の循環参照は基本的には問題ないですが、@DependentのBean同士で循環参照を行うとサーバ起動時にエラーとなります。
これは、依存先のBeanが見つからなくなるためです。
まとめ
よくよく考えてみると、Inject先に@ApplicationScopedがあったり@SessionScopedがあったりした時にどうなるんだよ。
ってなるんですが、周りでも勘違いしている人が多かったので。
ドメインでコンポーネントを分けた際などにそれぞれで別の状態が持てるのでなにかうまく使えたりしそうです。