Seasar 2.4リリース! 今更でも恥ずかしくない、始めてみようDIプログラミング SMART deploy 編

http://journal.mycom.co.jp/articles/2006/11/16/seasar/
頻繁に Seasar 関連プロダクトを取り上げてくださる MYCOM ジャーナル.
今回はリリースされたばかりの Seasar2.4 を使った DI の入門記事です.
ちなみにタイトルの「今更でも恥ずかしくない」は,Seasar Conference の定番ネタ (?)「今さら人には聞けない」つながりでしょうか? (^^;
ともあれ (JW),全 12 ページにも及ぶ力作です.
本当にありがとうございます.


この記事では,dicon にコンポーネントを明示的に書く方法から始まって,最後は AutoRegister を使ったサンプルで終わります.
しかし,Seasar2.4 の目玉である SMART deploy は使われていません.
そんなわけで (どんなわけで?),この記事の続きのような感じで SMART deploy を使ってみましょう.
ちなみに,SMART deploy というのは HOT deploy / COOL deploy / WARM deploy の総称で,Javadoc プロジェクトの鈴木 (id:hidebsd) さんが命名してくれました.


さて.
SMART deploy は Ruby on Rails で知られる Convention over Configuration を積極的に活用しているのですが,記事で使われているサンプルはその規約と合わないところがあります.
まず,記事中のサンプルはインタフェースと実装クラスでパッケージが異なっています.
インタフェースは org.example パッケージ.実装クラスは test パッケージ.
それから,SMART deploy ではインタフェース/クラスを典型的な Web アプリケーションに合わせてカテゴライズしていますが,記事中のサンプルに出てくる MessagePrinter はどのカテゴリにも当てはまりません.
これを Seasar2.4 の規約に適合させることにしましょう.


まずはルートパッケージが必要です.
ここでは記事中のサンプルでインタフェースのパッケージとして使われている,

  • org.example

をルートパッケージとします.


次に,サンプルで使われているクラスを何らかのカテゴリに当てはめる必要があります.
ここではサービスということにします.
サービスの場合,インタフェースはルートパッケージのサブパッケージ service に配置します.

  • org.example.service

実装クラスはさらにそのサブパッケージ impl に配置します.

  • org.example.service.impl


そしてインタフェース名は末尾に Service を付けます.
そんなわけで (どんなわけで?),次のようなコードになります.

MessageService.java
package org.example.service;

public interface MessageService {
    public String getMessage();
}
PrintService.java
package org.example.service;

public interface PrintService {
    public void print();
}


実装クラス名はインタフェース名の末尾に Impl を付けます.
そんなわけで (どんなわけで?),次のようなコードになります.

MessageServiceImpl.java
package org.example.service.impl;

import org.example.service.MessageService;

public class MessageServiceImpl implements MessageService {
    public String getMessage() {
        return ("新大陸へようこそ");
    }
}
PrintServiceImpl.java
package org.example.service.impl;

import org.example.service.MessageService;
import org.example.service.PrintService;

public class PrintServiceImpl implements PrintService {
    private MessageService messageService;

    public void setMessageService(MessageService messageService) {
        this.messageService = messageService;
    }

    public void print() {
        System.out.println(messageService.getMessage());
    }
}


そして実行用のクラスです.
こちらは S2 で管理されるクラスではないので,規約に従う必要はありません.
元の記事通り,test パッケージです.
ただし,SMART deploy は SingletonS2ContainerFactory に頼るところがあるので,コンテナの初期化を変えています.

Test.java
package test;

import org.example.service.PrintService;
import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.factory.SingletonS2ContainerFactory;

public class Test {
    public static void main(String[] args) {
        SingletonS2ContainerFactory.init();
        S2Container container = SingletonS2ContainerFactory.getContainer();

        PrintService printer = (PrintService) container
                .getComponent("printService");
        printer.print();
    }
}


それでは dicon ファイルを用意していきましょう.
まずはルートパッケージを設定する dicon です.

convention.dicon
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN" 
    "http://www.seasar.org/dtd/components24.dtd">
<components>
    <component class="org.seasar.framework.convention.impl.NamingConventionImpl">
        <initMethod name="addRootPackageName">
            <arg>"org.example"</arg>
        </initMethod>
    </component>
</components>

先に決めたルートパッケージを設定しています.


次に,コンポーネント定義を作成するコンポーネントリエータを設定する dicon です.

creator.dicon
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN" 
    "http://www.seasar.org/dtd/components24.dtd">
<components>
    <include path="convention.dicon"/>
    <include path="customizer.dicon"/>

    <component class="org.seasar.framework.container.creator.ServiceCreator"/>
</components>

今回のサンプルではサービスしか使用しないので,サービス用のクリエータのみを定義しています.


そしてコンポーネント定義をカスタマイズする,コンポーネントカスタマイザの定義です.

customizer.dicon
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN" 
    "http://www.seasar.org/dtd/components24.dtd">
<components>
    <component name="serviceCustomizer" class="org.seasar.framework.container.customizer.CustomizerChain"/>
</components>

今回のサンプルではサービスしか使用しないので,サービス用のカスタマイザのみを定義しています.


この 3 つの dicon ファイルが SMART deploy の設定の中心になります.
こうやってみると面倒そうに見えますが,実際は最初に作成すればその後手を加えることはあまりないですし,最初にしても Dolteng のプロジェクトウィザードを使えば勝手に作ってくれたりします.


続いて,Seasar2.3 時代から存在するにも関わらず謎の存在となっている (苦笑),s2container.dicon です.

s2container.dicon
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN" 
    "http://www.seasar.org/dtd/components24.dtd">
<components>
    <include path="warmdeploy.dicon"/>
</components>

ここで WARM deploy を使用するための設定をしています.
といっても,warmdeploy.dicon をインクルードしているだけですが.
この warmdeploy.dicons2-framework-2.4.x.jar の中に含まれています.
インクルードするファイルを cooldeploy.dicon にすれば COOL deploy になります.
条件インクルードを使うと,env.txt の内容によって WARM / COOL を切り替えることができます.
HOT deploy も基本的には同じで,hotdeploy.dicon をインクルードすることになりますが,HOT の場合には Test クラスに追加のコードが必要になります (Web アプリの場合は ServletFilter がやってくれます).


最後にお馴染みのもの.

app.dicon
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN" 
    "http://www.seasar.org/dtd/components24.dtd">
<components>
</components>

空です.(^^;


それでは実行してみましょう.

DEBUG 2006-11-18 04:43:25,531 [main] クラス(org.example.service.impl.PrintServiceImpl)のコンポーネント定義を登録します
DEBUG 2006-11-18 04:43:25,531 [main] クラス(org.example.service.impl.MessageServiceImpl)のコンポーネント定義を登録します
新大陸へようこそ

デバッグメッセージで出力されているように,PringServiceImpl および MessageServiceImpl が自動登録されたことが分かります.
その後に PrintServiceImplMessageServiceImpl から取得したメッセージを出力しています.


続いてトレースインターセプタを適用してみましょう.
まずは app.diconaop.dicon をインクルードします.

app.dicon
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN" 
    "http://www.seasar.org/dtd/components24.dtd">
<components>
    <include path="aop.dicon"/>
</components>


次に,サービス用のコンポーネントカスタマイザをアスペクトカスタマイザに変更します.

customizer.dicon
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN" 
    "http://www.seasar.org/dtd/components24.dtd">
<components>
    <component name="serviceCustomizer" class="org.seasar.framework.container.customizer.AspectCustomizer">
        <property name="interceptorName">"aop.traceInterceptor"</property>
    </component>
</components>

本当はアスペクトカスタマイザを元のカスタマイザチェーンに追加する方がいいのですが,記述が多く見えるのでイカサマ.(^^;


その実行結果.

DEBUG 2006-11-18 04:46:03,265 [main] クラス(org.example.service.impl.PrintServiceImpl)のコンポーネント定義を登録します
DEBUG 2006-11-18 04:46:03,546 [main] クラス(org.example.service.impl.MessageServiceImpl)のコンポーネント定義を登録します
DEBUG 2006-11-18 04:46:03,578 [main] BEGIN org.example.service.impl.PrintServiceImpl#print()
DEBUG 2006-11-18 04:46:03,578 [main] BEGIN org.example.service.impl.MessageServiceImpl#getMessage()
DEBUG 2006-11-18 04:46:03,578 [main] END org.example.service.impl.MessageServiceImpl#getMessage() : 新大陸へようこそ
新大陸へようこそ
DEBUG 2006-11-18 04:46:03,578 [main] END org.example.service.impl.PrintServiceImpl#print() : null

見てのとおり,トレースインターセプタによるメッセージが出力されました.


いかがでしょうか?
convention.diconcreator.diconcustomizer.dicon の三点セットがちょっと鬱陶しいでしょうか?
でもでも,これらは通常 Seasar2.4 の配布ファイル中に含まれている (resouces ディレクトリにあります) ものをコピーすれば,ルートパッケージ以外はほとんど手を加える必要がありません.
Dolteng を使えばそのコピーさえ不要になります.


Seasar2.3 では AutoRegister により一つ一つのコンポーネントを設定する手間を省きました.
Seasar2.4 では SMART deploy により AutoRegister を設定する手間も省きました.
残るのはルートパッケージ名の定義とカスタマイザの微調整くらい.
着実に進歩していることを実感して頂けるかと思います.
と,おいらが偉そうに書いてますが,これらを考案したのは当然ひがさん.
さすがです♪


11/19 15:00 追記
suga さんがこのサンプルの Eclipse プロジェクトをアーカイブしたファイルを公開してくれました.
ありがとうございます.
SMART deploy を簡単に試すことができるので是非どうぞ.