Seasar DI Container with AOP  

Quickstart

S2Container is a light weight container for Dependency Injection (DI) operations. DI is an idea to separate the interface and implementation, to communicate with each other through the interface.

First Step

Let us give this a try. The following objects will be used.

  • Greeting Class
    • Returns a greeting string.
  • Greeting Client Class
    • Outputs string obtained from the greeting class to console.
  • Greeting Main Class
    • Startup Class. Also assembles greeting and greeting client class.
Greeting.java

Greeting Interface.

package examples.di;

public interface Greeting {

    String greet();
}
GreetingImpl.java

Implementation of the greeting.

package examples.di.impl;

import examples.di.Greeting;

public class GreetingImpl implements Greeting {

    public String greet() {
        return "Hello World!";
    }
}
GreetingClient.java

Client interface to use the greeting.

package examples.di;

public interface GreetingClient {

    void execute();
}
GreetingClientImpl.java

Client implementation to use greeting. Instead of using Greeting Implementation (GreetingImpl) directly, the interface (Greeting) will be used.

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());
    }
}

Both function provider and user is prepared. Let us try running this.

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();
    }
}

The result will be as follows.

Hello World!

As shown here, the function user (GreetingClientImpl) uses the function through the interface (Greeting), a third party (GreetingMain, in this case) provides the implementation object at runtime. That is the basic style of DI

However, if one writes configurations into the source as GreetingMain, the source code must be altered to reflect any changes. DIContainer is used to avoid this. DIContainer assembles objects by reading external configuration files.

We will write the configuration information into the configuration file. That extension is ".dicon" in S2Container.

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>

Look at S2Container Tag Reference for details. We will first describe the main points.

<component name="greeting"
class="examples.di.impl.GreetingImpl"/>

We define component as above. This is equivalent to the following Java code.

Greeting greeting = new GreetingImpl();

We define a component name using the name property in the component tag, and a class name using the class property. Next, we configure greetingClient.

<component name="greetingClient"
class="examples.di.impl.GreetingClientImpl">
<property name="greeting">greeting</property>
</component>

We define a property name using the name property in the property tag, and specify the component name in the body. This is equivalent to the following Java code. Take care not to surround the component name in double quotation marks, as this will cause the contents to be interpreted as a string.

GreetingClientImpl greetingClient = new GreetingClientImpl();
greetingClient.setGreeting(greeting);

The startup class using S2Container will be as follows.

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 is created using S2ContainerFactor#create(String Path). Read Creating S2Container for details.

The component(greetingClient) is acquired using S2Container#getComponent(String componentName). Please reference Acquiring Components for details.

As before, the result will be as follows.

Hello World!

AOP is often used with DI. AOP is a technique to modularize the functions like logging that are spread over many classes. We will now output a log (trace) from GreetingImpl, and GreetingClientImpl without altering the existing source code. The configuration file with AOP will be as follows.

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 contains commonly used AOP modules predefined within aop.dicon. We use the include tag as follows. Please reference S2Container Definition division and include for details.

<include path="aop.dicon"/>

We define the aspect tag to apply AOP to a component, and specify the AOP module name in the body. We will be using aop.traceInterceptor as the AOP module name.

<aspect>aop.traceInterceptor</aspect>

This concludes the configuration of AOP. We will now try running GreetingMain3. The only difference from GreetingMain2 is the path of the configuration file.

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();
    }
}

The results will be as follows. We see the log output with no alteration to the original source code.

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

We have now mastered the basic use of S2Container.

Next Step

Writing the configuration file is still annoying, isn’t it? S2Container also possesses the following concepts to minimize the amount of writing in configuration files.

This implements a number of practical conventions, so that a program will run with no configuration as long as it follows these rules. For example, the configuration file from earlier explicitly defines the property tag as follows.

<component name="greetingClient"
class="examples.di.impl.GreetingClientImpl">
<property name="greeting">greeting</property>
</component>

As long as the property type is interface and there is a component impementing interface in the container, S2Container has a function to automatically DI. This means S2Container will automatically process components as long as they follow the DI convention of defining property type by interface.

"Convention" may sound annoying, but they are not only recommended but also making development easier.

We can simplify the configuration from above as follows.

<component name="greetingClient"
class="examples.di.impl.GreetingClientImpl">
</component>

Actually, “Convention over Configuration” is used in the AOP example from earlier. Normally, where (or which method) the AOP module is applied is defined in pointcut. All methods defined by interface have the AOP module applied without the use of pointcut in S2AOP as long as they follow the convention of using interface. This is how there was no need to define a module in pointcut in the earlier example.

Using “Convention over Configuration” will simplify the configuration of DI and AOP. However, component registration itself becomes a burden as the number of components increase. The automation of this component registration is Component auto-registration functionality. The registration of GreetingImpl, GreetingClientImpl from earlier is automated as follows.

<component
class="org.seasar.framework.container.autoregister.FileSystemComponentAutoRegister">
<initMethod name="addClassPattern">
<arg>"examples.di.impl"</arg>
<arg>".*Impl"</arg>
</initMethod>
<initMethod name="registAll"/>
</component>

This FileSystemComponentAutoRegister component searches classes defined in addClassPattern from the file system and auto-registers them in S2Container. Please refer to Method Injection for details on the initMethod tag.

The first argument of addClassPattern method is the package name of the component to be auto-registered. Child packages are also recursively searched. The second argument is the class name, which may be a regular expression. Multiple definitions are separated by commas.

Auto-registration of components decreases the overall amount of work, as the programmer is not required to configure anything for new components.

As we automate the registration of components, next focus is the automation of aspect registration. The configuration of Aspect auto-registration for GreetingImpl and GreetingClientImp is as follows.

<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>
<initMethod name="registAll"/>
</component>

We specify the AOP module name in the interceptor property. We will skip the explanation of the addClassPattern method, as it is identical to the component auto-registration. The combination of component and aspect auto-registration looks like the following.

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>
<initMethod name="registAll"/>
</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>
<initMethod name="registAll"/>
</component>
</components>

We will now run GreetingMain4. We must invoke S2Container#init() and S2Container#destroy() in the case of using auto-registration.

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();
        }
    }
}

The result will be identical to GreetingMain3 as follows.

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

Auto-registration and Auto-binding will work well in most cases. Components can be excluded from auto-registration using the addIgnoreClassPattern method.

Binding Annotation can be used to fine tune configuration settings without using configuration files for properties excluded from auto-binding.

We have now mastered advanced use of S2Container. Please reference the manual as needed.

S2Containerリファレンス

作成すべきファイル

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

S2Containerの定義

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の生成

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

  • SingletonS2ContainerFactoryを使用する。
  • S2ContainerFactoryを使用する。

SingletonS2ContainerFactoryを使用する

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を使用する

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のタイプ

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の定義ファイルには、次の内容を記述します。

  • コンポーネントの指定
    コンポーネントの指定は、コンストラクタ・インジェクションと同様です。
    name属性でコンポーネントに名前を付けることもできます。
  • プロパティの指定
    コンポーネントのプロパティは、componentタグの子タグであるpropertyタグを使って指定します。
    name属性でプロパティ名を指定します。
<components>
    <component name="..." class="...">
          <property name="...">...</property>
    </component>
</components>

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

メソッド・インジェクションとは、任意のメソッドを呼び出して、DIを行います。
S2Containerの定義ファイルには、次の内容を記述します。

  • コンポーネントの指定
    コンポーネントの指定は、コンストラクタ・インジェクションと同様です。
    name属性でコンポーネントに名前を付けることもできます。
  • 初期化メソッドの指定
    initMethodタグを使って、コンポーネントの任意のメソッドを呼び出します。name属性で、メソッド名を指定します。引数は、argタグを子タグに使います。name属性を省略して、ボディで、OGNL式を使うこともできます。その際、コンポーネント自身は#selfで表します。
<components>
    <component name="..." class="...">
          <initMethod>...</initMethod>
    </component>
</components>

S2Container定義の分割とインクルード

すべてのコンポーネントを1つのファイルに記述すると、直ぐに肥大化してしまい管理が難しくなります。そのため、コンポーネントの定義を複数に分割する機能と分割された定義をインクルードして1つにまとめる機能がS2Containerにあります。S2Container定義ファイルのインクルードは次のようにして行います。

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

includeタグのpath属性で取り込みたいS2Container定義ファイルのパスを指定します。詳しくは、includeタグを参照してください。
コンポーネントの検索順は、先ず自分自身に登録されているコンポーネントを探して、見つからない場合は、includeされている順に子供のS2Containerに登録されているコンポーネントを検索し、最初に見つかったコンポーネントが返されます。
次のような場合は、Foo(自身のコンポーネント)→aaa(子供のS2Container)→bbb(子供のS2Container)の順に検索します。

<components>
    <include path="aaa.dicon"/>
    <include path="bbb.dicon"/>
    <component class="example.container.Foo" />
</components>

自動でコンストラクタ・インジェクションセッター・インジェクションを行う場合、S2Containerはインクルード先のコンポーネントを自動インジェクションすることができます。自動でDependency Injectionを行う場合の条件は自動バインディングを参照してください。
次のようにセッター・インジェクションでプロパティに指定するコンポーネントがインクルード先のaaa.diconとbbb.diconに登録されている場合、各HelloClientでは、どちらのコンポーネントが使用されるかをみてましょう。

root.dicon
<components>
    <include path="examples/dicon/include/aaa.dicon"/>
    <include path="examples/dicon/include/bbb.dicon"/>
    <component name="root" class="examples.dicon.include.RootHelloClient"/>
</components>
aaa.dicon
<components>
    <component class="examples.dicon.include.HelloImpl">
        <property name="Message">"Hello Aaa!"</property>
    </component>

    <component name="aaa" class="examples.dicon.include.AaaHelloClient"/>
</components>
bbb.dicon
<components>
    <component class="examples.dicon.include.HelloImpl">
        <property name="Message">"Hello Bbb!"</property>
    </component>

    <component name="bbb" class="examples.dicon.include.BbbHelloClient"/>
</components>

各コンポーネントの内容は、次のようになります。

package examples.dicon.include;

public interface HelloClient {
    public void showMessage();
}
package examples.dicon.include;

public class RootHelloClient implements HelloClient {

    private Hello hello_;

    public void setHello(Hello hello) {
        hello_ = hello;
    }

    public Hello getHello() {
        return hello_;
    }

    public void showMessage() {
        System.out.println(getHello().getMessage());
    }
}

AaaHelloClientとBbbHelloClientはRootHelloClient同様の実装です。

package examples.dicon.include;

public interface Hello {
    public void setMessage(String helloMessage);
    public String getMessage();
}
package examples.dicon.include;

public class HelloImpl implements Hello {
    private String helloMessage_;

    public void setMessage(String helloMessage) {
        helloMessage_ = helloMessage;
    }

    public String getMessage() {
        return helloMessage_;
    }
}

HelloImplはMessageプロパティを定義しているだけです。各HelloClientのshowMessage()を呼び出した場合の実行結果は次のようになります。

RootHelloClientの実行結果
Hello Aaa!

まず、S2Containerはroot.diconにHelloImplが登録されているかを検索します。root.diconにはないので、次にインクルード先のaaa.diconを検索します。aaa.diconにはHelloImplが登録されているので、そのコンポーネントを使用します。

AaaHelloClientの実行結果
Hello Aaa!

AaaHelloClientは、aaa.diconに登録されているコンポーネントを使用します。自動でインジェクションを行う場合、子供のS2Containerは親のS2Containerのコンポーネントを使用することはできません。例えば、root.diconにHelloImplを登録していてもAaaHelloClientには自動インジェクションされないということです。

BbbHelloClientの実行結果
Hello Bbb!

AaaHelloClientと同様にBbbHelloClientもbbb.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>

同一のコンポーネント定義内では、名前空間なしで参照できます。他のS2Container定義のコンポーネントを参照する場合は、名前空間.をコンポーネント名の頭につけます。foo.aaaとbar.aaaは同じ名前がついていますが、名前空間が異なっているので、違うコンポーネントとして認識されます。慣習として、定義ファイルの名前は、名前空間.diconにすることを推奨します。

インスタンス管理

S2Containerで、コンポーネントのインスタンスをどのように管理するのかを指定するのが、componentタグのinstance属性です。デフォルトはsingletonで、これは、S2Container.getComponent()によって返されるコンポーネントは常に同じだという意味です。S2Container.getComponent()を呼び出すたびに、新たに作成されたコンポーネントを返して欲しい場合は、instance属性にprototypeを指定します。リクエスト(HttpServletRequest)ごとにコンポーネントを管理したい場合は、instance属性にrequestを指定します。セッション(HttpSession)ごとにコンポーネントを管理したい場合は、instance属性にsessionを指定します。

プレゼンテーションのフレームワークと組み合わせるときに、プレゼンテーションフレームワークが作成したインスタンスに対して、S2Containerで管理されているコンポーネントをセットしたい場合があります。そのようなS2Container外のコンポーネントに対してDependency Injectionしたいときには、次のメソッドを使用します。

- org.seasar.framework.container.S2Container#injectDependency(Object outerComponent)
- org.seasar.framework.container.S2Container#injectDependency(Object outerComponent, Class componentClass)
- org.seasar.framework.container.S2Container#injectDependency(Object outerComponent, String componentName)

第一引数には、外部のコンポーネントを指定します。第二引数には、外部コンポーネントのクラス、またはコンポーネント名を指定します。
そのとき、S2Container定義では、instance属性にouterを指定します。
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 コンストラクタ、プロパティの手動バインディングのみ。

コンストラクタの自動バインディングのルールは次のようになります。

  • コンストラクタの引数が明示的に指定されている場合は、それに従い、自動バインディングは適用されません。
  • 上記に該当しない場合、引数のないデフォルトコンストラクタが定義されているなら、そのコンストラクタを使い、自動バインディングは適用されません。
  • 上記に該当しない場合、コンストラクタの引数の数が1以上で、 引数の型がすべてインターフェースのコンストラクタで最も引数の数が多いコンストラクタを使います。そして、 引数の型を実装したコンポーネントをコンテナから取得して適用します。
  • 上記に該当しない場合、自動バインディングは適用されません。

プロパティの自動バインディングのルールは次のようになります。

  • プロパティが明示的に指定されている場合は、それに従い、自動バインディングは適用されません。
  • 上記に該当しない場合、プロパティ名と同一のコンポーネントがコンテナに登録されてプロパティに代入可能なら、 そのコンポーネントが適用されます。
  • 上記に該当しない場合、プロパティの型がインターフェースでプロパティの型を実装したコンポーネントがコンテナに登録されていれば、 そのコンポーネントが適用されます。
  • 上記に該当しない場合、自動バインディングは適用されません。

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

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

コンポーネントでS2Containerを利用する

コンポーネントは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>

S2ContainerServlet

これまでは、Javaアプリケーションで、明示的にS2Containerを作成していましたが、Webアプリケーションの場合、誰がS2Containerを作成するのでしょうか。その目的のために次のクラスが用意されています。

- org.seasar.framework.container.servlet#S2ContainerServlet

S2ContainerServletを使うためには、web.xmlに次の項目を記述します。
src/org/seasar/framework/container/servlet/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という名前のコンポーネントとしてアクセスできるようになります。

app.diconの役割

すべてのS2Container定義のルートになる定義ファイルは、慣例でapp.diconという名前にします。app.diconにはコンポーネントの定義はしないようにしてください。通常はWEB-INF/classesにおくと良いでしょう。

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

AOPの適用

コンポーネントに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>

components、component、arg、propertyタグに指定したメタデータの情報は、S2Container、ComponentDef、ArgDef、PropertyDefで定義されている次のメソッドで取得することが出来ます。

  • public int getMetaDefSize()
  • public MetaDef getMetaDef(int index)
  • public MetaDef getMetaDef(String name)
  • public MetaDef[] getMetaDefs(String name)

リクエストの自動バインディング

コンポーネントに対して、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の設定はほぼ自動化できます。 アノテーションを使って細かく制御することも可能です。 さらに、コンポーネントの登録も自動化してしまおうというのが、コンポーネントの自動登録機能です。 コンポーネントの自動登録には、ファイルシステム上のclassファイルを検索するFileSystemComponentAutoRegisterコンポーネントやjarファイルの中を検索するJarComponentAutoRegisterを利用します。

org.seasar.framework.container.autoregister.FileSystemComponentAutoRegister
プロパティ 説明
fileNameOfRoot このプロパティで指定されたファイルが存在するディレクトリをクラスパスのルート(例えば、WEB-INF/classes)とみなします。デフォルトは、j2ee.diconになります。
instanceDef 自動登録されるコンポーネントに適用するInstanceDefを指定します。XMLで指定する場合、
@org.seasar.framework.container.deployer.InstanceDefFactory@REQUEST
のように指定します。
autoNaming クラス名からコンポーネント名を自動的に決定するコンポーネント。org.seasar.framework.container.autoregister.AutoNamingインターフェースを実装している必要があります。デフォルトは、org.seasar.framework.container.autoregister.DefaultAutoNamingクラスのインスタンスになります。

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

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

メソッド 説明
addClassPattern 自動登録したいクラスパターンを登録します。最初の引数は、コンポーネントのパッケージ名です。子供のパッケージも再帰的に検索します。2番目の引数は、クラス名です。正規表現が使えます。「,」区切りで複数記述することもできます。
addIgnoreClassPattern 自動登録したくないクラスパターンを登録します。最初の引数は、コンポーネントのパッケージ名です。子供のパッケージも再帰的に検索します。2番目の引数は、クラス名です。正規表現が使えます。「,」区切りで複数記述することもできます。
org.seasar.framework.container.autoregister.ComponentAutoRegister
プロパティ 説明
instanceDef 自動登録されるコンポーネントに適用するInstanceDefを指定します。XMLで指定する場合、@org.seasar.framework.container.deployer.InstanceDefFactory@REQUESTのように指定します。
autoNaming クラス名からコンポーネント名を自動的に決定するコンポーネント。org.seasar.framework.container.autoregister.AutoNamingインターフェースを実装している必要があります。デフォルトは、org.seasar.framework.container.autoregister.DefaultAutoNamingクラスのインスタンスになります。

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

コンポーネントの名前はAutoNamingにより制御します.クラス名をコンポーネント名とするDefaultAutoNamingと、パッケージで修飾された名前をコンポーネント名とするAualifiedAutoNamingがあります.

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>
<initMethod name="registAll"/>
</component>
<component class="org.seasar.framework.container.autoregister.JarComponentAutoRegister">
<property name="classInJar">
@junit.framework.TestSuite@class
</property>
<property name="jarFileNames">"junit.*"</property>
<initMethod name="addClassPattern">
<arg>"junit.framework"</arg>
<arg>"TestSuite"</arg>
</initMethod>
<initMethod name="registAll"></initMethod>
</component>
<component class="org.seasar.framework.container.autoregister.ComponentAutoRegister">
<property name="referenceClass">
@aaa.bbb.ccc.ddd.HogeImpl@class
</property>

<initMethod name="addClassPattern">
<arg>"aaa.bbb"</arg>
<arg>".*Impl"</arg>
</initMethod>
<initMethod name="registAll"></initMethod>
</component>

アスペクトの自動登録

コンポーネントの自動登録により、コンポーネントの登録は自動化できます。 さらに、アスペクトの登録も自動化してしまおうというのが、アスペクトの自動登録機能です。アスペクトの自動登録には、AspectAutoRegisterコンポーネントを利用します。

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

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

S2Containerタグリファレンス

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でも/です。componentタグの前に記述する必要があります。

<?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タグ(任意)

アスペクトをコンポーネントに組み込みます。詳しくは、S2AOPのaspectタグの説明を参照してください。

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アノテーションリファレンス

S2Containerでは、アノテーションの実装方法として、 Tigerアノテーション、backport175アノテーション、定数アノテーションの3種類を用意しています。一般的にアノテーションといえば、Java 5から導入された Tigerアノテーションですが、それだけだと、JDK1.4のユーザが利用できなくなってしまいます。

できるだけ、多くの方にアノテーションを便利に使ってもらいたいため、JDK1.4で、 Tiger風アノテーション(JSR-175)を使うことのできる backport175アノテーション、public static finalな定数を利用する定数アノテーションを用意しています。

Componentアノテーション

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";

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) {
    ...
}

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(String aaa) {
    ...
}

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

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

Aspectアノテーション

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 =
    "interceptor=aop.traceInterceptor, pointcut=getAaa";