Seasar DI Container with AOP

機能リファレンス

作成すべきファイル

S2Containerを使用するためには、定義ファイルを作成する必要があります。 定義ファイルは、コンポーネントを組み立てるための設計書のようなものです。 形式はXMLで、拡張子は、diconです。

S2Containerの定義

S2Containerの定義は、次のようになります。

<?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="..." class="...">
            ...
    </component>
    <component name="..." class="...">
            ...
    </component>
</components>

DOCTYPEは省略できません。diconファイルを作成する場合は、上記のサンプルをコピー&ペーストしてください。 ルートはcomponentsタグです。 コンポーネントごとに、componentタグを定義していきます。 componentタグのclass属性でコンポーネントのクラスの完全限定名を指定します。 name属性には、コンポーネント名を指定します。 詳細は、S2Container定義タグリファレンスを参照してください。

S2Containerの生成

S2Containerを生成する方法は3通りあります。

SingletonS2ContainerFactory

SingletonS2ContainerFactoryを使用するサンプルは次のようになります。

SingletonS2ContainerFactory.init();
S2Container container = SingletonS2ContainerFactory.getContainer();

何も指定しない場合、デフォルトの定義ファイルはCLASSPATHで指定されているディレクトリにあるapp.diconになります。 通常のWebアプリケーションの場合、WEB-INF/classes/app.diconです。

定義ファイルのパスを指定する場合はinit()を呼び出す前にsetConfigPath(String Path)を使用します。 引数pathはCLASSPATHで指定されているディレクトリをルートとする定義ファイルの絶対パスです。 例えば、WEB-INF/classes/aaa.dicon の場合は aaa.dicon に、 WEB-INF/classes/aaa/bbb/ccc.dicon の場合は aaa/bbb/ccc.dicon になります。セパレータは、WindowsでもUnixでも/です。

private static final String PATH = "aaa/bbb/ccc.dicon";
...
SingletonS2ContainerFactory.setConfigPath(PATH);
SingletonS2ContainerFactory.init();
S2Container container = SingletonS2ContainerFactory.getContainer();

Webアプリケーションの場合、SingletonS2ContainerFactory.init()は、 S2ContainerServletが自動的に呼び出すので、 明示的に呼び出す必要はありません。

S2ContainerFactory

S2ContainerFactoryを使用するサンプルは次のようになります。

private static final String PATH = "aaa/bbb/ccc.dicon";
...
S2Container container = S2ContainerFactory.create(PATH);

この方法で取得したコンテナのインスタンスは、アプリケーションで管理する必要があります。 S2ContainerFactoryはSingletonS2ContainerFactoryが内部的に呼び出すクラスで、 通常は意識する必要はありません。

S2ContainerServlet

Webアプリケーションの場合、S2ContainerServletがSingletonS2ContainerFactory.init()を自動的に呼び出します。
S2ContainerServletを使うためには、web.xmlに次の項目を記述します。

<servlet>
    <servlet-name>s2servlet</servlet-name>
    <servlet-class>org.seasar.framework.container.servlet.S2ContainerServlet</servlet-class>
    <init-param>
        <param-name>configPath</param-name>
        <param-value>app.dicon</param-value>
    </init-param>
    <init-param>
        <param-name>debug</param-name>
        <param-value>false</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>s2servlet</servlet-name>
    <url-pattern>/s2servlet</url-pattern>
</servlet-mapping>

configPathで定義ファイルのパスを指定します。

debugパラメータをtrueにすると、次のようにして稼動中にS2Containerを再起動できます。 xxxはWebアプリケーションのコンテキスト名です。

http://localhost:8080/xxx/s2servlet?command=restart         

また、次のようにS2Containerにデプロイされているコンポーネントの定義を見ることもできます。

http://localhost:8080/xxx/s2servlet?command=list         


S2ContainerServletは、他のサーブレットよりもはやく起動されるようにload-on-startupタグを調整してください。

コンポーネントの取得

S2Containerからコンポーネントを取り出すには、次のメソッドを使用します。

S2Container.getComponent(Object componentKey)

componentKeyには、コンポーネントのクラスもしくはコンポーネント名を指定できます。

コンポーネントのクラスを指定する場合、コンポーネント instanceof クラスがtrueを返すクラスを指定することができます。 S2Containerの中に指定したクラスを実装しているコンポーネントが複数ある場合、 S2Containerは、どのコンポーネントを返せばよいのか判断できないため、 TooManyRegistrationRuntimeExceptionが発生します。 実装コンポーネントがユニークに決まるクラスを指定してください。

コンポーネント名で取得することもできます。 その場合も、同一の名前をもつコンポーネントが複数登録されている場合、 TooManyRegistrationRuntimeExceptionが発生します。 コンポーネント名指定の場合、スペルミスをする可能性もあるので、 できるだけクラス指定のほうが良いでしょう。

Java5でS2Tigerを利用している場合、SingletonS2Containerを使うことでキャストを不要にできます。 SingletonS2Containerを使う場合、あらかじめSingletonS2ContainerFactoryで、 S2Containerを初期化しておく必要があります。 SingletonS2Containerを利用するにはSeasar2.4以上が必要です。

Hoge hoge = (Hoge) container.getComponent(Hoge.class);
Hoge hoge = SingletonS2Container.getComponent(Hoge.class);
Hoge hoge = (Hoge) container.getComponent("hoge");
Hoge hoge = SingletonS2Container.getComponent("hoge");

Dependency Injectionのタイプ

Dependency Injectionには次のようなタイプがあります。 コンストラクタ・インジェクション、セッター・インジェクション、メソッド・インジェクションは組み合わせて使うこともできます。

コンストラクタ・インジェクション

コンストラクタの引数にDIを行なうのがコンストラクタ・インジェクションです。
コンストラクタの引数には、componentタグの子タグであるargタグを使って指定します。

<components>
    <component name="..." class="...">
        <arg>...</arg>
    </component>
</components>

セッター・インジェクション

セッターメソッドにDIを行なうのがセッター・インジェクションです。
コンポーネントのプロパティには、componentタグの子タグであるpropertyタグを使って指定します。
プロパティ名はname属性で指定します。

<components>
    <component name="..." class="...">
        <property name="...">...</property>
    </component>
</components>

メソッド・インジェクション

メソッドにDIを行なうのがメソッド・インジェクションです。
コンポーネントのメソッドには、componentタグの子タグであるinitMethodタグを使って指定します。
メソッド名はname属性で指定します。
initMethodタグの子タグであるargタグで引数を指定することや、name属性を省略してボディでOGNL式を使うこともできます。

<components>
    <component name="..." class="...">
        <initMethod name="...">
            <arg>...</arg>
        </initMethod>
    </component>
</components>

フィールド・インジェクション

フィールドにDIを行なうのがフィールド・インジェクションです。
フィールドには、Bindingアノテーションを使って指定します。この機能は、2.4から利用できます。

@Binding("foo")
private Foo foo;

バージョン2.4.17からは、publicなフィールドをプロパティとして認識するようになったので、 publicフィールドを定義しておけば自動バインディングが 適用されます。上記のfooプロパティの例は次のようになります。 publicフィールドにBindingアノテーションを指定することもできます。

public Foo foo;

インスタンス管理

componentタグのinstance属性で、 コンポーネントのインスタンスをどのように管理するのかを指定します。

instance属性 説明
singleton(default) S2Container.getComponent()を何度呼び出しても同じインスタンスが返されます。
prototype S2Container.getComponent()を呼び出すたびに新たなインスタンスが返されます。
request リクエスト毎に1つのインスタンスが作成されます。 name属性に指定した名前で、コンポーネントがリクエストに格納されます。 requestを利用するためにはS2ContainerFilterの設定が必要です。
session セッション毎に1つのインスタンスが作成されます。 name属性に指定した名前で、コンポーネントがセッションに格納されます。 sessionを利用するためにはS2ContainerFilterの設定が必要です。
application Servletを使う場合は、ServletContext毎に1つのインスタンスが作成されます。 name属性に指定した名前で、コンポーネントがServletContextに格納されます。 applicationを利用するためにはS2ContainerFilterの設定が必要です。
outer コンポーネントのインスタンスは、S2Container外で作成し、Dependency Injectionだけを行います。アスペクトコンストラクタ・インジェクションは適用できません。

自動バインディング

DIの指定を省略した場合、 コンポーネント間の依存関係は、型および名前によってS2Containerが自動的に解決します。 componentタグのautoBinding属性を指定することで細かく制御することもできます。

autoBinding 説明
auto(default) コンストラクタとプロパティの自動バインディングが適用されます。
constructor コンストラクタの自動バインディングが適用されます。
property プロパティの自動バインディングが適用されます。
none 自動バインディンは適用されません。

コンストラクタの自動バインディングのルールは次の順番で適用されます。

  • コンストラクタ・インジェクションが明示的に指定されている場合、 自動バインディングは適用されません。
  • 引数のないデフォルトコンストラクタが定義されている場合、 自動バインディングは適用されません。
  • 引数の型がすべてインターフェースで最も引数の数の多いコンストラクタを選択され、 その引数の型のコンポーネントが設定されます。

プロパティの自動バインディングのルールは次の順番で適用されます。

  • セッター・インジェクションが明示的に指定されている場合、 自動バインディングは適用されません。
  • プロパティの型を実装したコンポーネントがコンテナに1つ登録されていて、 プロパティ名とコンポーネント名が一致する場合、 そのコンポーネントが設定されます。 ただい、コンポーネント名にaaa_のようにサブパッケージ名がプレフィックスでついている場合、 にサブパッケージ名のプレフィックスは除いて、プロパティ名と比較します。 コンポーネント名については、こちらを参照してください。
  • プロパティ名と同一のコンポーネントがコンテナに登録されていて、 プロパティに代入可能なら、 そのコンポーネントが設定されます。
  • プロパティの型がインターフェースの場合、 コンテナをその型で検索し、見つかったコンポーネントが設定されます。 ただし、複数のコンポーネントが見つかった場合には、TooManyRegistrationRuntimeExceptionがスローされます。
  • プロパティの型がインターフェースの配列の場合、 コンテナをその型で検索し、見つかったコンポーネントの配列が設定されます。

propertyタグのbindingType属性で、プロパティごとに細かく制御することもできます。

bindingType 説明
must 自動バインディングが適用されない場合、例外が発生します。
should(default) 自動バインディングが適用されない場合、警告を通知します。
may 自動バインディングが適用されない場合、何もおきません。
none 自動バインディングは適用されません。

S2Containerのライフサイクル

Webアプリケーションの場合、S2ContainerServletの初期化時にS2Container.init()が呼び出され、 S2ContainerServletの終了時にS2Container.destroy()が呼び出されます。

S2Container.init()が呼び出されたときに、シングルトンのコンポーネントは初期化され、 S2Container.destroy()が呼び出されたときに、シングルトンのコンポーネントは破棄されることになります。

Webアプリケーション以外の場合、明示的にS2Container.init()、 S2Container.destroy()を呼び出す必要があります。

コンポーネントのライフサイクル

コンポーネントを初期化するメソッドをinitMethodタグで指定できます。 シングルトンのコンポーネントはS2Container.init()時に初期化されますが、 それ以外のコンポーネントは取得されるときに初期化が行なわれます。

コンポーネントを破棄するメソッドをdestroyMethodタグで指定できます。 シングルトンのコンポーネントはS2Container.destroy()時に破棄されます。 それ以外のコンポーネントはdestroyMethodを指定しても無視されます。

initMethodはコンポーネントがコンテナに登録した順番に実行され、 destroyMethodはその逆順に呼び出されることになります。

HashMapを使ったサンプルは、次のようになります。

<components>
    <component class="java.util.HashMap">
        <initMethod name="put">
            <arg>"aaa"</arg>
            <arg>111</arg>
        </initMethod>
        <destroyMethod name="remvoe">
            <arg>"aaa"</arg>
        </destroyMethod>
    </component>
</components>

環境名

環境名とは実行環境を表現する名前で、env.txtというテキストファイルによって指定します。 このenv.txtファイルはクラスパス上に配置します。 WebアプリケーションではWEB-INF/classesになります。 いくつかの機能で、環境名によって処理をカスタマイズすることができます。 この機能はSeasar2.4以上が必要です。

標準的な名称は次のようになります。

  • ut
    単体テスト環境を表します。
  • ct
    結合テスト環境を表します。
  • it
    統合テスト環境を表します。
  • product
    運用環境を表します。env.txtファイルが存在しない場合のデフォルトです。

定義ファイルのインクルード

定義ファイルに別の定義ファイルをインクルードすることができます。

<components>
    <include path="bar.dicon"/>
</components>

includeタグのpath属性で定義ファイルのパスを指定します。
コンポーネントを探す場合、最初に自分自身に登録されているコンポーネントを探し、 見つからない場合は、インクルードいる順に子供の定義ファイルを検索し、 最初に見つかったコンポーネントが対象になります。

定義ファイルの差し替え

インクルード元の定義ファイルを変更することなくインクルード先の定義ファイルを切り替えたい場合があります。 記述上はデフォルトの定義ファイルをインクルードしているが実際にはデフォルト以外の定義ファイルを利用したいという場合です。

このような場合に利用できるのが定義ファイルの差し替えです。

定義ファイルを差し替えるには、s2container.diconにSimplePathResolverのコンポーネントを定義し、差し替え対象の定義ファイルと実際に利用したい定義ファイルを対応付けます。 例えば、デフォルトのjta.diconをWAS6用のjta-was6.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 condition="#ENV == 'ut'" path="hotdeploy.dicon"/>
    <include condition="#ENV != 'ut'" path="cooldeploy.dicon"/>

    <component class="org.seasar.framework.container.factory.SimplePathResolver">
        <initMethod name="addRealPath">
            <arg>"jta.dicon"</arg>
            <arg>"jta-was6.dicon"</arg>
        </initMethod>
    </component>
</components>

この場合、インクルード元の定義ファイルが次のように記述されていれば実際にはjta.diconではなくjta-was6.diconがインクルードされます。

<components>
    <include path="jta.dicon"/>
</components>

定義ファイルの差し替え機能は、インクルード元の定義ファイルがjarファイル内にあり直接変更できない場合や、インクルード元の定義ファイルが複数あり一括でインクルード先を切り替えたい場合に役立ちます。

定義ファイルの条件インクルード

一つのアプリケーションで利用されるdiconファイルであっても、 状況によって異なった設定が必要になることがあります。 RDBMSの接続情報などは、環境によって変える必要があることがほとんどです、

このような場合に、diconファイルの記述を変更することなく、 環境に応じた設定をするための機能が条件インクルードです。 条件インクルードでは、環境ごとに必要な設定を記述したdiconファイルをインクルードすることができます。

条件インクルードは環境別のdiconファイルをインクルードするために、2つの方法を提供します。

  • 暗黙的な条件インクルード
    includeタグのpath属性で指定されたdicon名に、 環境名(環境を表す名前)をサフィックスとして持つdiconがあればそれをインクルードする機能です。
  • 明示的な条件インクルード
    OGNL式によって指定した条件が真となる場合のみdiconをインクルードする機能です。
暗黙的な条件インクルード

暗黙的な条件インクルードとは、includeタグのpath属性で指定されたdicon名に、 環境名をサフィックスとして持つdiconがあればそれをインクルードする機能です。

暗黙的な条件インクルードを使う場合,インクルードする側の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="foo.dicon"/>
</components>

インクルードされる側のdiconは、拡張子を除いたファイル名の最後にサフィックスを付加します。 サフィックスは,アンダースコアと環境名をつなげたものです。

  • foo_ut.dicon
    環境名がutの場合にインクルードされます。
  • foo_ct.dicon
    環境名がctの場合にインクルードされます。
  • foo.dicon
    環境名がutでもctでもない場合にインクルードされます。
明示的な条件インクルード

より複雑な状況でインクルードするdiconを切り替えたい場合には、 includeタグのcondition属性でインクルードする条件をOGNL式で明示的に指定することができます。 この場合には、指定された条件が満たされた場合のみ、指定された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 condition="#ENV == 'ct'" path="hotdeploy.dicon"/>
    <include condition="#ENV != 'ct'" path="cooldeploy.dicon"/>
</components>

この例では、#ENVで参照することのできる環境名が'ct'であればhotdeploy.diconを、 それ以外であればcooldeploy.diconをインクルードします。

includeタグのcondition属性には、 staticメソッドの呼び出しを含む任意のOGNL式を記述することができます。 OGNL式については「OGNLガイド」を参照してください。

条件インクルードはSeasar2.4以上が必要です。

設定ファイルの埋め込みインクルード(XInclude)

XInclude (XML Inclusion) は、 複数の XML ドキュメントを一つの XML ドキュメントに統合するための仕様です。 Seasar2.4 で導入された XInclude のサポートにより、複数の dicon ファイルを一つの dicon ファイルのように扱うことが可能になります。

前提条件

XInclude を利用するには、Java5 以降でかつXInclude に対応した XML パーサが必要です。 Java5 にバンドルされているXML パーサは XInclude に対応しています。

Teeda、S2JSF、Mayaa 等にバンドルされている Xerces 2.6.2 は XInclude に対応していません。 Xerces 2.6.2 は Tomcat 5.0 でも利用されています。 これらの環境で XInclude を使用するには次の設定が必要です.

  • META-INF/servicesディレクトリを作成し、 javax.xml.parsers.SAXParserFactory というファイルを作成し、 以下の内容を記述します (SunのJDK/JREの場合)。
com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl

XInclude の使い方

XInclude を使って他の 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
    xmlns:xi="http://www.w3.org/2001/XInclude"
>
    <xi:include href="/foo.dicon"/>
    <xi:include href="/bar.dicon"/>
</components>
  • 文書型宣言で Seasar2.4 の DTD を指定します。
  • componentsタグで XInclude の名前空間を宣言します。
  • componentsの子タグに xi:include タグを記述します。 href 属性で取り込む dicon ファイルのパスを指定します。 パスの先頭は必ずスラッシュ ('/') にしてください.

これにより、foo.diconbar.dicon の内容 (components 子タグ) が取り込まれます。 xi:include 要素は component タグと混在して記述することができます。

foo.diconbar.dicon が次の内容だった場合

foo.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="foo1">"Foo1"</component>
    <component name="foo2">"Foo2"</component>
</components>

bar.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="bar1">"Bar1"</component>
    <component name="bar2">"Bar2"</component>
</components>

最初に示した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
    xmlns:xi="http://www.w3.org/2001/XInclude"
>
    <component name="foo1">"Foo1"</component>
    <component name="foo2">"Foo2"</component>
    <component name="bar1">"Bar1"</component>
    <component name="bar2">"Bar2"</component>
</components>

includeタグとの違い

<include>タグと XInclude (<xi:include>) は一見似ているようですが、実際には大きく異なります。

次のように <include> を利用すると、それぞれの dicon ファイルを読み込んだ S2Containerが合計 3 つインスタンス化されます。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN"
    "http://www.seasar.org/dtd/components24.dtd"
>
<components
    xmlns:xi="http://www.w3.org/2001/XInclude"
>
    <include path="foo.dicon"/>
    <include path="bar.dicon"/>
</components>

親となる dicon に定義されたコンポーネントからは foo.diconbar.dicon に定義されたコンポーネントが見えます (DI の対象となる)が、 foo.dicon に定義されたコンポーネントからは 親の dicon に定義されたコンポーネントも bar.dicon に定義されたコンポーネントも見えません (DI の対象とならない)。 bar.dicon に定義されたコンポーネントも 親の dicon に定義されたコンポーネントや foo.dicon に定義されたコンポーネントが見えません (DI の対象とならない)。

一方,XInclude を利用した場合には,親のdiconも,foo.dicon および bar.dicon も一つの S2 コンテナに読み込まれるため、 どこの dicon に定義したコンポーネントからであっても他の dicon に定義されたコンポーネントが見えます (DI の対象となる)。

また、次のようにaaa.diconとbbb.diconから同じjdbc.diconをXIncludeしたとします。

aaa.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
    xmlns:xi="http://www.w3.org/2001/XInclude"
>
    <xi:include path="jdbc.dicon"/>
</components>
bbb.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
    xmlns:xi="http://www.w3.org/2001/XInclude"
>
    <xi:include path="jdbc.dicon"/>
</components>

この場合、jdbc.diconの中身は、aaa.diconとbbb.diconに物理的に別のものとしてコピーされます。 一方、includeタグを使った場合は、aaa.diconとbbb.diconに取り込まれたjdbc.diconは同じものをさしています。

XIncludeは1つのdiconファイルを物理的に分割する場合に使ってください。

名前空間

定義ファイルを分割した場合に、複数の定義ファイルで名前が衝突しないように、 componentsタグのnamespace属性で名前空間を指定することができます。

foo.dicon
<components namespace="foo">
    <component name="aaa" .../>
    <component name="bbb" ...>
        <arg>aaa</arg>
    </component>
</components>
bar.dicon
<components namespace="bar">
    <include path="foo.dicon"/>
    <component name="aaa" .../>
    <component name="bbb" ...>
        <arg>aaa</arg>
    </component>
    <component name="ccc" ...>
        <arg>foo.aaa</arg>
    </component>
</components>
app.dicon
<components>
    <include path="bar.dicon"/>
</components>

同じ定義ファイルのコンポーネントは名前空間なしで参照できます。 他の定義ファイルのコンポーネントを参照する場合は、名前空間.をコンポーネント名の頭につけます。 foo.aaaとbar.aaaは同じ名前がついていますが、名前空間が異なっているので、違うコンポーネントになります。

S2ContainerFilter

request、session、applicationでインスタンス管理をしたい場合、 S2ContainerFilterをweb.xmlに登録します。

<web-app>
    <filter>
        <filter-name>s2filter</filter-name>
        <filter-class>org.seasar.framework.container.filter.S2ContainerFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>s2filter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

暗黙的なコンポーネント

定義ファイルに記述しなくても暗黙的に使えるコンポーネントが存在します。 これらのコンポーネントは、次のようにプロパティを定義しておけば自動的にDIされます。

S2Container container;

HttpServletRequest request;

HttpServletResponse response;

ServletContext application;

//ServletContextの属性にMapインターフェースでアクセスできます。
Map applicationScope;

//ServletContextの初期化パラメータにMapインターフェースでアクセスできます。
Map initParam;

//HttpSessionの属性にMapインターフェースでアクセスできます。
Map sessionScope;

//HttpServletRequestの属性にMapインターフェースでアクセスできます。
Map requestScope;

//Cookieの属性にMapインターフェースでアクセスできます。
Map cookie;

//HttpServletRequestのheaderにMapインターフェースでアクセスできます。戻り値はStringです。
Map header;

//HttpServletRequestのheaderにMapインターフェースでアクセスできます。戻り値はStringの配列です。
Map headerValues;

//HttpServletRequestのパラメータにMapインターフェースでアクセスできます。戻り値はStringです。
Map param;

//HttpServletRequestのパラメータにMapインターフェースでアクセスできます。戻り値はStringの配列です。
Map paramValues;

containerはコンポーネント自身が登録されているS2Containerになります。 ルートのS2Containerはcontainer.getRoot()で取得します。

container以外のコンポーネントを使うにはS2ContainerFilterが必要です。

AOPの適用

コンポーネントにアスペクトを適用することもできます。 詳しい説明は、S2AOPを参照してください。

メタデータ

components、component、arg、propertyタグにメタデータを指定することもできます。metaタグはメタデータを指定したいタグの子タグに指定します。例えば、componentsタグにメタデータを指定したい場合次のようにします。

<components>
    <meta name="aaa">111</meta>
</components>

コンポーネントの自動登録

自動バインディングにより、DIの設定はほぼ自動化できます。 さらに、コンポーネントの登録も自動化してしまおうというのが、コンポーネントの自動登録機能です。

org.seasar.framework.container.autoregister.FileSystemComponentAutoRegister

ファイルシステムからクラスを検索としてコンポーネントを自動登録するコンポーネントです。

プロパティ 説明
instanceDef 自動登録されるコンポーネントに適用するInstanceDefを指定します。XMLで指定する場合、
@org.seasar.framework.container.deployer.InstanceDefFactory@REQUEST
のように指定します。
autoBindingDef 自動登録されるコンポーネントに適用するAutoBindingDefを指定します。XMLで指定する場合、
@org.seasar.framework.container.assembler.AutoBindingDefFactory@NONE
のように指定します。
autoNaming クラス名からコンポーネント名を自動的に決定するコンポーネント。org.seasar.framework.container.autoregister.AutoNamingインターフェースを実装している必要があります。デフォルトは、org.seasar.framework.container.autoregister.DefaultAutoNamingクラスのインスタンスになります。

メソッド 説明
addClassPattern 自動登録したいクラスパターンを登録します。最初の引数は、コンポーネントのパッケージ名です。子供のパッケージも再帰的に検索します。2番目の引数は、クラス名です。正規表現が使えます。「,」区切りで複数記述することもできます。
addIgnoreClassPattern 自動登録したくないクラスパターンを登録します。最初の引数は、コンポーネントのパッケージ名です。子供のパッケージも再帰的に検索します。2番目の引数は、クラス名です。正規表現が使えます。「,」区切りで複数記述することもできます。
org.seasar.framework.container.autoregister.JarComponentAutoRegister

Jarファイルの中からクラスを検索としてコンポーネントを自動登録するコンポーネントです。

プロパティ 説明
jarFileNames 対象のjarファイル名を指定します。正規表現も使えます。ただし、拡張子は含まれません。複数指定する場合は、「,」で区切ります。例えば、myapp.*, yourapp.*のようになります。
referenceClass このプロパティで指定されたクラスが存在するjarファイルの親ディレクトリをベースのディレクトリ(例えば、WEB-INF/lib)とみなします。デフォルトは、org.aopalliance.intercept.MethodInterceptor.classになります。
instanceDef 自動登録されるコンポーネントに適用するInstanceDefを指定します。XMLで指定する場合、
@org.seasar.framework.container.deployer.InstanceDefFactory@REQUEST
のように指定します。
autoBindingDef 自動登録されるコンポーネントに適用するAutoBindingDefを指定します。XMLで指定する場合、
@org.seasar.framework.container.assembler.AutoBindingDefFactory@NONE
のように指定します。
autoNaming クラス名からコンポーネント名を自動的に決定するコンポーネント。org.seasar.framework.container.autoregister.AutoNamingインターフェースを実装している必要があります。デフォルトは、org.seasar.framework.container.autoregister.DefaultAutoNamingクラスのインスタンスになります。

メソッド 説明
addClassPattern 自動登録したいクラスパターンを登録します。最初の引数は、コンポーネントのパッケージ名です。子供のパッケージも再帰的に検索します。2番目の引数は、クラス名です。正規表現が使えます。「,」区切りで複数記述することもできます。
addIgnoreClassPattern 自動登録したくないクラスパターンを登録します。最初の引数は、コンポーネントのパッケージ名です。子供のパッケージも再帰的に検索します。2番目の引数は、クラス名です。正規表現が使えます。「,」区切りで複数記述することもできます。
org.seasar.framework.container.autoregister.ComponentAutoRegister

ファイルシステムまたはJarファイルからクラスを検索としてコンポーネントを自動登録するコンポーネントです。

プロパティ 説明
instanceDef 自動登録されるコンポーネントに適用するInstanceDefを指定します。XMLで指定する場合、@org.seasar.framework.container.deployer.InstanceDefFactory@REQUESTのように指定します。
autoBindingDef 自動登録されるコンポーネントに適用するAutoBindingDefを指定します。XMLで指定する場合、
@org.seasar.framework.container.assembler.AutoBindingDefFactory@NONE
のように指定します。
autoNaming クラス名からコンポーネント名を自動的に決定するコンポーネント。org.seasar.framework.container.autoregister.AutoNamingインターフェースを実装している必要があります。デフォルトは、org.seasar.framework.container.autoregister.DefaultAutoNamingクラスのインスタンスになります。

メソッド 説明
addReferenceClass このメソッドで指定されたクラスが存在するディレクトリまたはJarファイルを基点としてクラスを検索します。
addClassPattern 自動登録したいクラスパターンを登録します。最初の引数は、コンポーネントのパッケージ名です。子供のパッケージも再帰的に検索します。2番目の引数は、クラス名です。正規表現が使えます。「,」区切りで複数記述することもできます。
addIgnoreClassPattern 自動登録したくないクラスパターンを登録します。最初の引数は、コンポーネントのパッケージ名です。子供のパッケージも再帰的に検索します。2番目の引数は、クラス名です。正規表現が使えます。「,」区切りで複数記述することもできます。

AutoNaming

AutoNamingによってコンポーネントに自動的に名前をつけることができます。

org.seasar.framework.container.autoregister.DefaultAutoNaming

クラスの完全修飾名からパッケージ部分を除き、最後がImplまたはBeanで終わっていたら削除し、 先頭を小文字にした名前をコンポーネントの名前に設定します。 例えば、aaa.HogeImplクラスの場合、コンポーネント名は、hogeになります。

プロパティ 説明
decapitalize コンポーネント名の先頭を小文字にする場合はtrueを指定します。デフォルトはtrueです。

メソッド 説明
setCustomizedName デフォルトのルールに従わないクラスを登録します。最初の引数は、クラスの完全修飾名です。 2番目の引数は、コンポーネント名です。
addIgnoreClassSuffix クラス名の末尾から削除する部分を指定します。デフォルトでImplおよびBeanが登録されています。
addReplaceRule 正規表現による置換ルールを追加します。最初の引数は正規表現です。2番目の引数は置換文字列です。
clearReplaceRule setCustomizedName、addIgnoreClassSuffix、addReplaceRuleで登録した変換規則をクリアします。デフォルトで登録されているImplおよびBeanもクリアされます。
org.seasar.framework.container.autoregister.QualifiedAutoNaming

パッケージ名またはその一部で修飾されたクラス名をコンポーネントの名前に設定します。 クラスの完全修飾名から最後がImplまたはBeanで終わっていたら削除し、 先頭を小文字、ピリオドの直後を大文字にした名前をコンポーネントの名前に設定します。
パッケージの先頭部分から不要な部分を削除することができます。
例えば、aaa.bbb.ccc.ddd.HogeImplクラスの場合で、先頭のaaa.bbbを削除するように指定した場合のコンポーネント名は、cccDddHogeになります。

プロパティ 説明
decapitalize コンポーネント名の先頭を小文字にする場合はtrueを指定します。デフォルトはtrueです。

メソッド 説明
setCustomizedName デフォルトのルールに従わないクラスを登録します。最初の引数は、クラスの完全修飾名です。 2番目の引数は、コンポーネント名です。
addIgnorePackagePrefix パッケージ名の先頭から削除する部分を指定します。
addIgnoreClassSuffix クラス名の末尾から削除する部分を指定します。デフォルトでImplおよびBeanが登録されています。
addReplaceRule 正規表現による置換ルールを追加します。最初の引数は正規表現です。2番目の引数は置換文字列です。
clearReplaceRule setCustomizedName、 addIgnorePackagePrefix、 addIgnoreClassSuffix、 addReplaceRuleで登録した変換規則をクリアします。デフォルトで登録されているImplおよびBeanもクリアされます。
<component
  class="org.seasar.framework.container.autoregister.FileSystemComponentAutoRegister">
    <property name="autoNaming">
        <component class="org.seasar.framework.container.autoregister.DefaultAutoNaming">
            <initMethod name="setCustomizedName">
                <arg>"examples.di.impl.HogeImpl"</arg>
                <arg>"hoge2"</arg>
            </initMethod>
        </component>
    </property>
    <initMethod name="addClassPattern">
        <arg>"examples.di.impl"</arg>
        <arg>".*Impl"</arg>
    </initMethod>
</component>
<component class="org.seasar.framework.container.autoregister.JarComponentAutoRegister">
    <property name="referenceClass">
        @junit.framework.TestSuite@class
    </property>
    <property name="jarFileNames">"junit.*"</property>
    <initMethod name="addClassPattern">
        <arg>"junit.framework"</arg>
        <arg>"TestSuite"</arg>
    </initMethod>
</component>
<component class="org.seasar.framework.container.autoregister.ComponentAutoRegister">
    <initMethod name="addReferenceClass">
        <arg>@aaa.bbb.ccc.ddd.HogeImpl@class</arg>
    </initMethod>
    <initMethod name="addClassPattern">
        <arg>"aaa.bbb"</arg>
        <arg>".*Impl"</arg>
    </initMethod>
</component>

アスペクトの自動登録

コンポーネントの自動登録により、コンポーネントの登録は自動化できます。 さらに、アスペクトの登録も自動化してしまおうというのが、アスペクトの自動登録機能です。 アスペクトについては、aspectタグを参照してください。

コンポーネントの自動登録と組み合わせる場合は、コンポーネントの自動登録の設定よりも後に、アスペクトの自動登録の設定を記述する必要があります。 アスペクトを適用されるコンポーネントは、アスペクトの自動登録の設定よりも後に記述する必要があります。

<components>
    <!-- 1.コンポーネントの自動登録 -->
    <component class="org.seasar.framework.container.autoregister.ComponentAutoRegister">
        ...
    </component>

    <!-- 2.アスペクトの自動登録 -->
    <component class="org.seasar.framework.container.autoregister.AspectAutoRegister">
        ...
    </component>

    <!-- 3.その他のコンポーネント -->
    <component class="...">
        ...
    </component>
    ...
<components>
org.seasar.framework.container.autoregister.AspectAutoRegister

クラス名のパターンを指定してアスペクトを自動登録するコンポーネントです。

プロパティ 説明
interceptor インターセプタを指定します。複数のインターセプタを指定したい場合は、InterceptorChainを使ってください。
pointcut インターセプタを適用するメソッドをカンマ区切りで指定します。 pointcutを指定しない場合は、コンポーネントが実装しているインターフェースのすべてのメソッドが対象になります。 インターフェースを実装していない場合、Object.classに属していないpublicでfinalではないメソッドが対象になります。 メソッド名には正規表現(JDK1.4のregex)も使えます。

メソッド 説明
addClassPattern 自動登録したいクラスパターンを登録します。最初の引数は、コンポーネントのパッケージ名です。子供のパッケージも再帰的に検索します。2番目の引数は、クラス名です。正規表現が使えます。「,」区切りで複数記述することもできます。
addIgnoreClassPattern 自動登録したくないクラスパターンを登録します。最初の引数は、コンポーネントのパッケージ名です。子供のパッケージも再帰的に検索します。2番目の引数は、クラス名です。正規表現が使えます。「,」区切りで複数記述することもできます。

<include path="aop.dicon"/>
...
<component
class="org.seasar.framework.container.autoregister.AspectAutoRegister">
<property name="interceptor">aop.traceInterceptor</property>
<initMethod name="addClassPattern">
<arg>"examples.di.impl"</arg>
<arg>".*Impl"</arg>
</initMethod>
</component>
org.seasar.framework.container.autoregister.InterfaceAspectAutoRegister

あるインタフェースを実装したクラスに対してアスペクトを自動登録するコンポーネントです。

プロパティ 説明
interceptor インターセプタを指定します。複数のインターセプタを指定したい場合は、InterceptorChainを使ってください。
targetInterface 指定したインターフェースを実装しているコンポーネントに対して、アスペクトを適用します。
<include path="aop.dicon"/>
...
<component
class="org.seasar.framework.container.autoregister.InterfaceAspectAutoRegister">
<property name="interceptor">aop.traceInterceptor</property> <property name="targetInterface">@examples.Greeing@class</property>
</component>

メタデータの自動登録

メタデータも自動登録することができます。

コンポーネントの自動登録と組み合わせる場合は、コンポーネントの自動登録の設定よりも後に、メタデータの自動登録の設定を記述する必要があります。 メタデータを参照するコンポーネントは、メタデータの自動登録の設定よりも後に記述する必要があります。

<components>
    <!-- 1.コンポーネントの自動登録 -->
    <component class="org.seasar.framework.container.autoregister.ComponentAutoRegister">
        ...
    </component>

    <!-- 2.メタデータの自動登録 -->
    <component class="org.seasar.framework.container.autoregister.MetaAutoRegister">
        ...
    </component>

    <!-- 3.その他のコンポーネント -->
    <component class="...">
        ...
    </component>
    ...
<components>
org.seasar.framework.container.autoregister.MetaAutoRegister

クラス名のパターンを指定してメタデータを自動登録するコンポーネントです。
自動登録されるメタデータは、このコンポーネント自身の定義にautoRegisterという名前を持つメタデータの子として記述します。

メソッド 説明
addClassPattern 自動登録したいクラスパターンを登録します。最初の引数は、コンポーネントのパッケージ名です。子供のパッケージも再帰的に検索します。2番目の引数は、クラス名です。正規表現が使えます。「,」区切りで複数記述することもできます。
addIgnoreClassPattern 自動登録したくないクラスパターンを登録します。最初の引数は、コンポーネントのパッケージ名です。子供のパッケージも再帰的に検索します。2番目の引数は、クラス名です。正規表現が使えます。「,」区切りで複数記述することもできます。
<component
  class="org.seasar.framework.container.autoregister.MetaAutoRegister">
    <meta name="autoRegister">
        <meta name="hoge"</meta>
    </meta>
    <initMethod name="addClassPattern">
        <arg>"examples.di.impl"</arg>
        <arg>".*Impl"</arg>
    </initMethod>
</component>

この例では、hogeという名前のメタデータが他のコンポーネント定義に自動登録されます。

SMART deploy

コンポーネントの自動登録アスペクトの自動登録によりほとんど定義ファイルを書く必要はなくなりました。 しかし、コンポーネントのソースコードを書き換えるたびにアプリケーションサーバを再起動しなければならないという問題は、 まだ解決されていません。

この問題を解決するために開発されたのが、Seasar2.4から登場したHOT deploy機能です。 HOT deployを使うと、アプリケーションサーバを起動したままで、 ソースコードの修正が即座に反映されるので、「さくさく感のある開発」が可能になります。

開発時には便利なHOT deployですが、若干のオーバーヘッドがあり、 運用時には有効な機能だとはいえません。 運用時には、パフォーマンスや安定性が求められるでしょう。 このために用意されているのが、COOL deployです。 COOL deployを使うと、アプリケーションの起動時にすべてのデプロイを行い、 クラスのタイムスタンプチェックなどは一切行なわないので、 オーバヘッドなしで本来のパフォーマンスを発揮できます。

HOT deployとCOOL deployの中間に位置するのがWARM deployです。 JUnitなどでテストをすることを思い浮かべてください。 テストを実行しているときに、ソースコードを修正することはないので、 HOT deployの機能は不要です。 しかし、COOL deployのようにすべてのデプロイが、テストメソッドごとに行なわれると、 テストの時間が無駄に長くなってしまいます。 必要なコンポーネントだけがデプロイされるのが望ましいでしょう。 このように必要なコンポーネントだけがデプロイされ、 修正を即座に認識しないのがWARM deployの機能です。

HOT deploy、COOL deploy、WARM deployをあわせてSMART deployと呼びます。

HOT deployは、各開発者が個人のPCでテストするときに使うことを想定しています。 そのため、リクエストを複数同時に処理することはできません。 複数の開発者で使うときは、HOT deployは使わないようにしてください。

SMART deployの切り替え

SMART deployの切り替えは、環境名を変えるだけでOKです。 例えば、次のようにs2container.diconが設定されている場合、 utのときにはWARM deploy、ctのときはHOT deploy、それ以外はCOOL deployになります。

<components>
    <include condition="#ENV == 'ut'" path="warmdeploy.dicon"/>
    <include condition="#ENV == 'ct'" path="hotdeploy.dicon"/>
    <include condition="#ENV != 'ut' and #ENV != 'ct'" path="cooldeploy.dicon"/>
</components>

s2container.diconはS2Containerをカスタマイズするための定義ファイルです。 クラスパスの通っているディレクトリにおいておくだけでS2Containerが自動認識します。 通常は、WEB-INF/classesに配置します。

SMART deployのPackage構成

SMART deployで推奨しているパッケージ構成は次のようになります。

examples.aaa
examples.aaa.web.xxx
examples.aaa.web.yyy
examples.aaa.entity
examples.aaa.dao
examples.aaa.dto
examples.aaa.service
examples.aaa.util
examples.aaa.helper
examples.aaa.interceptor
examples.aaa.converter
examples.aaa.validator

examples.aaaをルートパッケージと呼びます。 ルートパッケージの配下に役割に応じてパッケージを配置します。

ルートパッケージは、convention.diconで指定します。

<component class="org.seasar.framework.convention.impl.NamingConventionImpl">
    <initMethod name="addRootPackageName">
        <arg>"examples.aaa"</arg>
    </initMethod>
</component>

addRootPackage()メソッドの第2引数にfalseを指定することで、 ルートパッケージはHOT deploy非対象になります。 この場合、SMART deployによるコンポーネントの自動登録は行われますが, HOT deployモードでコンポーネントがクラスローダにロードされた後にクラスを変更しても反映されません。

<component class="org.seasar.framework.convention.impl.NamingConventionImpl">
    <initMethod name="addRootPackageName">
        <arg>"examples.aaa"</arg>
        <arg>false</arg>
    </initMethod>
</component>

examples.aaa.web.xxx、examples.aaa.web.yyyをサブアプリケーションパッケージと呼びます。 xxx、yyyがサブアプリケーション名になります。 サブアプリケーションはユースケースと置き換えても良いでしょう。 複数の関連のある画面を1つにまとめたものがサブアプリケーションです。 典型的なWebアプリケーションは、一覧画面、編集画面、確認画面でサブアプリケーションが構成されます。

サブアプリケーションパッケージに格納するのは、ユースケース固有のクラスです。 HTMLと一対一に結びつくPageクラスやPageクラスとDTOの変換を行なうDxoクラスが、 サブアプリケーションパッケージに格納する代表的なクラスです。

examples.aaa.entityをエンティティパッケージと呼びます。 このパッケージには、テーブルと一対一に対応するEntityクラスを格納します。

examples.aaa.daoをDaoパッケージを呼びます。 このパッケージには、Entityと一対一に対応するDaoクラスを格納します。

examples.aaa.dtoをDtoパッケージを呼びます。 このパッケージには、DTOクラスを格納します。

examples.aaa.serviceをサービスパッケージと呼びます。 このパッケージには、複数のサブアプリケーションから共通に使われるユースケースを実装するServiceクラスを格納します。

examples.aaa.utilをユーティリティパッケージと呼びます。 このパッケージには、staticなメソッドで構成されるユーティリティクラスを格納します。

examples.aaa.helperをヘルパパッケージと呼びます。 このパッケージにも、ユーティリティクラスを格納します。 ユーティリティパッケージとの違いは、DIやパラメータが必要なクラスを格納することです。

examples.aaa.interceptorをインタセプタパッケージを呼びます。 このパッケージには、AOPのモジュールを格納します。

examples.aaa.converterをコンバータパッケージを呼びます。 このパッケージには、データ変換を行なうモジュールを格納します。

examples.aaa.validatorをバリデータパッケージを呼びます。 このパッケージには、値の検証用のモジュールを格納します。

entity、serviceなどのルートパッケージ直下のパッケージを共通パッケージと呼びます。 共通パッケージには、複数のサブアプリケーションから共通に使われるクラスを格納します。 DTOクラスなどで特定のサブアプリケーションでしか使わない場合は、 Dtoパッケージではなく、サブアプリケーションパッケージに格納してください。

コンポーネント名

クラス名の先頭を小文字にしたものが基本的なコンポーネント名になります。
ただし、XXaaaPageのように2つ以上大文字が続いている場合は、 大文字のままです。これはJavaBeansの仕様です。

サブアプリケーションパッケージに格納されるコンポーネントは、 "サブアプリケーション名_"がプレフィックスにつきます。
アンダースコア(_)をプレフィックスの区切りに使っているので、 サブアプリケーション名やコンポーネント名にアンダースコア(_)は使えません。 これは、Seasar2の制限です。

サブアプリケーションパッケージ共通パッケージは、 さらにサブパッケージで区切ることもできます。 サブパッケージを使った場合は、"サブパッケージ名_"をプレフィックスに追加します。 サブパッケージはいくらでもネストすることができます
例えば、examples.aaa.web.bbb.ccc.DddPageのコンポーネント名は、 bbb_ccc_dddPageになります。
examples.aaa.service.bbb.CccServiceのコンポーネント名は、 bbb_cccServiceになります。

クリエータ

クリエータとはSMART deployの命名規約に従いコンポーネント定義を作成するコンポーネントの総称です。 クリエータは規約に従いインスタンス属性やコンポーネント名などコンポーネントに必要な情報を決定しコンポーネント定義を作成します。

Seasar2にはあらかじめ以下に示すクリエータが用意されています。 これらのクリエータは、それぞれ規約に合致するクラスのコンポーネント定義を作成します。 クリエータはcreator.diconに定義する必要があります。

クリエータ一覧
クリエータのクラスの単純名 コンポーネント化対象とするクラスの名前のサフィックス 作成するコンポーネントのインスタンス属性
ActionCreatorActionrequest
ConverterCreatorConverterprototype
DaoCreatorDaoprototype
DtoCreatorDtorequest
DxoCreatorDxosingleton
HelperCreatorHelperprototype
InterceptorCreatorInterceptorprototype
LogicCreatorLogicprototype
PageCreatorPagerequest
ServiceCreatorServiceprototype
ValidatorCreatorValidatorprototype

クリエータが作成するコンポーネント定義に設定されるインスタンス属性は、あらかじめクリエータごとにデフォルトの値が指定されています。

クリエータは、コンポーネント名から対象となるクラスを一定のルールにしたがって見つけます。

例えば、hogeDaoという名前のコンポーネントがある場合、 最初にコンポーネント名のサフィックスを求めます。 サフィックスとはコンポーネント名の後ろから最初に大文字の部分までをさします。 hogeDaoの場合、サフィックスはDaoになります。 次にサフィックスの先頭を小文字(dao)にして、 パッケージ名を組み立てます。 daoの場合は、ルートパッケージ.daoになります。 コンポーネント名の先頭を大文字にしてパッケージ名とつなぎ合わせてクラス名を求めます。 今回の場合は、ルートパッケージ.dao.HogeDaoになります。

カスタマイザ

カスタマイザとはコンポーネント定義をカスタマイズするコンポーネントの総称です。 クリエータに呼び出されたカスタマイザは自身に定義されたカスタマイズ処理をコンポーネント定義に対し実行します。 カスタマイザの機能の代表例はアスペクト定義(インターセプタ)の追加です。 例えば、サービスのカスタマイザにトレース出力のアスペクト定義を設定すると、サービスのクリエータによって作成されたすべてのコンポーネントにトレース出力のインターセプタが自動で適用されます。

S2Containerにはdefault-customizer.diconに定義されたデフォルトカスタマイザとstd-customizer.diconに定義された標準カスタマイザがあらかじめ用意されています。 アプリケーションで定義するカスタマイザはcustomizer.diconに記述してください。

デフォルトカスタマイザ

デフォルトカスタマイザはクリエータに関連付けられるカスタマイザです。 クリエータがコンポーネント定義を作成したあとにこれらのカスタマイザが呼び出されます。 デフォルトカスタマイザには以下のコンポーネントがあります。

コンポーネント名説明
actionCustomizerAction用のカスタマイザ
converterCustomizerConverter用のカスタマイザ
daoCustomizerDao用のカスタマイザ
dtoCustomizerDto用のカスタマイザ
dxoCustomizerDxoカスタマイザ
helperCustomizerHelper用のカスタマイザ
logicCustomizerLogic用のカスタマイザ
interceptorCustomizerInterceptor用のカスタマイザ
pageCustomizerPage用のカスタマイザ
serviceCustomizerService用のカスタマイザ
validatorCustomizerValidator用のカスタマイザ

これらのコンポーネントの実体はorg.seasar.framework.container.customizer.CustomizerChainです。

CustomizerChainには任意の数のカスタマイザを設定(チェイン)可能ですが、デフォルトカスタマイザのコンポーネントには何も設定されていません。 したがって、デフォルトカスタマイザはクリエータから呼び出されても何の処理も実行しません。 デフォルトカスタマイザがそのように定義されている理由は、カスタマイザをどのように設定するかはアプリケーション開発者に委ねられているからです。

カスタマイザの設定はデフォルトカスタマイザの設定を上書きする形でcustomizer.diconに記述します。 その設定は通常CustomizerChainに任意の標準カスタマイザを追加することで行います。

標準カスタマイザ

標準カスタマイザは標準的に利用されることを想定したコンポーネントです。 標準カスタマイザには以下のコンポーネントがあります。

コンポーネント名関連付けられたインターセプト名説明
traceCustomizeraop.traceInterceptorトレース出力のためのカスタマイザ
simpleTraceCustomizeraop.simpleTraceInterceptor引数や戻り値を出力しない単純なトレースのためのカスタマイザ
classLoaderAwareTraceCustomizeraop.classLoaderAwareTraceInterceptorクラスローダーの情報を含めたトレース出力のためのカスタマイザ
syncCustomizeraop.syncInterceptor同期処理のためのカスタマイザ
traceThrowsCustomizeraop.traceThrowsInterceptor例外のトレース出力のためのカスタマイザ
toStringCustomizeraop.toStringInterceptorオブジェクトのフィールドの値を文字列化するためのカスタマイザ
removeSessionCustomizeraop.removeSessionInterceptorメソッドの実行後にHTTPセッションから属性を削除するためのカスタマイザ
commandTraceCustomizeraop.traceInterceptordo.*, initialize, prerenderをポイントカットとしてトレース出力するカスタマイザ
requiredTxCustomizerj2ee.requiredTxRequiredのトランザクション属性を有効にするためのカスタマイザ
requiresNewTxCustomizerj2ee.requiresNewTxRequiresNewのトランザクション属性を有効にするためのカスタマイザ
mandatoryTxCustomizerj2ee.mandatoryTxMandatoryのトランザクション属性を有効にするためのカスタマイザ
notSupportedTxCustomizerj2ee.notSupportedTxNotSupportedのトランザクション属性を有効にするためのカスタマイザ
neverTxCustomizerj2ee.neverTxNeverのトランザクション属性を有効にするためのカスタマイザ
txAttributeCustomizerj2ee.requiredTxEJB3の@TransactionAttributeアノテーションで指定されたトランザクション属性を有効にするためのカスタマイザ (S2-Tigerが必要です)
j2ee.requiresNewTx
j2ee.mandatoryTx
j2ee.notSupportedTx
j2ee.neverTx
s2DxoCustomizerdxo.interceptorDxoを利用するためのカスタマイザ
s2DaoCustomizerdao.interceptorS2Daoを利用するためのカスタマイザ
kuinaDaoCustomizerkuinaDao.interceptorKuina-Daoを利用するためのカスタマイザ
dependencyLookupCustomizeraop.dependencyLookupInterceptor@DependencyLookupアノテーションの指定されたgetterメソッドがS2コンテナからルックアップしたコンポーネントを返すようにするためのカスタマイザ

txAttributeCustomizerdependencyLookupCustomizerを除いて、これらのコンポーネントの実体はorg.seasar.framework.container.customizer.AspectCustomizerです。 AspectCustomizerはコンポーネント定義にアスペクト定義を登録するカスタマイザです。

標準カスタマイザはインターセプタと関連づけられています。 実行時、標準カスタマイザは関連付けられたインターセプタからアスペクト定義を作成しコンポーネント定義に登録します。

カスタマイザの設定

アプリケーションで使用するカスタマイザの設定はcustomizer.diconに定義します。 例えば、サービスのコンポーネントに標準のカスタマイザであるtraceCustomizertraceThrowsCustomizerを適用する場合、次のように記述します。

<components>
  <include path="default-customizer.dicon"/>
  ...
  <component name="serviceCustomizer" 
    class="org.seasar.framework.container.customizer.CustomizerChain">
    <initMethod name="addCustomizer">
      <arg>traceCustomizer</arg>
    </initMethod>
    <initMethod name="addCustomizer">
      <arg>traceThrowsCustomizer</arg>
    </initMethod>
  </component>
  ...
</components>

定義するコンポーネントの名前はデフォルトカスタマイザ一覧に示したコンポーネント名と同じにしてください。 こうすることでcusomizer.diconの定義がdefault-customizar.diconの定義より優先されます。

カスタマイザ設定例

カスタマイザの設定例を紹介します。 ここで紹介する設定はすべてcusomizer.diconに記述されることを想定しています。

特定のパターンに従ってカスタマイザを適用する
  • パターンに合致するコンポーネントにのみカスタマイザを適用する場合
  • 例えば、サービスのコンポーネントのうち「Hoge」で始まるクラス名をもつコンポーネントにtraceCustomizerを適用する場合、 次のようにaddClassPatternメソッドを使用し条件を引数で渡します。 第1引数にはパッケージ名、第2引数にはクラス名の正規表現を指定します。 第1引数のパッケージ名は、前方一致条件として使用されます。 たとえば、「example.hoge」を指定した場合「example.hoge」も「example.hoge.foo」もパターンに合致するとみなされます。

      <component name="serviceCustomizer" 
        class="org.seasar.framework.container.customizer.CustomizerChain">
        <initMethod name="addClassPattern">
          <arg>"example.service"</arg>
          <arg>"Hoge.*"</arg>
        </initMethod>
        <initMethod name="addCustomizer">
          <arg>traceCustomizer</arg>
        </initMethod>    
      </component>
    
  • パターンに合致するコンポーネントにはカスタマイザを適用しない場合
  • 逆に、サービスのコンポーネントのうち「Hoge」で始まるクラス名をもつコンポーネントにはtraceCustomizerを適用しない場合、 次のようにaddIgnoreClassPatternメソッドを使用し条件を引数で渡します。 第1引数にはパッケージ名、第2引数にはクラス名の正規表現を指定します。 第1引数のパッケージ名は、前方一致条件として使用されます。

      <component name="serviceCustomizer" 
        class="org.seasar.framework.container.customizer.CustomizerChain">
        <initMethod name="addIgnoreClassPattern">
          <arg>"example.service"</arg>
          <arg>"Hoge.*"</arg>
        </initMethod>
        <initMethod name="addCustomizer">
          <arg>traceCustomizer</arg>
        </initMethod>    
      </component>
    
トランザクション制御を設定する
  • サービスのコンポーネントに適用する場合
  • serviceCustomizerにトランザクション制御を有効にするためのカスタマイザを設定します。 ここではRequiredのトランザクション属性に対応するrequiredTxCustomizerを使用します。

      <component name="serviceCustomizer" 
        class="org.seasar.framework.container.customizer.CustomizerChain">
        <initMethod name="addCustomizer">
          <arg>requiredTxCustomizer</arg>
        </initMethod>
      </component>
    

    この設定ではサービスのコンポーネントが実装するインタフェースのすべてのメソッドがトランザクション制御の対象になります。

  • @TransactionAttributeアノテーションを使ったサービスのコンポーネントに適用する場合
  • EJB3の@TransactionAttributeアノテーションで指定されたトランザクション属性に対応するトランザクション制御を有効にするためのカスタマイザを設定します。 ここではtxAttributeCustomizerを使用します。

      <component name="serviceCustomizer" 
        class="org.seasar.framework.container.customizer.CustomizerChain">
        <initMethod name="addCustomizer">
          <arg>txAttributeCustomizer</arg>
        </initMethod>
      </component>
    

    この設定では、サービスのコンポーネントが持つすべてのpublicメソッドがトランザクション制御の対象になります。 ただし、Objectクラスで定義されているメソッドは除きます。 EJB3と異なり、インタフェースに定義されたメソッドでなくてもトランザクション制御の対象となります (インタフェースを実装している必要はありません)。 EJB3ではないので、@Stateless等のアノテーションを指定する必要もありません。

    メソッドに@TransactionAttributeアノテーションが指定されていれば、そのvalue属性の値で指定されたトランザクション属性が適用されます。 メソッドに@TransactionAttributeアノテーションが指定されていなければ、クラスに指定された@TransactionAttributeアノテーションが使われます。 クラスにも@TransactionAttributeアノテーションが指定されていなければ、デフォルトとしてrequiredTxが使われます。

  • ページのコンポーネントに適用する場合
  • pageCustomizerにトランザクション制御を有効にするためのカスタマイザを設定します。 addAspectCustomizer()メソッドの第1引数にインターセプタのコンポーネント名、 第2引数にトランザクション制御の対象となる特定のメソッドをポイントカットとして指定します。

      <component name="pageCustomizer" 
        class="org.seasar.framework.container.customizer.CustomizerChain">
        <initMethod name="addAspectCustomizer">
          <arg>"j2ee.requiredTx"</arg>
          <arg>"do.*, initialize, prerender"</arg>
        </initMethod>
      </component>
    
  • EJB 3.0のステートレスセッションBeanを利用する場合
  • EJB 3.0のステートレスセッションBeanはデフォルトでトランザクション制御を行う機能が備わっているのでcusomizer.diconに対する設定は不要です。 S2ContainerのEJB 3.0サポートについてはEJB3.0 Simplified APIを参照してください。

インスタンス属性がsingleton以外のインターセプタを利用する

インターセプタを適用するAspectCustomizeruseLookupAdapterプロパティにtrueを設定をします。

  <component name="pageCustomizer" 
    class="org.seasar.framework.container.customizer.CustomizerChain">
    <initMethod name="addCustomizer">
      <arg>
        <component class="org.seasar.framework.container.customizer.AspectCustomizer">
          <property name="useLookupAdapter">true</property>
          <property name="interceptorName">"app_aop.authenticateInterceptor"</property>
          <property name="pointcut">"do.*"</property>
        </component>
      </arg>
    </initMethod>
  </component>

※ この例で使用した「app_aop.authenticateInterceptor」という名前のインターセプタは標準で用意されているものではありません。

複数のデータソースを利用する

データソースごとにカスタマイザを作成し、それらのカスタマイザをdaoCustomizerに設定してください。 S2Daoを使用した例についてはSMART deployで複数データソースに対応するには?を参照してください。

タグリファレンス

DOCTYPE

DOCTYPEは、XML宣言の次に指定します。下記のように指定してください。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.3//EN"
"http://www.seasar.org/dtd/components23.dtd">
<components>
    <component name="hello" class="examples.dicon.HelloConstructorInjection">
        <arg>"Hello World!"</arg>
    </component>
</components>

componentsタグ(必須)

ルートのタグになります。

namespace属性(任意)

名前空間を指定することができます。Javaの識別子として使えるものにします

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.3//EN"
"http://www.seasar.org/dtd/components23.dtd">
<components namespace="hoge">
    ...
</components>

includeタグ(任意)

分割されたS2Containerの定義を取り込む場合に使います。

path属性(必須)

定義ファイルのパスを指定することができます。CLASSPATHで指定されているディレクトリをルートとする定義ファイルの絶対パスです。例えば、WEB-INF/classes/aaa.dicon の場合は aaa.dicon に、WEB-INF/classes/aaa/bbb/ccc.dicon の場合は aaa/bbb/ccc.dicon になりますセパレータは、WindowsでもUnixでも/です。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.3//EN"
"http://www.seasar.org/dtd/components23.dtd">
<components>
    <include path="aaa/bbb/ccc.dicon" />
</components>

componentタグ(任意)

コンポーネントを定義します。

class属性(任意)

クラスの完全限定名を指定します。ボディで、OGNL式を使ってコンポーネントを指定した場合は、class属性を省略することができます。OGNL式を使った場合にclass属性を指定すると、型チェックを行います。

name属性(任意)

名前を指定することもできます。Javaの識別子として使えるものにします。詳しくは、コンポーネントの取得を参照してください。

instance属性(任意)

S2Containerがどのようにコンポーネントのインスタンスを管理するのかを指定することができます。singleton(デフォルト)、prototype、outer、request、sessionを指定することができます。詳しくは、インスタンス管理を参照してください。

autoBinding属性(任意)

S2Containerがコンポーネントの依存関係をどのように解決するのかを指定できます。auto(デフォルト)、constructor、property、noneを指定することができます。詳しくは、自動バインディングを参照してください。

argタグ(任意)

componentタグの子タグとして使った場合は、コンストラクタの引数になります。記述した順番でコンストラクタに渡されます。 initMethodタグdestroyMethodタグの子タグとして使った場合は、メソッドの引数になります。記述した順番でメソッドに渡されます。 引数として渡される実際の値は、ボディで、OGNL式を使うか、子タグで、componentタグを使います。

propertyタグ(任意)

componentタグの子タグとして使います。プロパティとして設定される実際の値は、ボディで、OGNL式を使うか、子タグで、componentタグを使います。

name属性(必須)

プロパティ名を指定します。

bindingType属性(任意)

プロパティごとに自動バインディングを細かく制御できます。must、should(デフォルト)、may、noneを指定することができます。詳しくは、自動バインディングを参照してください。

metaタグ(任意)

componentsタグcomponentタグargタグpropertyタグの子タグとして使います。メタデータの値は、ボディで、OGNL式を使うか、子タグで、componentタグを使います。

name属性(任意)

メタ名を指定します。

initMethodタグ(任意)

componentタグの子タグとして使います。引数は、子タグで、argタグを使います。name属性を書かずに、OGNL式を使って、コンポーネントのメソッドを呼び出すこともできます。initMethodタグが定義されているコンポーネント自身を表す#self、System.outを表す#out、System.errを表す#errがinitMethodタグ内だけで有効なオブジェクトとして使えます。

name属性(任意)

メソッド名を指定します。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.3//EN"
"http://www.seasar.org/dtd/components23.dtd">
<components>
    <component class="java.util.HashMap">
        <initMethod name="put">
            <arg>"aaa"</arg>
            <arg>111</arg>
        </initMethod>
        <initMethod>#self.put("aaa", 111)</initMethod>
        <initMethod>#out.println("Hello")</initMethod>
    </component>
</components>

destroyMethodタグ(任意)

initMethodタグと同様です。

aspectタグ(任意)

Advice(以下Interceptor)をコンポーネントに組み込みます。 Interceptorの指定は、ボディで他のコンポーネントを参照するか、子タグでcomponentタグを使います。 1つのコンポーネントに複数のアスペクトを組み込んだ場合はアスペクトの登録順に組み込まれ実行されます。

注意点

  • finalなクラスにはアスペクトを適用できません。
  • finalまたはstaticまたは非publicなメソッドにはアスペクトを適用できません。
  • aspectタグで指定されたコンポーネントは、 コンテナの初期化時にコンテナから取得されます。 そのため、aspectタグで指定されたコンポーネントのinstance属性がprototypeだったとしても、 Interceptor のメソッドが呼び出される度に新しいインスタンスが作成されるわけではありません。 singleton以外のスコープにしたい場合は、 InterceptorLifecycleAdapterを使ってください。

pointcut属性(任意)

カンマ区切りで対象となるメソッド名を指定することができます。 pointcutを指定しない場合は、コンポーネントが実装しているインターフェースのすべてのメソッドが対象になります。 インターフェースを実装していない場合、Object.classに属していないpublicでfinalではないメソッドが対象になります。 メソッド名には正規表現(JDK1.4のregex)も使えます。

設定例

pointcut属性を指定してjava.util.DateのgetTime()メソッドとhashCode()メソッドを対象とする場合以下のようになります。

<component class="java.util.Date">
    <aspect pointcut="getTime,hashCode">
        <component class="org.seasar.framework.aop.interceptors.TraceInterceptor"/>
    </aspect>
</component>

pointcutの対象から外したいメソッドがある場合は以下のようにします。
以下は「do」で始まり,その後に「ABC」が続くメソッドをpointcutの対象から外す例です。 doABC()メソッド、doABCDEF()メソッドがpointcutの対象から外れます。

<component class="java.util.Date">
    <aspect pointcut="do(?!ABC).*">
        <component class="org.seasar.framework.aop.interceptors.TraceInterceptor"/>
    </aspect>
</component>

また、以下のような設定も可能です。
「do」で始まり,その後に「ABC」または「BCD」が続くメソッドをpointcutの対象から外す例です。

<component class="java.util.Date">
    <aspect pointcut="do(?!ABC|BCD).*">
        <component class="org.seasar.framework.aop.interceptors.TraceInterceptor"/>
    </aspect>
</component>

interTypeタグ(任意)

InterTypeをコンポーネントに組み込みます。InterTypeの指定は、 ボディで他のコンポーネントを参照するか、子タグでcomponentタグを使います。 1つのコンポーネントに複数のInterTypeを組み込んだ場合はInterTypeの登録順に組み込まれます。

設定例
<component class="java.util.Date">
    <interType>
        <component class="org.seasar.framework.aop.intertype.PropertyInterType"/>
    </interType>
</component>

descriptionタグ(任意)

componentsタグcomponentタグargタグpropertyタグの子タグとしてdescriptionタグを使うことができます。自由に説明を記述できます。

OGNL式

S2Containerでは、式言語としてOGNLを利用しています。XMLの中で、文字列で記述した内容(式)をJavaのオブジェクトに変換するためのものだと思って間違いないと思います。

  • 文字列は、"hoge"のように"で囲みます。
  • charは、'a'のように'で囲みます。
  • 数値は、123のようにそのまま記述します。
  • 論理値は、true,falseのようにそのまま記述します。
  • new java.util.Date(0)のようにクラスの完全限定名でコンストラクタを呼び出すことができます。
  • @java.lang.Math@max(1, 2)のようにstaticなメソッドを呼び出した結果を参照することができます。
  • @java.lang.String@classのようにクラスを参照できます。
  • hoge.toString()のようにコンポーネントのメソッドを呼び出した結果を参照することができます。この例は、どこかでhogeという名前のコンポーネントが定義されているという前提です。
詳しくは、OGNLガイドを参照してください。

アノテーションリファレンス

S2Containerでは、アノテーションの実装方法として、 Tigerアノテーション、 定数アノテーションの2種類を用意しています。 一般的にアノテーションといえば、Java 5から導入された Tigerアノテーションですが、 JDK1.4のユーザでも利用できるように、public static finalな定数を利用する定数アノテーションも用意しています。

Componentアノテーション

componentタグのかわりに使えるのが、Componentアノテーションです。

Tigerアノテーションは以下のようになります。

@Component(name="xxx", instance=InstanceType.PROTOTYPE,
        autoBinding=AutoBindingType.PROPERTY)
public class Xxx {
    ...
}

定数アノテーションは以下のようになります。

public static final String COMPONENT =
  "name = xxx, instance = prototype, autoBinding = property";

Bindingアノテーション

propertyタグのかわりに使えるのが、Bindingアノテーションです。

Tigerアノテーションは以下のようになります。

@Binding("aaa2")
public void setAaa(String aaa) {
    ...
}

@Binding(bindingType=BindingType.NONE)
public void setBbb(String bbb) {
    ...
}

@Binding
public void setCcc(String ccc) {
    ...
}

@Binding
private Foo foo;

定数アノテーションはプロパティ名_BINDINGで指定します。

public static final String aaa_BINDING = "aaa2";

public static final String bbb_BINDING = "bindingType=none";

public static final String ccc_BINDING = null;

public void setAaa(Aaa aaa) {
    ...
}

public void setBbb(Bbb bbb) {
    ...
}

public void setCcc(Ccc ccc) {
    ...
}

Aspectアノテーション

aspectタグのかわりに使えるのが、Aspectアノテーションです。 aspectタグと異なり、複数定義することはできないので、 複数のインターセプタを適用したい場合は、 InterceptorChainを使ってください。 ポイントカットを指定したいときは、pointcut属性を指定します。 クラスに対するアノテーションの場合、pointcutを指定しないときは、 そのクラスが実装しているすべてのインターフェースのメソッドが対象になります。 インターフェースを実装していない場合、Object.classに属していないpublicでfinalではないメソッドが対象になります。

Tigerアノテーションは以下のようになります。

@Aspect("aop.traceInterceptor")
public class Xxx {
    ...
}

public class Xxx {
    ...
    @Aspect("aop.traceInterceptor")
    public void hoge() {
        ...
    }
}

定数アノテーションは以下のようになります。pointcutを複数指定したい場合は、pointcut= get.*\nexecute.*のように\nで区切ってください。 \n以外の区切り文字を使ってはいけません。

public static final String ASPECT =
    "value=aop.traceInterceptor, pointcut=getAaa";

InterTypeアノテーション

interTypeタグのかわりに使えるのが、InterTypeアノテーションです。

Tigerアノテーションは以下のようになります。

@InterType({"aop.propertyInterType", "aop.someInterType"})
public class Xxx {
    ...
}

定数アノテーションは以下のようになります。

public static final String INTER_TYPE =
    "aop.propertyInterType, aop.someInterType";

InitMethodアノテーション

initMethodタグのかわりに使えるのが、InitMethodアノテーションです。 initMethodタグと異なり、OGNLの式を書いたり、引数を設定することはできません。

Tigerアノテーションは以下のようになります。

public class Xxx {
    ...
    @InitMethod
    public void init() {
        ...
    }
}

定数アノテーションは以下のようになります。初期化メソッドを複数指定したい場合は、カンマ(,)で区切ってください。

public static final String INIT_METHOD = "init";

DestroyMethodアノテーション

destroyMethodタグのかわりに使えるのが、DestroyMethodアノテーションです。 destroyMethodタグと異なり、OGNLの式を書いたり、引数を設定することはできません。

Tigerアノテーションは以下のようになります。

public class Xxx {
    ...
    @DestroyMethod
    public void destroy() {
        ...
    }
}

定数アノテーションは以下のようになります。destroyメソッドを複数指定したい場合は、カンマ(,)で区切ってください。

public static final String DESTROY_METHOD = "destroy";