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.
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 constructs 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 constructs 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.
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> </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> </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> </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>
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.
Hotswap can be used to retry immediately without restarting application
so that development is more efficient.
We have now mastered advanced use of S2Container. Please reference the manual as needed.
A definition file must be created in order to use S2Container.
A definition file is a XML file with a .dicon extension,
and serves as a blueprint for component creation.
The definition of S2Container is as follows.
<?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 may not be abbreviated.
The above sample should be copied verbatim when creating a dicon file.
The root is the components tag.
We will define the component tag for every component tag.
Please refer to the S2Container Definition Tag Reference for additional details.
<components>
<component name="hoge" class="examples.dicon.HogeImpl"/>
</components>
There are two methods of creating S2Container.
- Use SingletonS2ContainerFactory
- Use S2ContainerFactory
We will use the following method when utilizing SingletonS2ContainerFactory.
- org.seasar.framework.container.factory.SingletonS2ContainerFactory#init()
app.dicon located in the directory as defined in CLASSPATH is used.
The created S2Container can be obtained by the following method from any location.
- org.seasar.framework.container.factory.SingletonS2ContainerFactory#getContainer()
SingletonS2ContainerFactory.init();
...
S2Container container = SingletonS2ContainerFactory.getContainer();
Use the following method before calling init() if specifying a path to the definition file.
- org.seasar.framework.container.factory.SingletonS2ContainerFactory#setConfigPath(String Path)
The arguments path is an absolute path of the definition file referencing the directory defined in CLASSPATH as root.
For example, WEB-INF/classes/aaa.dicon is aaa.dicon. WEB-INF/classes/aaa/bbb/ccc.dicon becomes aaa/bbb/ccc.dicon.
The separator is “/” in both Windows and UNIX systems.
private static final String PATH = "aaa/bbb/ccc.dicon";
...
SingletonS2ContainerFactory.setConfigPath(PATH);
SingletonS2ContainerFactory.init();
...
S2Container container = SingletonS2ContainerFactory.getContainer();
We will use the following method when utilizing S2ContainerFactory.
- org.seasar.framework.container.factory.S2ContainerFactory#create(String path)
We will call the following method after creating S2Container.
- org.seasar.framework.container.S2Container#init()
private static final String PATH = "aaa/bbb/ccc.dicon";
...
S2Container container = S2ContainerFactory.create(PATH);
container.init();
An instance of the container obtained using this method must be managed by an application.
To extract a component from S2Container, we use the following method.
- org.seasar.framework.container.S2Container#getComponent(Object componentKey)
A component name or component class can be specified as an argument.
Please refer to component tag for details.
When specifying a component class, the class must return true for the component class instanceof class.
If there are multiple components that implement the specified class,
S2Container will fail to identify the target component and throw TooManyRegistrationRuntimeException.
Please specify a class that will uniquely identify an implemented component, or acquire using the component name.
If there are multiple components with identical names registered, S2Container will throw TooManyRegistrationRuntimeException as well.
Example: When acquiring a component by class
S2Container container = S2ContainerFactory.create(PATH);
Hoge hoge = (Hoge) container.getComponent(Hoge.class);
Example: When acquiring a component by name
S2Container container = S2ContainerFactory.create(PATH);
Hoge hoge = (Hoge) container.getComponent("hoge");
Dependency Injection has three different types, Constructor Injection, Setter Injection, and Method Injection.
Constructor Injection configures by the constructor,
Setter Injection configures by the setter method,
and Method Injection is configured by the initialization method.
Method injection is specific to S2Container.
S2Container supports all types and its hybrids.
Constructor Injection applies DI to the constructor arguments.
The following contents are written into the S2Container definition file.
- Specifying component
The component is constructed using the component tag. The class name is specified using the class property.
The name property can be used to give the component a name.
- Specifying constructor argument
The arguments of the constructor are specified in the arg sub-tag of the component tag.
Strings are to be surrounded by double quotation marks (").
<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>
すべてのコンポーネントを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自身も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に次の項目を記述します。 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という名前のコンポーネントとしてアクセスできるようになります。
すべてのS2Container定義のルートになる定義ファイルは、慣例でapp.diconという名前にします。app.diconにはコンポーネントの定義はしないようにしてください。通常はWEB-INF/classesにおくと良いでしょう。
<components>
<include path="examples/foo.dicon"/>
<include path="examples/bar.dicon"/>
...
</components>
コンポーネントに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を利用します。
プロパティ |
説明 |
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番目の引数は、クラス名です。正規表現が使えます。「,」区切りで複数記述することもできます。 |
プロパティ |
説明 |
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番目の引数は、クラス名です。正規表現が使えます。「,」区切りで複数記述することもできます。 |
プロパティ |
説明 |
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番目の引数は、クラス名です。正規表現が使えます。「,」区切りで複数記述することもできます。 |
プロパティ |
説明 |
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と、パッケージで修飾された名前をコンポーネント名とするQualifiedAutoNamingがあります.
クラスの完全修飾名からパッケージ部分を除き、最後が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>
コンポーネントの自動登録により、コンポーネントの登録は自動化できます。
さらに、アスペクトの登録も自動化してしまおうというのが、アスペクトの自動登録機能です。アスペクトの自動登録には、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> </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でも/です。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>
コンポーネントを定義します。
クラスの完全限定名を指定します。ボディで、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(String aaa) {
...
}
public void setBbb(String bbb) {
...
}
public void setCcc(String 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 =
"interceptor=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";
|