初めてのJava EE 6 (Servlet + CDI(WebBeans))

前回では、Servlet + EJB 3.1という組み合わせのサンプルを試しに作ってみました。今回はServlet + CDIです。
CDIは、http://d.hatena.ne.jp/Hirohiro/20091228/1262006216:title=というマニアックなエントリで既に登場していますが、知らない人は全く知らないと思いますので、簡単にですが紹介します。
CDIとは、Contexts and Dependency Injectionの略でmikiさんのBlogで詳しく書かれていますが、WebBeansと以前は呼ばれていた仕様です。そのCDIの参照実装がWeldというプロダクトで、今回、Java EE 6のお勉強に使っているGlassFishにもバンドルされてます。WeldはJBoss.orgで開発されていますので、もちろんJBossASにもバンドルされます。
さて、CDIは何をしてくれるかというと、ざっくりいうとプレゼンテーション層の技術とビジネスロジック層の技術をつなぐものであり、Java EE全体で統一的なDIコンテナを提供してくれるものです。CDIの元ネタになっているJBoss Seamだと、主にJSFEJB3をつなぐものとして開発されてましたが、CDIJSF以外からも使えるようになってます。標準でServletととも連携が可能です。Weld固有の実装ですが、Wicket連携もできるようです。GWT連携も考えているとか。

ということで、今回はServletからCDIを使ってビジネスロジックのオブジェクトをインジェクションして呼び出します。

前回作成したEJBバージョンを修正してCDI利用バージョンに変えたいと思います。EJBバージョンでは、ビジネスロジックであるHelloServiceクラス(EJB)を@EJBでインジェクションしていました。これをCDIを利用してインジェクションするようにします。

変更するものは以下の通りです。

  • HelloService.javaを修正
    • @Statelessを外し*1CDIに管理してもらうために@Named*2を付ける
package org.example;

import javax.inject.Named;

@Named
public class HelloService {

    public void sayHello() {
    	System.out.println("Hello!");
    }

}
  • HelloServlet.javaを修正
    • @EJBでインジェクションしていた部分を、@Injectに変更します。
@WebServlet(name = "HelloWorld", urlPatterns = { "/HelloWorld" })
public class HelloWorld extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
	@Inject
	HelloService helloService;

Javaコードの修正は以上です。あとは、CDIを組み込むためにbeans.xmlというXMLファイルをWEB-INF/libに配置します。なお、今回はインターセプタなどは使わないので、beans.xmlは空でOKです。

これでGlassFishにデプロイし、前回と同様にブラウザからアクセスすると、EJB版と同じように正常に動作します。

EJBと同じように動作するとなると、CDIの利点は何でしょうか?一つは、EJBコンテナがいらないということ。EJB3.1になり、WARだけでもEJBが使えるようになりましたが、EJBコンテナを積んでいないTomcatやJettyでは動きません。CDIなら、EJBコンテナが無い環境でもDIやインターセプタが標準技術として使えます。これは一つの利点だと思います。
他にもCDI固有の機能がありますが、それは追々紹介して行きたいと思います。

*1:EJBのままでもCDIでもちろん使えますが、ここではEJBをつかなくてもCDIを使えるということを言いたいために、あえて外しています。

*2:Seamを知っている人からみると、CDIは若干シンプルになってますね。Seamでは、登録するために@Nameアノテーションとともにコンポーネント名の指定が必要でした。CDIだと、@Namedだけ付けるとデフォルトでクラス名の先頭を小文字にした文字列が名前になります。