使用方法
S2Wicketを使用して,WicketとSeasar2を連携するための手順について紹介します。
0. 前提条件
WicketおよびSeasar2 Containerが使用可能な状態にWebアプリケーションがセットアップされていることとします。 また,インジェクションしたいコンポーネントオブジェクトが,Seasar2のDIコンテナに登録されていることとします。
maven2を使用してS2Wicketを利用する場合は,maven2が利用可能な状態になっていることが必要です。
1. S2Wicketをセットアップする
S2Wicketをセットアップする形態として,以下の3つの方法があります。
- seasar.orgのmaven2リポジトリに登録されているjarファイルを利用する。
- 配布パッケージに含まれるjarファイルを利用する。
- 配布パッケージのソースコードをビルドし,jarファイルを作成する。
以下にそれぞれの手順を示します。
seasar.orgのmaven2リポジトリに登録されているjarファイルを利用する。
S2Wicketは,seasar.orgが提供するmaven2リポジトリに各種jarを登録しています。 あなたのWebアプリケーションがmaven2でプロジェクト管理されている場合は, pom.xmlファイルにseasar.orgのmaven2リポジトリの場所とS2Wicketの依存関係を記述するだけで, すぐにS2Wicketを使用可能な状態にすることができます。
まず,pom.xmlファイルにseasar.orgが提供するmaven2リポジトリの場所を以下のように記述します。
<project ...> ... <repositories> ... <repository> <id>maven.seasar.org</id> <name>The Seasar Foundation Maven2 Repository</name> <url>http://maven.seasar.org/maven2</url> </repository> <repository> <id>local</id> <name>The Seasar Foundation Maven2 local Repository</name> <url>file:repo</url> </repository> </repositories> ... </project>
さらに,S2Wicketへの依存関係を以下のように記述します。
<project ...> ... <dependencies> ... <dependency> <groupId>org.seasar.wicket</groupId> <artifactId>s2wicket</artifactId> <version>1.2.1</version> </dependency> </dependencies> ... </project>
pom.xmlファイルにS2Wicketへの依存関係が記述されたことにより,maven2の各種ゴールでS2Wicketが 利用されるようになります。例えば,「mvn eclipse:eclipse」を実行することにより,あなたのWebアプリケーションの Eclipseプロジェクトのクラスパスに,s2wicket-1.2.1.jarファイルやその他必要なライブラリが追加されます。 また,「mvn package」を行うことにより,warファイルの/WEB-INF/libディレクトリに,s2wicket-1.2.1.jar ファイルや必要なライブラリが含まれるようになります。
配布パッケージに含まれるjarファイルを利用する。
S2Wicketは,ダウンロード可能な配布パッケージに,ビルド済みのjarファイルとS2Wicketに必要な依存jarファイル を含めて提供しています。配布パッケージに含まれるjarを使用することで,maven2がない場合でもS2Wicketを 使用することができます。
1. S2Wicket配布パッケージを入手する
ダウンロードページから,S2Wicketrの最新バージョンのアーカイブをダウンロードします。その後, 適当なディレクトリで展開します。展開後のトップディレクトリは,以下のようになっているはずです。
S2Wicket +- src +- lib +- cglib-nodep-2.1_3.jar +- commons-lang-2.2.jar +- LICENSE.txt +- pom.xml +- build.xml +- s2wicket-1.2.1.jar
このような構成になっていない場合は,ダウンロードしたアーカイブが破損している可能性があります。 もう一度ダウンロードし直してください。
2. ライブラリをWebアプリケーションに組み込む
S2Wicketを使用するためには,3つのjarファイルをあなたのWebアプリケーションに配置する必要があります。 配置が必要なjarファイルは,以下のファイルです。
s2wicket +- lib +- cglib-nodep-2.1_3.jar +- commons-lang-2.2.jar +- s2wicket-1.2.1.jar
S2Wicketのアーカイブに含まれる上記3つのjarファイルを,あなたのWebアプリケーションにコピーして配置します。 配置先は,あなたのWebアプリケーションに存在する「/WEB-INF/lib」ディレクトリです。
[あなたのWebアプリケーション] +- WEB-INF +- lib +- ... +- cglib-nodep-2.1_3.jar +- commons-lang-2.2.jar +- s2wicket-1.2.1.jar
S2Wicketは,wicketとseasar2が前提となりますので,/WEB-INF/libディレクトリ内には, wicket-1.2.4.jarファイルやs2-framework-2.4.8.jarファイルが存在するはずです。
これにより,あなたのWebアプリケーションにS2Wicketが組み込まれたことになります。 あなたのWebアプリケーションの開発時には,上記3つのjarファイルについてクラスパスを通した状態で コンパイルなどを行ってください。
配布パッケージのソースコードをビルドし,jarファイルを作成する。
S2Wicketの配布パッケージには,ソースコードが含まれています。さらにmaven2のpom.xmlファイルも提供しています。 あなたはmaven2を使用して,配布パッケージに含まれるソースコードからS2Wicketのjarファイルをビルドすることができます。
S2Wicketのアーカイブに含まれるソースファイルを使用してs2wicket-1.2.1.jarファイルをビルドし, maven2のローカルリポジトリにインストールすることができます。インストールされれば,あなたのWebアプリケーション から,maven2を使用して必要なライブラリを自動的に組み込むことが可能となります。
1. S2Wicket配布パッケージを入手する
ダウンロードページから,S2Wicketrの最新バージョンのアーカイブをダウンロードします。その後, 適当なディレクトリで展開します。展開後のトップディレクトリは,以下のようになっているはずです。
S2Wicket +- src +- lib +- cglib-nodep-2.1_3.jar +- commons-lang-2.2.jar +- LICENSE.txt +- pom.xml +- build.xml +- s2wicket-1.2.1.jar
このような構成になっていない場合は,ダウンロードしたアーカイブが破損している可能性があります。 もう一度ダウンロードし直してください。
2. s2wicket-1.2.1.jarをビルド&インストールする
s2wicket-1.2.1.jarをビルドし,maven2のローカルリポジトリにインストールします。 コマンドプロンプトやシェルを開き,以下のコマンドを実行します。
% cd [S2Wicketアーカイブの展開先ディレクトリ] % mvn install
「BUILD SUCCESSFUL」と表示されれば,インストールは成功です。
3. pom.xmlファイルにS2Wicketの依存を記述する
あなたのWebアプリケーションのpom.xmlファイルに,S2Wicketへの依存関係を追記します。 pom.xmlファイル中のdependenciesタグ内に記述します。
<project ...> ... <dependencies> ... <dependency> <groupId>org.seasar.wicket</groupId> <artifactId>s2wicket</artifactId> <version>1.2.1</version> </dependency> </dependencies> ... </project>
4. ビルドやEclipseプロジェクトの生成を行う
pom.xmlファイルにS2Wicketへの依存関係が記述されたことにより,maven2の各種ゴールでS2Wicketが 利用されるようになります。例えば,「mvn eclipse:eclipse」を実行することにより,あなたのWebアプリケーションの Eclipseプロジェクトのクラスパスに,s2wicket-1.2.1.jarファイルやその他必要なライブラリが追加されます。 また,「mvn package」を行うことにより,warファイルの/WEB-INF/libディレクトリに,s2wicket-1.2.1.jar ファイルや必要なライブラリが含まれるようになります。
3. フィールドフィルタ
インジェクション対象となるフィールドの判断基準として,S2Wicketは標準で@SeasarComponentアノテーションが 利用可能となっています。しかし,いちいち@SeasarComponentアノテーションを記述していくのは手間がかかりますし, 多くの3層アプリケーションでは,下層のオブジェクトには特定の命名規則(〜Serviceなど)が存在します。つまり, 明示的に@SeasarComponentアノテーションなどでマーキングせずとも,インジェクション対象とするフィールドかどうかを 判断することは,ほとんどのWebアプリケーションでは可能なのです。
S2Wicketでは,フィールドフィルタという判断機構があります。フィールドフィルタを適切にS2Wicketに登録することにより, インジェクション対象のフィールドかどうかを柔軟に判断することができるようになります。 いくつかのフィールドフィルタが標準で提供されていますが,独自の規約をフィールドフィルタの実装クラスとして作り込むことで, ドメイン依存のインジェクション規約をあなたのWebアプリケーションに適用することができるようになります。
@SeasarComponentアノテーションフィールドフィルタを使用する
S2Wicketでは,@SeasarComponentアノテーションをインジェクション対象のマーカーとするフィールドフィルタが 標準で提供されています。このフィールドフィルタの実装クラスは,AnnotationFieldFilterクラスです。 SeasarComponentInjectionListenerオブジェクトの生成時に,明示的にフィールドフィルタを 指定しなかった場合は,@SeasarComponentアノテーションをインジェクション対象とするAnnotationFieldFilterクラス が暗黙的に適用されます。
Seasarコンテナからコンポーネントオブジェクトをルックアップする際に, 名前でルックアップするか型でルックアップするかで,@SeasarComponentアノテーションの使い方が異なってきます。
型でルックアップする場合
Seasarコンテナから型を指定してコンポーネントオブジェクトをルックアップする場合は,以下のように記述します。
import org.seasar.wicket.injection.fieldfilters.SeasarComponent; public class InputPage extends WebPage { @SeasarComponent private OrderService orderService; }
@SeasarComponentアノテーションを属性なしで記述した場合,@SeasarComponentアノテーションを付与した フィールドの型に一致するコンポーネントオブジェクトが,Seasarコンテナからルックアップされます。 つまり上記の例では,S2Container#getComponent()メソッドの引数にOrderService.classオブジェクトを渡して ルックアップが行われます。フィールドの型は,インタフェースとクラスのどちらでも構いません。
名前でルックアップする場合
Seasarコンテナから名前を指定してコンポーネントオブジェクトをルックアップする場合は,以下のように記述します。
import org.seasar.wicket.injection.fieldfilters.SeasarComponent; public class InputPage extends WebPage { @SeasarComponent(name="orderService") private OrderService orderService; }
@SeasarComponentアノテーションにname属性を記述した場合,name属性の値を名前とするコンポーネントオブジェクトが, Seasarコンテナからルックアップされます。つまり上記の例では,S2Container#getComponent()メソッドの引数に"orderService"という 文字列を渡してルックアップが行われます。この場合も,フィールドの型はインタフェースとクラスのどちらでも構いません。
フィールド名パターンフィールドフィルタ
パッケージ名,クラス名,フィールド名について,それぞれ正規表現でパターンを与えることにより, そのパターンに一致するフィールドをインジェクション対象とするフィールドフィルタを, S2Wicketは標準で提供しています。このフィールドフィルタの実装クラスは,FieldNamePatternFieldFilterクラスです。
フィールド名パターンフィールドフィルタは,@SeasarComponentアノテーションフィルタと違い, 暗黙的に適用されることはありません。フィールド名パターンフィールドフィルタを使用したい場合は, SeasarComponentInjectionListenerクラスのコンストラクタの引数で明示的に指定するか, Seasarコンテナに登録するかのどちらかの方法を使用して,使用可能な状態にする必要があります。
フィールド名パターンフィールドフィルタは,パッケージ名,クラス名,そしてフィールド名について, それぞれ正規表現によるパターンマッチングを行います。パターンマッチングの結果,パターンに一致した 場合に,そのフィールドがインジェクション対象として判断されます。この際,Seasarコンテナから ルックアップする際のコンポーネント名は,フィールド名がそのまま使用されます。
FieldNamePatternFieldFilterクラスは,パッケージ名,クラス名,フィールド名の正規表現文字列を 持つためのプロパティが定義されています。
パッケージ名のパターン | packageNamePatternRegex |
クラス名のパターン | classNamePatternRegex |
フィールド名のパターン | fieldNamePatternRegex |
例えば「〜.exampleパッケージの〜Pageクラスの〜Serviceフィールドをインジェクション対象とする」 という規約を適用したい場合は,以下のような正規表現を各プロパティに与えます。
packageNamePatternRegex | .* .example |
classNamePatternRegex | .*Page |
fieldNamePatternRegex | .*Service |
もし複数のパターンを同時に適用したい場合は,FieldNamePatternFieldFilterオブジェクトを複数個登録してください。
フィールド名パターンフィールドフィルタを適用する場合は,FieldNamePatternFieldFilterオブジェクトに対して, 上記のプロパティ値として正規表現の文字列を適切にセットする必要があります。
明示的にインスタンスを生成する場合
FieldNamePatternFieldFilterクラスのインスタンスを明示的に指定する場合は, 以下のようにコーディングします。
FieldNamePatternFieldFilter filter = new FieldNamePatternFieldFilter(); filter.setPackageNamePatternRegex("パッケージ名の正規表現"); filter.setClassNamePatternRegex("クラス名の正規表現"); filter.setFieldNamePatternRegex("フィールド名の正規表現");
Seasarコンテナに登録する場合
FieldNamePatternFieldFilterクラスのインスタンスをSeasarコンテナに登録する場合は, diconファイルに以下のように記述します。
<component name="filter1" class="org.seasar.wicket.injection.fieldfilters.FieldNamePatternFieldFilter"> <property name="packageNamePatternRegex">"パッケージ名の正規表現"</property> <property name="classNamePatternRegex">"クラス名の正規表現"</property> <property name="fieldNamePatternRegex">"フィールド名の正規表現"</property> </component>
上記のコンポーネント名"field1"は例であり,任意の名前をつけることができます。
フィールドフィルタを自作する
インジェクション対象のフィールドかどうかを判断するためのフィールドフィルタを自作することで, あなたが独自の判断基準をS2Wicketに登録することができます。フィールドフィルタの処理は,FieldFilterインタフェースで 規定されています。あなたは,このインタフェースの実装クラスを作成することで, 独自にインジェクション対象とするフィールドの判断基準をS2Wicketに登録することができます。 また,インジェクション対象と判断したフィールドについて, そのフィールドにインジェクションするSeasarコンテナ管理下のコンポーネントオブジェクトを, どのようなコンポーネント名でルックアップするかについても決定します。
FieldFilterインタフェースでは,以下の2つの処理を規定しています。
- isSupported(Field) - 指定されたフィールドがインジェクション対象かどうかを判断し,その結果を返す。
- getLookupComponentName(Field) - 指定されたフィールドに対してインジェクションするオブジェクトのコンポーネント名を返す。
isSupported()メソッドで,指定されたフィールドをインジェクション対象とするかどうかを判断し,その結果をboolean値で返します。 isSupported()メソッドでtrueが返されたフィールドに関しては,次にgetLookupComponentName()メソッドに渡されて, Seasarコンテナからどのコンポーネント名のコンポーネントオブジェクトをルックアップするかを文字列で返します。もし, コンポーネント名ではなく,フィールドの型を使ってルックアップを行いたい場合は,getLookupComponentName()メソッドの戻り値として nullを返します。
例えば「〜Service」という名前のフィールドだった場合にインジェクション対象として,ルックアップ時にフィールド名を コンポーネント名として使用する,という判断ロジックのフィールドフィルタのコードを,以下に示します。
public class ServiceFieldFilter implements FieldFilter { public boolean isSupported(Field field) { String fieldName = field.getName(); return fieldName.endsWith("Service"); } public String getLookupComponentName(Field field) { return field.getName(); } }
もしフィールド名ではなく,フィールドの型でルックアップしたい場合は,「return field.getName();」 を「return null;」に変更します。
フィールドフィルタを登録する
フィールドフィルタは,S2Wicketに登録することで適用されます。フィールドフィルタの登録は,2つの方法があります。
- SeasarComponentInjectionListenerクラスのコンストラクタの引数で明示的に指定する。
- Seasarコンテナに自作したフィールドフィルタを登録する。
SeasarComponentInjectionListenerクラスのコンストラクタには,引数にList<FieldFilter>コレクションを受け取るものが2つあります。 適用したいフィールドフィルタのインスタンスを生成し,それらをListコレクションに追加して, SeasarComponentInjectionListenerクラスのコンストラクタに明示的に渡すことで,コレクションに含まれるフィールドフィルタが 使用されるようになります。この場合,インジェクション対象とする判断は,Listコレクション内でのフィールドフィルタの順番となります。
SeasarComponentInjectionListenerクラスのコンストラクタには,引数にList<FieldFilter>コレクションを受け取らないものもあります。 これを使用した場合,@SeasarComponentアノテーションが付与されたフィールドをインジェクション対象とする AnnotationFieldFilterクラスが暗黙的に適用されます。さらに,自作したフィールドフィルタを,Seasarコンテナにコンポーネント登録 しておくことで,S2Wicketは自動的にそれをルックアップし,暗黙的に適用します。例えば,FieldFilterインタフェースを実装した MyFieldFilterクラスを作成し,それを登録したい場合は,
<components> <component name="myFilter" class="MyFieldFilter" /> </components>
というようにdiconファイルに記述するなどしてSeasarコンテナからルックアップ可能なようにしておけば,自動的に適用されます。 この場合,インジェクション対象とする判断は,Seasarコンテナからフィールドフィルタをルックアップした結果の順序( S2Container#findComponents()メソッドの結果得られる配列の順序)に依存します。ただし,自動的に適用される AnnotationFieldFilterクラスに関しては,必ず最後の評価順となります。
4. SeasarComponentInjectionListenerを登録する
S2Wicketがインジェクション対象とするフィールドは,Wicketによって実行時に管理されるwicket.Component クラスの派生クラスに定義されたフィールドです。各種Componentクラスのオブジェクトは,Wicketアプリケーションの実行時に Wicketフレームワークに登録され,コンポーネントツリーを形成します。 ComponentクラスのオブジェクトがWicketに登録された際のコールバックをアプリケーションが受けるために, wicket.application.IComponentInstantiationListenerインタフェースが提供されています。 S2Wicketは,このIComponentInstantiationListenerインタフェースの実装クラスとして, SeasarComponentInjectionListenerクラスを提供します。SeasarComponentInjectionListener クラスのインスタンスを生成し,wicket.Application#addComponentInstantiationListener()メソッドを 使用してWicketに登録することで,S2Wicketの機能が有効になります。
S2Wicketは,インジェクションするオブジェクトを取得するために,SeasarコンテナであるS2Containerオブジェクトを必要とします。 SeasarComponentInjectionListenerクラスでは,S2Containerオブジェクトを明示的に指定してインスタンスを生成するコンストラクタと, 内部で自動的にS2Containerオブジェクトを取得するコンストラクタの両方を用意しています。
また,フィールドフィルタを明示的に指定するために,SeasarComponentInjectionListenerクラスのコンストラクタには, List<FieldFilter>オブジェクトを引数に持つものが用意されています。暗黙的にフィールドフィルタを適用する場合は, List<FieldFilter>オブジェクトを指定しないコンストラクタを使用します。
つまり,Seasarコンテナを明示的に指定するかしないか,そしてフィールドフィルタを明示的に指定するかしないかで, SeasarComponentInjectionListenerクラスのコンストラクタを使い分けることができます。
SeasarComponentInjectionListenerオブジェクトのWicketへの登録は,Applicationクラス(のサブクラス) のコンストラクタ内では記述しないでください。Applicationクラスのコンストラクタ内は,スレッドにApplicationオブジェクトが アタッチされていないため,S2Wicket内で例外が発生します。init()メソッドをオーバーライドして,その中で記述するのが 良いでしょう。
Seasarコンテナ,フィールドフィルタを自動取得するコンストラクタ
S2Containerオブジェクトおよびフィールドフィルタを自動的に取得するコンストラクタでは, 引数にWebApplicationオブジェクトのみを指定します。
import org.seasar.wicket.injection.fieldfilters.SeasarComponentInjectionListener; public class OrderApplication extends WebApplication { public OrderApplication() { ... } @Override protected void init() { super.init(); ... addComponentInstantiationListener( new SeasarComponentInjectionListener(this)); ... } }
上記の例では,コンストラクタ内でSingletonS2ContainerFactory.getContainer()を呼び出すことにより, S2Containerのインスタンスを取得します。つまり,事前にS2ContainerServletなどでS2Containerオブジェクトが準備され, SingletonS2ContainerFactory.getContainer()によってS2Containerオブジェクトが得られる環境となっている必要があります。
フィールドフィルタは,渡されたS2ContainerオブジェクトからfindComponents(FieldFilter.class)メソッドを呼び出して フィールドフィルタオブジェクトを検索し,さらに@SeasarComponentアノテーションをインジェクション対象とするための AnnotationFieldFilterオブジェクトも登録されます。
Seasarコンテナ,フィールドフィルタを明示的に指定するコンストラクタ
既に獲得しているS2Containerオブジェクトを使用してSeasarComponentInjectionListenerオブジェクトを生成する場合や, 自作したフィールドフィルタを決まった評価順で使用したい場合は, WebApplicationオブジェクトとS2Containerオブジェクト,フィールドフィルタのコレクションを引数に取るコンストラクタを使用します。
import org.seasar.wicket.injection.fieldfilters.SeasarComponentInjectionListener; public class OrderApplication extends WebApplication { public OrderApplication() { ... } @Override protected void init() { super.init(); ... S2Container container = ...; List<FieldFilter> fieldFilters = ...; addComponentInstantiationListener( new SeasarComponentInjectionListener(this, container, fieldFilters)); ... } }
S2Containerオブジェクトをアプリケーション側で管理している場合は,このコンストラクタにより S2Wicketで使用されるようになります。また,自作したフィールドフィルタを引数に渡すことで,List<FieldFilter>コレクションに 格納されている順番で自作したフィールドフィルタが適用されるようになります。このコンストラクタの場合は, AnnotationFieldFilterオブジェクトやSeasarコンテナに登録されている自作したフィールドフィルタオブジェクトは 適用されません。
5. インジェクション対象のフィールドを作成する
SeasarComponentInjectionListenerオブジェクトのWicketへの登録が完了すれば,あとは適用されている フィールドフィルタの規約に従って,インジェクション対象のフィールドを作成していきます。 例えば,フィールドフィルタを自作していない場合は,AnnotationFieldFilterクラスの規約に従って, @SeasarComponentアノテーションを付与したフィールドを作成します。また,例えば前述した ServiceFieldFilterクラスを適用していた場合は,「〜Service」という名前のフィールドを作成すれば, インジェクション対象となります。
6. インジェクションされたオブジェクトを使用する
フィールドフィルタの規約に従って定義されたフィールドは,任意のタイミングで利用することが可能となります。 wicket.Componentクラスのデフォルトコンストラクタ内でIComponentInstantiatinoListenerリスナオブジェクトへの 通知が行われるため,そのクラスがインスタンス化された時には既にインジェクションされた状態です。 例えば,AnnotationFieldFilterフィールドフィルタの適用下で, フォームのサブミット時にインジェクションされたオブジェクトを使用する場合は,以下のように記述します。
import org.seasar.wicket.injection.fieldfilters.SeasarComponent; public class InputPage extends WebPage { @SeasarComponent private OrderService orderService; public InputPage() { ... Form form = new Form("form1") { public void onSubmit() { orderService.order(); } }; } }
form1フォームがサブミットされると,onSubmit()メソッドが呼び出されます。 上記では,orderServiceフィールドにインジェクションされたOrderSerice型のコンポーネントオブジェクトが持つorder()メソッドを呼び出しています。