S2Containerは、Dependency Injection(以降DIと略します)をおこなう軽量コンテナです。DIとは、インターフェースと実装を分離して、お互いがインターフェースのみで会話するようにしようという考え方です。
早速試してみましょう。登場自分物は次のようになります。
- あいさつクラス
- あいさつクライアントクラス
- あいさつクラスから取得したあいさつ(文字列)をコンソールに出力する。
- あいさつメインクラス
- 起動用のクラス。あいさつクラスとあいさつクライアントクラスの組み立ても行う。
Greeting.java
あいさつのためのインターフェースです。
package examples.di;
public interface Greeting {
String greet();
}
GreetingImpl.java
あいさつの実装です。
package examples.di.impl;
import examples.di.Greeting;
public class GreetingImpl implements Greeting {
public String greet() {
return "Hello World!";
}
}
GreetingClient.java
あいさつを利用するクライアントのインターフェースです。
package examples.di;
public interface GreetingClient {
void execute();
}
GreetingClientImpl.java
あいさつを利用するクライアントの実装です。GreetingImpl(実装)を直接利用するのではなく、Greeting(インターフェース)を通じて機能を利用するようにします。
package examples.di.impl;
import examples.di.Greeting;
import examples.di.GreetingClient;
public class GreetingClientImpl implements GreetingClient {
private Greeting greeting;
public void setGreeting(Greeting greeting) {
this.greeting = greeting;
}
public void execute() {
System.out.println(greeting.greet());
}
}
機能を提供する側と利用する側の準備ができました。実行してみましょう。
GreetingMain.java
package examples.di.main;
import examples.di.Greeting;
import examples.di.impl.GreetingClientImpl;
import examples.di.impl.GreetingImpl;
public class GreetingMain {
public static void main(String[] args) {
Greeting greeting = new GreetingImpl();
GreetingClientImpl greetingClient = new GreetingClientImpl();
greetingClient.setGreeting(greeting);
greetingClient.execute();
}
}
実行結果は下記のようになります。
Hello World!
このように機能の利用者(GreetingClientImpl)は、インターフェース(Greeting)経由で機能を利用するようにし、具体的な実装オブジェクトは、
実行時に第三者(この場合はGreetingMain)が提供するというのが、DIの基本的な考え方になります。
でも、GeetingMainのように設定内容をソースに直接書いてしまうと、設定に変更が起きるたびに
ソースコードを修正する必要が出てきます。それを避けるために、登場するのがDIContainerです。
設定内容を外出しの設定ファイルにして、DIContainerがその設定ファイルを読み込んでオブジェクトの組み立てを行うのです。
それでは、先程の設定内容を設定ファイルに書き出してみましょう。S2Containerでは、拡張子は".dicon"になります。
GreetingMain2.dicon
<?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="greeting" class="examples.di.impl.GreetingImpl"/> <component name="greetingClient" class="examples.di.impl.GreetingClientImpl"> <property name="greeting">greeting</property> </component> </components>
詳しくは、S2Containerタグリファレンスを参照してください。まず、ポイントを説明します。
<component name="greeting" class="examples.di.impl.GreetingImpl"/>
上記のようにコンポーネントを定義しています。これは、下記のJavaのコードに相当します。
Greeting greeting = new GreetingImpl();
conponentタグのname属性でコンポーネント名を指定し、class属性でクラス名を指定します。次は、greetingClientの設定です。
<component name="greetingClient" class="examples.di.impl.GreetingClientImpl"> <property name="greeting">greeting</property> </component>
propertyタグのname属性でプロパティ名を指定し、ボディでコンポーネント名を指定します。これは、下記のJavaのコードに相当します。コンポーネント名は、「"」で囲まないことに注意してください。「"」で囲むと文字列とみなされてしまいます。
GreetingClientImpl greetingClient = new GreetingClientImpl();
greetingClient.setGreeting(greeting);
S2Containerを利用する起動クラスは次のようになります。
GreetingMain2.java
package examples.di.main;
import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.factory.S2ContainerFactory;
import examples.di.GreetingClient;
public class GreetingMain2 {
private static final String PATH =
"examples/di/dicon/GreetingMain2.dicon";
public static void main(String[] args) {
S2Container container =
S2ContainerFactory.create(PATH);
GreetingClient greetingClient = (GreetingClient)
container.getComponent("greetingClient");
greetingClient.execute();
}
}
S2Containerは、S2ContainerFactory#create(String path)を使って作成します。詳しくは、
S2Containerの作成を参照してください。
コンポーネント(greetingClient)は、S2Container#getComponent(String componentName)を使って取得します。
詳しくは、コンポーネントの取得を参照してください。
実行結果は先程と同様に下記のようになります。
Hello World!
DIとよく一緒に用いられるのがAOPです。AOPとは、ログ出力だとか複数のクラスに分散するロジックを
モジュール化する技術です。それでは、既に作成済みの
GreetingImpl、GreetingClinetImplのソースを修正することなく、ログ(トレース)を出力させて見ましょう。
AOPを適用した設定ファイルは、次のようになります。
GreetingMain3.dicon
<?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="aop.dicon"/> <component name="greeting" class="examples.di.impl.GreetingImpl"> <aspect>aop.traceInterceptor</aspect> </component> <component name="greetingClient" class="examples.di.impl.GreetingClientImpl"> <property name="greeting">greeting</property> <aspect>aop.traceInterceptor</aspect> </component> </components>
Seasar2では、良く使われるAOPのモジュールがaop.diconの中にあらかじめ定義されています。
次のようにして、includeタグを使います。
詳しくは、S2Container定義の分割とインクルードを参照してください。
<include path="aop.dicon"/>
コンポーネントにAOPを適用するには、componentタグの子タグの
aspectタグのボディにAOPのモジュール名を指定します。aop.traceInterceptorがAOPのモジュール名になります。
<aspect>aop.traceInterceptor</aspect>
AOPの設定は以上です。それではGreetingMain3を実行してみます。GreetingMain2との違いは、設定ファイルのパスだけです。
GreetingMain3.java
package examples.di.main;
import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.factory.S2ContainerFactory;
import examples.di.GreetingClient;
public class GreetingMain3 {
private static final String PATH =
"examples/di/dicon/GreetingMain3.dicon";
public static void main(String[] args) {
S2Container container =
S2ContainerFactory.create(PATH);
GreetingClient greetingClient = (GreetingClient)
container.getComponent("greetingClient");
greetingClient.execute();
}
}
実行結果は、下記のようになります。元のソースコードを修正していないのに、ログが出力されていることが分かります。
DEBUG 2005-10-11 21:01:49,655 [main] BEGIN examples.di.impl.GreetingClientImpl#execute()
DEBUG 2005-10-11 21:01:49,665 [main] BEGIN examples.di.impl.GreetingImpl#greet()
DEBUG 2005-10-11 21:01:49,665 [main] END examples.di.impl.GreetingImpl#greet() : Hello World!
Hello World!
DEBUG 2005-10-11 21:01:49,675 [main] END examples.di.impl.GreetingClientImpl#execute() : null
これで、S2Containerの基本的な使い方をマスターすることができました。
でも、なんだか設定ファイルを書くのって面倒ですよね。
S2Containerでは、できるだけ設定ファイルの記述量を減らすために、次のコンセプトが実現されています。
これは適切な規約を決め、その規約を守っていれば、設定なしでも動くようにするというものです。
例えば、先程の設定ファイルで、下記のように明示的にプロパティを指定している部分がありました。
<component name="greetingClient" class="examples.di.impl.GreetingClientImpl"> <property name="greeting">greeting</property> </component>
S2Containerには、プロパティの型がインターフェースの場合、
プロパティの型を実装したのコンポーネントがコンテナに登録されていれば、
特に設定をしなくても自動的にDIするという機能があります。
これは、「プロパティの型をインターフェースで定義する」というDIで推奨されている規約を守れば、
S2Containerが自動的に処理してくれるということです。
規約というとめんどくさいと思われがちですが、「推奨されていて、かつ守ると開発が楽になる」なら、
規約に準拠しようというモチベーションが生まれます。そこがポイントなのです。
上記の設定は、次のように簡略化することができます。
<component name="greetingClient" class="examples.di.impl.GreetingClientImpl"> </component>
実は、先程のAOPの例でも「Convention over Configuration」が適用されています。
AOPでは通常、AOPのモジュールをどこ(例えばどのメソッド)に適用するのかということを
pointcutで指定するのですが、S2AOPの場合、
「インターフェースを使う」という推奨されている規約が守られていれば、
pointcutを指定しなくても、自動的にインターフェースで定義されているすべてのメソッドに適用するという機能が用意されています。
そのため、先程の例では、pointcutを指定する必要が無かったのです。
「Convention over Configuration」によって、DIやAOPの設定は、簡略化できますが、
扱うコンポーネントの数が増えると、コンポーネントの登録もかなり大変な作業になってきます。
このコンポーネントの登録も自動化してしまうのがコンポーネントの自動登録機能です。
先程のGreetingImpl、GreetingClientImplの登録を自動化する設定は次のようになります。
<component class="org.seasar.framework.container.autoregister.FileSystemComponentAutoRegister"> <initMethod name="addClassPattern"> <arg>"examples.di.impl"</arg> <arg>".*Impl"</arg> </initMethod> </component>
FileSystemComponentAutoRegisterコンポーネントは、
addClassPatternメソッドで指定されたクラスをファイルシステムから探して、
S2Containerに自動登録します。initMethodタグについては、メソッド・インジェクションを参照してください。
addClassPatternメソッドの最初の引数は、自動登録したいコンポーネントのパッケージ名です。
子供のパッケージも再帰的に検索します。2番目の引数は、クラス名です。正規表現が使えます。「,」区切りで複数記述することもできます。
コンポーネントの自動登録によって、後からコンポーネントを追加しても、設定を追加する必要がなくなるので、
手間を大幅に削減できるようになります。
コンポーネントの登録が自動化できるようになると、次は、アスペクトの登録を自動化したくなることでしょう。
先程のGreetingImpl、GreetingClientImpへのアスペクトの登録を自動化する設定は次のようになります。
<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>
interceptorプロパティでAOPのモジュール名を指定します。addClassPatternメソッドは、コンポーネントの自動登録と同様なので、
特に説明は必要ないでしょう。
コンポーネントの自動登録とアスペクトの自動登録を組み合わせた例は次のようになります。
GreetingMain4.dicon
<?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="aop.dicon"/>
<component class="org.seasar.framework.container.autoregister.FileSystemComponentAutoRegister"> <initMethod name="addClassPattern"> <arg>"examples.di.impl"</arg> <arg>".*Impl"</arg> </initMethod> </component>
<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> </components>
それではGreetingMain4を実行してみます。
自動登録を使う場合は、S2Container#init()とS2Container#destroy()を呼び出す必要があります。
GreetingMain4.java
package examples.di.main;
import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.factory.S2ContainerFactory;
import examples.di.GreetingClient;
public class GreetingMain4 {
private static final String PATH =
"examples/di/dicon/GreetingMain4.dicon";
public static void main(String[] args) {
S2Container container =
S2ContainerFactory.create(PATH);
container.init();
try {
GreetingClient greetingClient = (GreetingClient)
container.getComponent("greetingClient");
greetingClient.execute();
} finally {
container.destroy();
}
}
}
実行結果は、GreetingMain3と同様に下記のようになります。
DEBUG 2005-10-12 16:00:08,093 [main] BEGIN examples.di.impl.GreetingClientImpl#execute()
DEBUG 2005-10-12 16:00:08,103 [main] BEGIN examples.di.impl.GreetingImpl#greet()
DEBUG 2005-10-12 16:00:08,103 [main] END examples.di.impl.GreetingImpl#greet() : Hello World!
Hello World!
DEBUG 2005-10-12 16:00:08,103 [main] END examples.di.impl.GreetingClientImpl#execute() : null
たいていの場合、自動登録と自動バインディングの仕組みでうまくいくでしょう。
自動登録したくないコンポーネントがある場合には、自動登録コンポーネントに用意されているaddIgnoreClassPatternメソッドで、
自動登録の対象外にすることができます。
自動バインディングしたくないプロパティがある場合は、Bindingアノテーションを使い、
設定ファイルを使わずに、細かい調整をすることもできます。
Hotswapを使えば、アプリケーションの稼動中にクラスファイルを書き換えて、
直ぐにその結果を試すことができます。いちいち、アプリケーションを再起動しなくてもよくなるので、
開発効率を大きく向上させることができます。
これで、S2Containerの高度な使い方をマスターすることができました。後は、必要に応じてマニュアルを参照すると良いでしょう。
S2Containerを使用するためには、定義ファイルを作成する必要があります。
定義ファイルは、コンポーネントを組み立てるための設計書のようなものです。
形式はXMLで、拡張子は、diconです。
S2Containerの定義は、次のようになります。
<?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="..." class="...">
...
</component>
<component name="..." class="...">
...
</component>
</components>
DOCTYPEは省略できません。diconファイルを作成する場合は、上記のサンプルをコピー&ペーストしてください。ルートはcomponentsタグです。コンポーネントごとに、componentタグを定義していきます。componentタグのclass属性でコンポーネントのクラスの完全限定名を指定します。name属性には、コンポーネント名を指定します。詳細は、S2Container定義タグリファレンスを参照してください。
<components>
<component name="hoge" class="examples.dicon.HogeImpl"/>
</components>
S2Containerを生成する方法は二通りあります。
- SingletonS2ContainerFactoryを使用する。
- S2ContainerFactoryを使用する。
SingletonS2ContainerFactoryを使用する場合は、次のメソッドを使用します。
- org.seasar.framework.container.factory.SingletonS2ContainerFactory#init()
定義ファイルはCLASSPATHで指定されているディレクトリにあるapp.diconが使われます。
作成したS2Containerは、どこからでも次のメソッドで取得することができます。
- org.seasar.framework.container.factory.SingletonS2ContainerFactory#getContainer()
SingletonS2ContainerFactory.init();
...
S2Container container = SingletonS2ContainerFactory.getContainer();
定義ファイルのパスを指定する場合はinit()を呼び出す前に次のメソッドを使用します。
- org.seasar.framework.container.factory.SingletonS2ContainerFactory#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();
S2ContainerFactoryを使用する場合は、次のメソッドを使用します。
- org.seasar.framework.container.factory.S2ContainerFactory#create(String path)
S2Containerを生成した後に次のメソッドを呼び出します。
- org.seasar.framework.container.S2Container#init()
private static final String PATH = "aaa/bbb/ccc.dicon";
...
S2Container container = S2ContainerFactory.create(PATH);
container.init();
この方法で取得したコンテナのインスタンスは、アプリケーションで管理する必要があります。
S2Containerからコンポーネントを取り出すには、次のメソッドを使用します。
- org.seasar.framework.container.S2Container#getComponent(Object componentKey)
引数には、コンポーネントのクラスもしくはコンポーネント名を指定できます。詳しくは、componentタグを参照してください。コンポーネントのクラスを指定する場合、コンポーネント instanceof クラスがtrueを返すクラスなら指定することができます。しかし、S2Containerの中に指定したクラスを実装しているコンポーネントが複数ある場合、S2Containerは、どのコンポーネントを返せばよいのか判断できないため、TooManyRegistrationRuntimeExceptionが発生します。実装コンポーネントがユニークに決まるクラスを指定してください。コンポーネント名で取得することもできます。その場合も、同一の名前をもつコンポーネントが複数登録されている場合、TooManyRegistrationRuntimeExceptionが発生します。コンポーネント名指定の場合、スペルミスをする可能性もあるので、できるだけクラス指定のほうが良いでしょう。
例) クラスを指定してコンポーネントを取得する場合
S2Container container = S2ContainerFactory.create(PATH);
Hoge hoge = (Hoge) container.getComponent(Hoge.class);
例) コンポーネント名を指定してコンポーネントを取得する場合
S2Container container = S2ContainerFactory.create(PATH);
Hoge hoge = (Hoge) container.getComponent("hoge");
Dependency Injectionには、コンポーネントの構成に必要な値をコンストラクタで設定する(Constructor Injection)のか、セッター・メソッドで設定する(Setter Injection)のか、初期化メソッドで設定する(Method Injection)のかで、タイプが分かれます。Method InjectionはS2Containerのオリジナルです。S2Containerはすべてのタイプとそのハイブリッド型もサポートします。
コンストラクタの引数にDIを行うのが、コンストラクタ・インジェクションです。
S2Containerの定義ファイルには、次の内容を記述します。
- コンポーネントの指定
コンポーネントは、componentタグで組み立てます。class属性でクラス名を指定します。 name属性でコンポーネントに名前を付けることもできます。
- コンストラクタの引数の指定
コンポーネントのコンストラクタの引数は、componentタグの子タグであるargタグを使って指定します。 文字列の場合は、ダブルコーテーション(")で囲みます。
<components>
<component name="..." class="...">
<arg>...</arg>
</component>
</components>
セッター・インジェクションとは、任意のプロパティにセッターメソッドを使用してDIを行います。
S2Containerの定義ファイルには、次の内容を記述します。
- コンポーネントの指定
コンポーネントの指定は、コンストラクタ・インジェクションと同様です。
- プロパティの指定
コンポーネントのプロパティは、componentタグの子タグであるpropertyタグを使って指定します。 name属性でプロパティ名を指定します。
<components>
<component name="..." class="...">
<property name="...">...</property>
</component>
</components>
メソッド・インジェクションとは、任意のメソッドを呼び出して、DIを行います。
S2Containerの定義ファイルには、次の内容を記述します。
- コンポーネントの指定
コンポーネントの指定は、コンストラクタ・インジェクションと同様です。
- 初期化メソッドの指定
initMethodタグを使って、コンポーネントの任意のメソッドを呼び出します。name属性で、メソッド名を指定します。
argタグで引数を指定することや、name属性を省略して、ボディで、OGNL式を使うこともできます。
<components>
<component name="..." class="...">
<initMethod name="...">
<arg>...</arg>
</initMethod>
</component>
</components>
すべてのコンポーネントを1つのファイルに記述すると、直ぐに肥大化してしまい管理が難しくなります。そのため、コンポーネントの定義を複数に分割する機能と分割された定義をインクルードして1つにまとめる機能がS2Containerにあります。S2Container定義ファイルのインクルードは次のようにして行います。
<components>
<include path="bar.dicon"/>
</components>
includeタグのpath属性で取り込みたいS2Container定義ファイルのパスを指定します。詳しくは、includeタグを参照してください。 コンポーネントの検索順は、先ず自分自身に登録されているコンポーネントを探して、見つからない場合は、includeされている順に子供のS2Containerに登録されているコンポーネントを検索し、最初に見つかったコンポーネントが返されます。
<components>
<include path="aaa.dicon"/>
<include path="bbb.dicon"/>
<component class="example.container.Foo" />
</components>
コンポーネントの定義を分割した場合に、複数のコンポーネント定義間で名前が衝突しないように、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>
同一のコンポーネント定義内では、名前空間なしで参照できます。他のS2Container定義のコンポーネントを参照する場合は、名前空間.をコンポーネント名の頭につけます。foo.aaaとbar.aaaは同じ名前がついていますが、名前空間が異なっているので、違うコンポーネントとして認識されます。
S2Containerで、コンポーネントのインスタンスをどのように管理するのかを指定するのが、componentタグのinstance属性です。
instance属性 |
説明 |
singleton(default) |
S2Container.getComponent()を何度呼び出しても同じインスタンスが返されます。 |
prototype |
S2Container.getComponent()を呼び出すたびに新たなインスタンスが返されます。 |
request |
リクエスト毎に1つのインスタンスが作成されます。name属性に指定した名前で、コンポーネントがリクエストに格納されます。requestを使う場合は、S2ContainerFilterを設定する必要があります。 |
session |
セッション毎に1つのインスタンスが作成されます。name属性に指定した名前で、コンポーネントがセッションに格納されます。sessionを使う場合は、S2ContainerFilterを設定する必要があります。 |
outer |
コンポーネントのインスタンスは、S2Container外で作成し、Dependency Injectionだけを行います。アスペクト、コンストラクタ・インジェクションは適用できません。 |
initMethodやdestroyMethodでコンポーネントのライフサイクルもコンテナで管理することができます。S2Containerの開始時(S2Container.init())にinitMethodタグで指定したメソッドが呼び出され、S2Containerの終了時(S2Container.destroy())にdestroyMethodタグで指定したメソッドが呼び出されるようになります。initMethodはコンポーネントがコンテナに登録した順番に実行され、destroyMethodはその逆順に呼び出されることになります。instance属性がsingleton以外の場合、destroyMethodを指定しても無視されます。java.util.HashMap#put()メソッドに初期化(aaaに111を設定)・終了処理(aaaにnullを設定)を設定する場合は、次のようになります。
<components namespace="bar">
<component name="map" class="java.util.HashMap">
<initMethod name="put">
<arg>"aaa"</arg>
<arg>111</arg>
</initMethod>
<destroyMethod name="put">
<arg>"aaa"</arg>
<arg>null</arg>
</destroyMethod>
</component>
</components>
コンポーネント間の依存関係は、型がインターフェースの場合、コンテナによって自動的に解決されます。これがS2Containerのデフォルトですが、componentタグのautoBinding属性を指定することで細かく制御することもできます。
autoBinding |
説明 |
auto(default) |
コンストラクタとプロパティの自動バインディングが適用されます。
|
constructor |
コンストラクタの自動バインディングが適用されます。 |
property |
プロパティの自動バインディングが適用されます。 |
none |
コンストラクタ、プロパティの手動バインディングのみ。 |
コンストラクタの自動バインディングのルールは次のようになります。
- コンストラクタの引数が明示的に指定されている場合は、自動バインディングは適用されません。
- 上記に該当しない場合、引数のないデフォルトコンストラクタが定義されているなら、そのコンストラクタを使い、自動バインディングは適用されません。
- 上記に該当しない場合、引数の型がすべてインターフェースで最も引数の数の多いコンストラクタを使います。
そして、引数の型を実装したコンポーネントをコンテナから取得して適用します。
- 上記に該当しない場合、自動バインディングは適用されません。
プロパティの自動バインディングのルールは次のようになります。
- プロパティが明示的に指定されている場合は、自動バインディングは適用されません。
- 上記に該当しない場合、プロパティ名と同一のコンポーネントがコンテナに登録されてプロパティに代入可能なら、
そのコンポーネントが適用されます。
- 上記に該当しない場合、プロパティの型がインターフェースでプロパティの型を実装したコンポーネントがコンテナに登録されていれば、
そのコンポーネントが適用されます。
- 上記に該当しない場合、自動バインディングは適用されません。
propertyタグのbindingType属性で、プロパティごとに細かく制御することもできます。
bindingType |
説明 |
must |
自動バインディングが適用されない場合、例外が発生します。
|
should(default) |
自動バインディングが適用されない場合、警告を通知します。 |
may |
自動バインディングが適用されない場合、何もおきません。 |
none |
autoBinding属性が、auto、propertyの場合でも、自動バインディングは適用されません。 |
コンポーネントはS2Containerに依存しないことが望ましいのですが、コンポーネントによっては、S2Containerのメソッドを呼び出したい場合もあるでしょう。S2Container自身もcontainerという名前で、登録されているので、arg,propertyタグのボディでcontainerを指定することで、コンテナのインスタンスを取得できます。また、S2Container型のsetterメソッドを定義しておいて自動バインディングで設定することもできます。arg,propertyタグでcontainerを指定する場合は、次のようになります。
<components>
<component class="examples.dicon.BarImpl">
<arg>container</arg>
</component>
<component class="examples.dicon.FooImpl">
<property name="foo">container</property>
</component>
</components>
これまでは、Javaアプリケーションで、明示的にS2Containerを作成していましたが、Webアプリケーションの場合、誰がS2Containerを作成するのでしょうか。その目的のために次のクラスが用意されています。
- org.seasar.framework.container.servlet#S2ContainerServlet
S2ContainerServletを使うためには、web.xmlに次の項目を記述します。
<servlet>
<servlet-name>s2servlet</servlet-name>
<servlet-class>org.seasar.framework.container.servlet.S2ContainerServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<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>
</servlet>
<servlet-mapping>
<servlet-name>s2servlet</servlet-name>
<url-pattern>/s2servlet</url-pattern>
</servlet-mapping>
configPathでルートとなるS2Container定義のパスを指定します。定義ファイルはWEB-INF/classesにおきます。S2ContainerServletは、他のサーブレットよりもはやく起動されるようにload-on-startupタグを調整してください。S2ContainerServletが起動した後は、次のメソッドでS2Containerのインスタンスを取得することができます。
- org.seasar.framework.container.factory.SingletonS2ContainerFactory#getContainer()
また、S2Containerのライフサイクルは、S2ContainerServletと連動します。debugパラメータをtrueにすると、次のようにして、稼動中にS2Containerを再起動できます。xxxはWebアプリケーションのコンテキスト名です。
http://localhost:8080/xxx/s2servlet?command=restart
S2ContainerServletを使っている場合、ServletContextをservletContextという名前のコンポーネントとしてアクセスできるようになります。
ルートのS2Containerの定義ファイルは、慣例でapp.diconという名前にします。
通常はWEB-INF/classesにおくと良いでしょう。
コンポーネントにAOPを適用することもできます。例えば、ArrayListにTraceInterceptorを適用したい場合次のようにします。
<components>
<component name="traceInterceptor"
class="org.seasar.framework.aop.interceptors.TraceInterceptor"/>
<component class="java.util.ArrayList">
<aspect>traceInterceptor</aspect>
</component>
<component class="java.util.Date">
<arg>0</arg>
<aspect pointcut="getTime, hashCode">traceInterceptor</aspect>
</component>
</components>
aspectタグのボディでInterceptorの名前を指定します。pointcut属性にカンマ区切りで対象となるメソッド名を指定することができます。pointcut属性を指定しない場合は、コンポーネントが実装しているインターフェースのすべてのメソッドが対象になります。メソッド名には正規表現(JDK1.4のregex)も使えます。この定義を使うサンプルは次のようになります。
private static final String PATH =
"examples/dicon/Aop.dicon";
S2Container container = S2ContainerFactory.create(PATH);
List list = (List) container.getComponent(List.class);
list.size();
Date date = (Date) container.getComponent(Date.class);
date.getTime();
date.hashCode();
date.toString();
実行結果は次のようになります。
BEGIN java.util.ArrayList#size()
END java.util.ArrayList#size() : 0
BEGIN java.util.Date#getTime()
END java.util.Date#getTime() : 0
BEGIN java.util.Date#hashCode()
BEGIN java.util.Date#getTime()
END java.util.Date#getTime() : 0
END java.util.Date#hashCode() : 0
BEGIN java.util.Date#getTime()
END java.util.Date#getTime() : 0
components、component、arg、propertyタグにメタデータを指定することもできます。metaタグはメタデータを指定したいタグの子タグに指定します。例えば、componentsタグにメタデータを指定したい場合次のようにします。
<components>
<meta name="aaa">111</meta>
</components>
コンポーネントに対して、HttpServletRequestを自動的にバインディングすることが出来ます。そのためには、コンポーネントに、setRequest(HttpServletRequest request)のメソッドを定義します。そうすると、S2Containerが自動的にリクエストを設定します。また、次のようにFilterを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>
同様にHttpServletResponse、HttpSessionもsetterメソッドを定義するだけで、自動バインディングすることが出来ます。また、S2ContainerFilterを使うと、HttpServletRequest、HttpServletResponse、HttpSessionをそれぞれrequest、response、sessionという名前のコンポーネントとしてアクセスできるようになります。
自動バインディングにより、DIの設定はほぼ自動化できます。
アノテーションを使って細かく制御することも可能です。
さらに、コンポーネントの登録も自動化してしまおうというのが、コンポーネントの自動登録機能です。
ファイルシステムからクラスを検索としてコンポーネントを自動登録するコンポーネントです。
プロパティ |
説明 |
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番目の引数は、クラス名です。正規表現が使えます。「,」区切りで複数記述することもできます。 |
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番目の引数は、クラス名です。正規表現が使えます。「,」区切りで複数記述することもできます。 |
ファイルシステムまたは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により制御します。
クラスの完全修飾名からパッケージ部分を除き、最後がImplまたはBeanで終わっていたら削除し、
先頭を小文字にした名前をコンポーネントの名前に設定します。
例えば、aaa.HogeImplクラスの場合、コンポーネント名は、hogeになります。
プロパティ |
説明 |
decapitalize |
コンポーネント名の先頭を小文字にする場合はtrueを指定します。デフォルトはtrueです。 |
メソッド |
説明 |
setCustomizedName |
デフォルトのルールに従わないクラスを登録します。最初の引数は、クラスの完全修飾名です。
2番目の引数は、コンポーネント名です。 |
addIgnoreClassSuffix |
クラス名の末尾から削除する部分を指定します。デフォルトでImplおよびBeanが登録されています。 |
addReplaceRule |
正規表現による置換ルールを追加します。最初の引数は正規表現です。2番目の引数は置換文字列です。 |
clearReplaceRule |
setCustomizedName、addIgnoreClassSuffix、addReplaceRuleで登録した変換規則をクリアします。デフォルトで登録されているImplおよびBeanもクリアされます。 |
パッケージ名またはその一部で修飾されたクラス名をコンポーネントの名前に設定します。
クラスの完全修飾名から最後が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>
コンポーネントの自動登録により、コンポーネントの登録は自動化できます。
さらに、アスペクトの登録も自動化してしまおうというのが、アスペクトの自動登録機能です。
コンポーネントの自動登録と組み合わせる場合は、コンポーネントの自動登録の設定よりも後に、アスペクトの自動登録の設定を記述する必要があります。
アスペクトを適用されるコンポーネントは、アスペクトの自動登録の設定よりも後に記述する必要があります。
<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を指定しない場合は、コンポーネントが実装しているインターフェースのすべてのメソッドが対象になります。メソッド名には正規表現(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>
あるインタフェースを実装したクラスに対してアスペクトを自動登録するコンポーネントです。
プロパティ |
説明 |
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>
従来、ソースコードを書き換えてコンパイルした場合、コンパイル後の機能を試すためには、
アプリケーション(正確にはClassLoader)を再起動する必要がありました。
アプリケーションサーバ上で、アプリケーションを再起動するのは、非常に時間がかかるものです。
「かったるい」と思っていた方も多くいることでしょう。
Seasar2では、アプリケーションを稼動させたまま、クラスファイルを置き換えて、
即座に試すことのできるHotswap機能を実現しています。
これであの「イライラ」からも解放されます。余分な待ち時間が無いので生産性を向上させることもできます。それでは、試してみましょう。
Greeting.java
package examples.hotswap;
public interface Greeting {
String greet();
}
GreetingImpl.java
package examples.hotswap.impl;
import examples.hotswap.Greeting;
public class GreetingImpl implements Greeting {
public String greet() {
return "Hello";
}
}
hotswap.dicon
<components> <component class="examples.hotswap.impl.GreetingImpl"/> </components>
ここまでは、特に変わったところはありません。ポイントはこれからです。
s2container.diconを使って、hotswapモードに切り替えます。
s2container.dicon
<components> <component class="org.seasar.framework.container.factory.S2ContainerFactory$DefaultProvider"> <property name="hotswapMode">true</property> </component> </components>
s2container.diconはクラスパスの通っているルートのディレクトリに置いておけば、自動で認識されます。
S2ContainerFactory#configure()を使って明示的に指定することもできます。
GreetingMain.dicon
package examples.hotswap.main;
import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.factory.S2ContainerFactory;
import examples.hotswap.Greeting;
public class GreetingMain {
private static final String CONFIGURE_PATH =
"examples/hotswap/dicon/s2container.dicon";
private static final String PATH =
"examples/hotswap/dicon/hotswap.dicon";
public static void main(String[] args) throws Exception {
S2ContainerFactory.configure(CONFIGURE_PATH);
S2Container container = S2ContainerFactory.create(PATH);
System.out.println("hotswapMode:" + container.isHotswapMode());
container.init();
try {
Greeting greeting = (Greeting) container
.getComponent(Greeting.class);
System.out.println(greeting.greet());
System.out.println("Let's modify GreetingImpl, then press ENTER.");
System.in.read();
System.out.println("after modify");
System.out.println(greeting.greet());
} finally {
container.destroy();
}
}
}
hotswapを使うためには、S2Container#init()を呼び出す必要があります。
実行すると"Hello"と表示された後に、プログラムが停止するので、
GreetingImpl#greet()が"Hello2"を返すように修正してコンパイルします。
その後、コンソールにフォーカスを当ててENTERキーを押してください。
同じインスタンスなのにかかわらず、classファイルが入れ替わっていることがわかります。
これは、インスタンスモードがsingletonの場合の例ですが、
インスタンスモードがprototypeの場合は、S2Container#getComponent()のタイミングでクラスが入れ替わります。
実行結果
hotswapMode:true
Hello
Let's modify GreetingImpl, then press ENTER.
after modify
Hello2
hotswapを使うためには、コンポーネントがインターフェースを提供し、
コンポーネントの利用者側は、インターフェース経由でコンポーネントを利用する必要があります。
インスタンスモードがrequest、sessionの場合は、
そのコンポーネントが他のコンポーネントから参照されていない場合には、
インターフェースなしでもhotswapを利用することができます。
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>
ルートのタグになります。
名前空間を指定することができます。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>
分割されたS2Containerの定義を取り込む場合に使います。
定義ファイルのパスを指定することができます。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>
コンポーネントを定義します。
クラスの完全限定名を指定します。ボディで、OGNL式を使ってコンポーネントを指定した場合は、class属性を省略することができます。OGNL式を使った場合にclass属性を指定すると、型チェックを行います。
名前を指定することもできます。Javaの識別子として使えるものにします。詳しくは、コンポーネントの取得を参照してください。
S2Containerがどのようにコンポーネントのインスタンスを管理するのかを指定することができます。singleton(デフォルト)、prototype、outer、request、sessionを指定することができます。詳しくは、インスタンス管理を参照してください。
S2Containerがコンポーネントの依存関係をどのように解決するのかを指定できます。auto(デフォルト)、constructor、property、noneを指定することができます。詳しくは、自動バインディングを参照してください。
componentタグの子タグとして使った場合は、コンストラクタの引数になります。記述した順番でコンストラクタに渡されます。
initMethodタグ、destroyMethodタグの子タグとして使った場合は、メソッドの引数になります。記述した順番でメソッドに渡されます。
引数として渡される実際の値は、ボディで、OGNL式を使うか、子タグで、componentタグを使います。
componentタグの子タグとして使います。プロパティとして設定される実際の値は、ボディで、OGNL式を使うか、子タグで、componentタグを使います。
プロパティ名を指定します。
プロパティごとに自動バインディングを細かく制御できます。must、should(デフォルト)、may、noneを指定することができます。詳しくは、自動バインディングを参照してください。
componentsタグ、componentタグ、argタグ、propertyタグの子タグとして使います。メタデータの値は、ボディで、OGNL式を使うか、子タグで、componentタグを使います。
メタ名を指定します。
componentタグの子タグとして使います。引数は、子タグで、argタグを使います。name属性を書かずに、OGNL式を使って、コンポーネントのメソッドを呼び出すこともできます。initMethodタグが定義されているコンポーネント自身を表す#self、System.outを表す#out、System.errを表す#errがinitMethodタグ内だけで有効なオブジェクトとして使えます。
メソッド名を指定します。
<?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>
initMethodタグと同様です。
アスペクトをコンポーネントに組み込みます。詳しくは、S2AOPのaspectタグの説明を参照してください。
componentsタグ、componentタグ、argタグ、propertyタグの子タグとしてdescriptionタグを使うことができます。自由に説明を記述できます。
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アノテーション、backport175アノテーション、定数アノテーションの3種類を用意しています。一般的にアノテーションといえば、Java 5から導入された
Tigerアノテーションですが、それだけだと、JDK1.4のユーザが利用できなくなってしまいます。
できるだけ、多くの方にアノテーションを便利に使ってもらいたいため、JDK1.4で、
Tiger風アノテーション(JSR-175)を使うことのできる
backport175アノテーション、public static finalな定数を利用する定数アノテーションを用意しています。
componentタグのかわりに使えるのが、Componentアノテーションです。
Tigerアノテーションは以下のようになります。
@Component(name="xxx", instance=InstanceType.PROTOTYPE,
autoBinding=AutoBindingType.PROPERTY)
public class Xxx {
...
}
backport175アノテーションは以下のようになります。
/**
* @org.seasar.framework.container.annotation.backport175.Component(
* name = "xxx",
* instance = "prototype",
* autoBinding = "property")
*/
public class Xxx {
...
}
定数アノテーションは以下のようになります。
public static final String COMPONENT =
"name = xxx, instance = prototype, autoBinding = property";
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) {
...
}
backport175アノテーションは以下のようになります。
/**
* @org.seasar.framework.container.annotation.backport175.Binding("aaa2")
*/
public void setAaa(String aaa) {
...
}
/**
* @org.seasar.framework.container.annotation.backport175.Binding(bindingType="none")
*/
public void setBbb(String bbb) {
...
}
/**
* @org.seasar.framework.container.annotation.backport175.Binding
*/
public void setCcc(String ccc) {
...
}
定数アノテーションはプロパティ名_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タグと異なり、複数定義することはできないので、
複数のインターセプタを適用したい場合は、InterceptorChainを使ってください。ポイントカットを指定したいときは、pointcut属性を指定します。クラスに対するアノテーションの場合、pointcutを指定しないときは、そのクラスが実装しているすべてのインターフェースのメソッドが対象になります。
Tigerアノテーションは以下のようになります。
@Aspect("aop.traceInterceptor")
public class Xxx {
...
}
public class Xxx {
...
@Aspect("aop.traceInterceptor")
public void hoge() {
...
}
}
backport175アノテーションは以下のようになります。
/**
* @org.seasar.framework.container.annotation.backport175.Aspect(
* "aop.traceInterceptor")
*/
public class Xxx {
...
}
public class Xxx {
...
/**
* @org.seasar.framework.container.annotation.backport175.Aspect(
* "aop.traceInterceptor")
*/
public void hoge() {
...
}
}
定数アノテーションは以下のようになります。pointcutを複数指定したい場合は、pointcut= get.*\nexecute.*のように\nで区切ってください。
\n以外の区切り文字を使ってはいけません。
public static final String ASPECT =
"value=aop.traceInterceptor, pointcut=getAaa";
initMethodタグのかわりに使えるのが、InitMethodアノテーションです。
initMethodタグと異なり、OGNLの式を書いたり、引数を設定することはできません。
Tigerアノテーションは以下のようになります。
public class Xxx {
...
@InitMethod
public void init() {
...
}
}
backport175アノテーションは以下のようになります。
public class Xxx {
...
/**
* @org.seasar.framework.container.annotation.backport175.InitMethod
*/
public void init() {
...
}
}
定数アノテーションは以下のようになります。初期化メソッドを複数指定したい場合は、カンマ(,)で区切ってください。
public static final String INIT_METHOD = "init";
|