Java の習得は、SE → EE の順に進むと思うが、EE になって役者がごそっと出てくるため、躓きやすい。
これまでにも EJB や JPA の話題は取り上げてきたが、今回は CDI 。
まず、CDI と weld の関係だが、CDI が仕様で weld がその実装の一つ。
WildFly の stanalone.xml などで
<extension module="org.jboss.as.weld"/>
とあるのは、この WildFly には weld を組み込んでいますよ、という意味だ。
WildFly 27 では、weld 5.1 が採用されているので、その概略は
Weld 5.1.0.Final – CDI Reference Implementation
でチェックするといいだろう。
下手な日本語解説記事よりわかりやすい。
CDI とは?
ところで CDI とは何だろう?
よく依存性注入がどうしたこうしたと難しげに解説してあるサイトがあるが、初学者が理解できるとは思えない。
簡単に言えば、あるオブジェクトを使うのにいちいち new して作成せずともコンテナの方で勝手にやってくれる機能のことだ。
注入されるクラスにはそのインスタンスの生存期間を明示する必要があるので scope 関係のアノテーションを付与し、実際に注入する際には @Inject アノテーションでその箇所を明記する。
なお、CDI は Contexts and Dependency Injection の頭文字をつなげたもの。
サンプルコード解説
いつものように簡単なサンプル作成。
インジェクトされるクラスとインジェクトするクラス(サーブレット)を作成し、使用通りに動くか検証。
インジェクトされる側のクラスは以下の通り。
ScopedSevice.java
------
@RequestScoped
public class ScopedService {
private int counter = 0;
public void countUp() {
this.counter++;
}
public int getCounter() {
return this.counter;
}
}
これを以下のサーブレットクラスにインジェクトする(今回はフィールドにそのままインジェクト)する。
CDI.java
-------
public class CDI extends HttpServlet {
@Inject
ScopedService scopedService;
@Override
protected void doGet(final HttpServletRequest pReq, final HttpServletResponse pResp) throws ServletException, IOException {
this.scopedService.countUp();
this.scopedService.countUp();
pResp.setContentType("text/plain");
pResp.getWriter().println("Counter Value -> " + this.scopedService.getCounter());
}
}
ScopedService クラスは new などで明示的にインスタンス化されていないが、CDI クラスから問題なく使えるか試すコードです。
インジェクトされた後、countUp メソッドを2回呼び出しているので、counter = 2 となっていれば、良いわけです。
サーブレットなので、ブラウザから呼び出すと
と見事に予想通りの処理をしてくれています。
ソースコードは github にあげてあります。
面白かったのは、Inject される側のクラスで @Singleton を使うと以下のようなエラーが出た点だ。
Dispatcher error: {"WFLYCTL0080: Failed services" => {"jboss.deployment.unit.\"CDI-Jakarta10-1.0.war\".WeldStartService" => "Failed to start service
Caused by: org.jboss.weld.exceptions.DeploymentException: WELD-001408: Unsatisfied dependencies for type ScopedService with qualifiers @Default
at injection point [BackedAnnotatedField] @Inject servlet.CDI.scopedService
at servlet.CDI.scopedService(CDI.java:0)
"}}
CDI を使う利点の一つは、スコープの管理が楽になることなので、安直に singleton は使わない方がいいのかもしれません。
参考
ここ。
Unfortunately, there’s a little problem with this pseudo-scope. Beans with scope @Singleton don’t have a proxy object.
CDI の仕様が変われば、うまく扱えなくなること確実なアノテーション。それが @Singleton www