CDI(旧名WebBeans)入門 その1
帰省前にCDIのコードをさらしてみたが、そもそもCDIがなんたるかを知らない人が多い気がするのでそのためのエントリ。
今回のほうを先に書くべきだったかな。
CDIとはJSR-299仕様のContexts and Dependency Injectionの略。つまり、大雑把に言えばWebアプリケーションにおけるDIコンテナの標準化をしようというもの。
JSR-330にDependency Injection for Javaという似たものがあり大変な混乱が見られたが、この2つは統合され、CDIはJSR-330のDIを利用するように変更になった。どちらもJava EE 6の一仕様となっている。
JSR-330というのはDIの書式の共通化を仕様というもので、中身はまんまGoogle Guice。したがって既存のDIコンテナを触ったことがあるのならすんなり触ることが出来るはずだ。
DIコンテナはその名の通りDIが一番大事な要素に思われがちだが、個人的にはそんなのはサービスロケータ等で取得してもかまわないと思っている。もちろん楽にはなるけど。大事なのは取得したインスタンスのスコープを管理してくれること。そして、スコープの管理とコンテナを経由することによってAOPが手軽に扱えるようになっていることも大きい。インジェクションはおまけ。
JSR-330ではスコープなしとシングルトンしか用意されていないが、Webアプリで便利なリクエストやセッションスコープ等がJSR-299で用意されているということを知ると位置関係がわかるかもしれない。もちろんCDIにもAOPもある。EJBにも普通にあるけど、完全にPOJOで開発できるところがポイント。
前回のサンプルはいろいろとつめこんだので、今回は一番単純な注入をしてみる。
カウンタクラス
public class Counter { private int count = 0; public int next(){ return ++count; } }
import java.io.IOException; import java.io.PrintWriter; import javax.inject.Inject; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet(name="CounterServlet", urlPatterns={"/CounterServlet"}) public class CounterServlet extends HttpServlet { @Inject private Counter c; @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); out.println("<html>"); out.println("<head>"); out.println("<title>Servlet CounterServlet</title>"); out.println("</head>"); out.println("<body>"); out.println("<h1>" + c.next() + "人目のアクセス</h1>"); out.println("</body>"); out.println("</html>"); } }
今回のクラスのスコープはなし。サーブレットがアプリケーション起動から最後までインスタンスが存在するために、サーブレットからのアクセスだと実質シングルトンと同じになっている。もちろん、他のクラスに注入されるときはそれぞれインスタンスが作られる。
見てわかるようにカウンタクラスはただのクラス。アノテーションすらついていない。
注入するときにはjavax.inject.Injectアノテーションをフィールドにつけるだけ。パッケージ名は違うがGuiceまんまである。
フィールドインジェクション以外にもセッターインジェクションも可能だ。上記コードを以下のようにしてもよい。
private Counter c; @Inject public void setC(Counter c) { this.c = c; }
そのほかメソッドインジェクションも可能。
private Counter c; @Inject public void init(Counter c) { this.c = c; }
さらにコンストラクタインジェクションも可能。引数のあるサーブレットのコンストラクタは違和感があるかもしれないが問題なく動く。
private Counter c; @Inject public CounterServlet(Counter c) { this.c = c; }
型で自動で判断して注入しているということを覚えておこう。
まずは一番簡単なところということで今回はここまで。
EJB3/DIコンテナを触ったことがある人なら何も難しいことは無いはず。逆に触ったことが無い人は必要性自体がわからないという数年前によくあった状態なんだろうなぁとか思ったり。