CDIが使えると何がうれしいか

昨日のコメントで書いたやつをエントリに格上げしてみる。


CDIを使うメリットはおそらくアプリケーションサーバーの各種機能が使いやすくなるというのが一番ですね。CDI自体の機能がどうのこうのではなく。

今まではアプリケーションサーバー管理のクラスはJSFの管理Bean、サーブレット、フィルタ、リスナーくらいでした。これらのクラスは簡単にEJBやらデータソースやらトランザクション管理やらメッセージングやらアプリケーションサーバーに実装されたものが扱えます。

ですが、それ以外だと簡単とは行きませんでした。

便利な機能がアプリケーションサーバーにせっかくたくさんあるのにそれを使うのが容易ではない、と。結果全部DIコンテナやらその上のフレームワークで実装することが多かったと思います。結局ただのサーブレットコンテナに成り下がっています。同じものがあるのにそれを使わない、いわば車輪の再発明だらけというのはもったいないですよね。


それがCDIを利用することによってすべてアプリケーションサーバーの管理下におかれます。EJBすら呼び出しが簡単になります。


たとえば昨日のT2FrameworkCDIの組み合わせのサンプルですが、メッセージ生成をロジックとしてEJBに外だししてみます。そしてそれをPageクラスから呼び出します。Requestスコープのフォームに入力と出力のプロパティを持たせるだけというシンプルなもの。


Pageクラス

package t2cdi;

import javax.ejb.EJB;
import javax.inject.Singleton;
import org.t2framework.t2.annotation.core.Default;
import org.t2framework.t2.annotation.core.Form;
import org.t2framework.t2.annotation.core.Page;
import org.t2framework.t2.navigation.Forward;
import org.t2framework.t2.spi.Navigation;


@Singleton
@Page("/")
public class HelloPage {

    @EJB
    MessageEJB msg;

    @Default
    public Navigation index(@Form RequestData input){

        String outText = msg.getMessage(input.getName());
        input.setMessage(outText);

        return Forward.to("/index.jsp");
    }

}


リクエストスコープのフォーム

package t2cdi;

import java.io.Serializable;
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;

@RequestScoped
@Named("requestData")
public class RequestData implements Serializable{
    String name ;
    String message;


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

}


EJB

package t2cdi;

import javax.ejb.Stateless;

@Stateless
public class MessageEJB {
    
    public String getMessage(String name){
        if(name == null || name.isEmpty()){
            return "名前を入力してください";
        }
        return "Hello! "+name;
    }
}


見てわかるように@EJBで注入できていますね。これで簡単にEJBとつながるというのがよくわかります。今回は利用していませんが、EJBですからトランザクションまわりも全自動で行われます。

今まではJSFだったらEJBは簡単につかえるけどさーというのがなくなるのがわかるでしょうか。

CDIのRIであるWeldはWicket用のjarも用意されていて、Wicketからアプリケーションサーバーのリソースを使うことも簡単にできるようになっています。Wicketの性格だとステートフルな情報をEJBのStatefulセッションビーンのほうに持っていくというのが考えられます。たとえば頻繁にアクセスされるユーザーのデータはメモリ上に残っている割合が高く、アクセスが少ないユーザーはHDDに退避されやすいといった制御が自動(大概のアプリケーションサーバーは設定でLRUとかアルゴリズムやメモリ上で保持するBeanの数とかをGUIで簡単に変えれる)で行われます。


ほかにも、たとえばトランザクションまわりを利用したい場合、以下の2行を書くだけです。

@Resource
UserTransaction tx;


データソースを使いたい場合、以下の2行だけです。

@Resource("jdbc/hoge")
DataSource ds;


時間がかかる処理なので非同期処理をしたい場合、たとえばメール送信などをする場合以下の4行記述するだけでJMSを使えます。

@Resource(name = "jms/mail")
Queue mail;

@Resource(name = "jms/mailFactory")
ConnectionFactory mailFactory;

簡単ですね。


今までは遠いものだったサーバーのリソース使用がぐっと身近になると思われます。


アプリケーション側にデータベースのコネクションを記述するのはよくないことだと思うのでこのへんから使うのがいいですね。テストと運用で別のwarを作ることになりますので。どんなに自動化したとしても2つあっては作業者のミスは発生してしまいますし、ある意味運用用のwarはテストされてないともいえます。

コネクションプーリングとトランザクション管理だけでもアプリケーションサーバーに用意されているものを使うのがいいですね。



ほかには各種オブジェクトの管理を一元化するということはフレームワーク間でのやり取りが用意になる可能性が高いということです。たとえばJSFT2FrameworkWicketとでリクエストやセッション情報のやり取りが可能になる可能性があります。ロジックの共通化も可能になる可能性があるわけです。

こうなると一般ユーザーがアクセスするところはデザイナにHTML作ってもらってjQueryとか使うような見た目重視の開発はT2Frameworkなどアクションベースで、見た目は重視しないが画面数が膨らんでしまうので生産性が重要でユーザー数が特定可能な管理画面等はJSFWicketなどのイベント+ステートフルなAjaxバリバリアプリで快適に、といった組み合わせが可能になります。フレームワークは得意不得意がどうしてもあるので組み合わせは結構便利だと思います。無理して統一してもどちらかの画面開発側が悲鳴を上げます。

また、別のフレームワークへの移行時とかでこの特徴は便利になるかもしれませんね。