Seasar DI Container with AOP  

Quickstart

Seasar2 is a light weight container for Dependency Injection (DI) operations.

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 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 the 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 are 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 S2ContainerFactory#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>
</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.

S2Container Reference

Files to be Created

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.

S2Container Definition

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>

S2Container Creation

There are two methods of creating S2Container.

  • Use SingletonS2ContainerFactory
  • Use S2ContainerFactory

Using SingletonS2ContainerFactory

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

Using S2ContainerFactory

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.

Acquiring Components

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 Type

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

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>

Setter Injection

Setter Injection applies DI using the setter method on specified properties.
The following contents are written into the S2Container definition file.

  • Specifying component
    Specifying component is identical to constructor injection.
  • Specifying property
    The component properties are specified using the property sub-tag of the component tag. The name property is used to specify a property name.
<components>
    <component name="..." class="...">
          <property name="...">...</property>
    </component>
</components>

Method Injection

Method injection applies DI by calling a specified method. The following contents are written into the S2Container definition file.

  • Specifying component
    Specifying component is identical to constructor injection.
  • Specifing initialization method
    The initMethod tag is used to call the specified method of the component. The name property is used to specify a method name. The arg sub-tag is used to define arguments. The name property can be abbreviated, and OGNL expression used in the body.
<components>
    <component name="..." class="...">
        <initMethod name="...">
            <arg>...</arg>
        </initMethod>
    </component>
</components>

S2Container Definition division and include

Listing all components in one file quickly lead to an immense and unmanageable system. To avoid this, S2Container has functionality to split the definitions into several pieces and recombine those pieces. The inclusion of S2Container definition files are done as follows.

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

Specify the S2Container definition file to be included using the path property of the include tag. Please refer to include tag for additional details.

The search order for components start with those components registered with itself. When no matches are found, the process searches in each child S2Container component registration list, returning the first component found.

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

Namespace

Namespace of components can be specified using the namespace property of the components tag. This can be used to prevent component name collisions (overlap) between the multiple divided component definitions.

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>

Within a single component definition, items may be referenced without a namespace. "namespace." is prefixed on the component name when referencing an external namespace. "foo.aaa" and "bar.aaa" shares an identical component name, aaa, but are recognized as unique components as they inhabit different namespaces, foo and bar.

Instance Management

The instance property of the component tag is used to specify the method of component instance management.

instance Description
singleton(default) Every call of S2Container.getComponent() will return the same instance.
prototype Every call of S2Container.getComponent() will return a new instance.
request A new instance is created for each request. The component is stored in the request as named using the name property. S2ContainerFilter must be configured to use request.
session A new instance is created for each session. The component is stored in the session as named using the name property. S2ContainerFilter must be configured to use session.
application In case we use servlet, a new instance is created for each ServletContext. The component is stored in the ServletContext as named using the name property. S2ContainerFilter must be configured to use application.
outer The component instance is created external to S2Container, and only Dependency Injection is applied. Aspect and Constructor Injection cannot be used.

Lifecycle

initMethod tag and destroyMethod tag can be used to manage component lifecycles in S2Container. The method specified in the initMethod tag is called at the initialization of S2Container, or S2Container.init(). Conversely, S2Container will call the method specified in the destroyMethod tag at shutdown of S2Container, or S2Container.destroy(). initMethod is run in order of component registration in the container, and destroyMethod makes calls in reverse order. destroyMethod is ignored by any method whose instance property is not set to singleton. java.util.HashMap#put() method looks like the following when configured for initialization (aaa set as 111), or shutdown procedure (aaa set as 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>

Automatic Binding

Inter-component dependencies are automatically resolved by the container when type is interface. This is default behavior of S2Container, but can be more finely controlled using the autoBinding property of the component tag.

autoBinding Description
auto(default) Automatic binding of constructor and property is applied.
constructor Automatic binding of constructor is applied.
property Automatic binding of property is applied.
none Manual binding of constructor and property only.

Constructor auto-binding rules are as follows.

  • When constructor arguments are explicitly specified, auto-binding is not applied.
  • When above conditions are not true and a default constructor is specified with no arguments, that constructor is used with no auto-binding.
  • When above conditions are not met, the constructor with the largest number of 'interface' type arguments is used. In addition, components with implemented argument type are acquired from the container and applied.
  • When above conditions are not met, auto-binding is not applied.

Property auto-binding rules are as follows.

  • When property is explicitly selected, auto-binding is not applied.
  • When above conditions are not met, if a component with an identical name to the property name is registered in container, this component will be used.
  • When above conditions are not met, if a component that has implemented 'interface' type property is registered in container, this component will be used.
  • When above conditions are not met, auto-binding is not applied.

The bindingType property of the property tag can be used to more finely control each property.

bindingType Description
must An exception is throwed when auto-binding is not applied.
should(default) A warning is returned when auto-binding is not applied.
may No response is returned when auto-binding is not applied.
none Auto-binding is not applied even if the autoBinding property is set to auto or property.

Using S2Container in Components.

It is preferable that components are not dependent on S2Container. However, there are certain components that would want to call the S2Container method. S2Container itself is registered by the name of container. Using this, an instance of the container can be acquired by specifying container in the body of arg and property tags. It can also be configured using auto-binding by defining the setter method of the S2Container type. Specifying container using arg and property tags looks like the following.

<components>
    <component class="examples.dicon.BarImpl">
        <arg>container</arg>
    </component>

    <component class="examples.dicon.FooImpl">
        <property name="foo">container</property>
    </component>
</components>

S2ContainerServlet

We have explicitly created S2Container using Java applications so far. But in the case of web applications, who will create S2Container? The following class is prepared for this reason.

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

The following is entered in web.xml in order to use S2ContainerServlet.

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

The path to the root S2Container definition is selected in configPath. The definition file is placed in WEB-INF/classes. Configure load-on-startup tag to make sure S2ContainerServlet is initialized first. Once S2ContainerServlet is initialized, an instance of S2Container is acquired using the following method.

  • org.seasar.framework.container.factory.SingletonS2ContainerFactory#getContainer()

The lifecycle of S2Container is linked with S2ContainerServlet. When the debug parameter is set to true, S2Container may be restarted while operating by using the following command. In this example, xxx is the web application's context name.

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

ServletContext can be accessed as a component named servletContext when using S2ContainerServlet.

Purpose of app.dicon

Name the root S2Container definition file "app.dicon" as convention. It is recommended that the file be stored in WEB-INF/classes.

Use of AOP

We can apply AOP to components. For example, we do the following if we want to apply TraceInterceptor to ArrayList.

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

We specify the name of Interceptor in the body of the aspect tag. We can specify target method names in the pointcut property. Multiple entries can be entered separated by commas. When pointcut is not specified, all interfaces implemented methods in the component are targeted. A regular expression(JDK1.4 regex) can be used for method name. A sample use of this definition is as follows.

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

The results will be the following.

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

We can apply InterType to a component. For example, When we want to apply PropertyInterType to Hoge, we define as follows:

<components>
    <include path="aop.dicon"/>
    <component class="examples.Hoge">
        <interType>aop.propertyInterType</aspect>
    </component>
</components>

We can specify InterType in the interType tag body.

Metadata

Metadata can be defined in the components, component, arg, and property tags. The meta tag is used as a child tag in the parent tag where the metadata is to be set. Adding metadata to a components tag will look like the following example.

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

Auto-binding of Requests

HttpServletRequest can be automatically bound to a component. For this, setRequest (HttpServletRequest request) method is defined within the component. S2Container will then automatically configure a request. Also, the following Filter must be defined in 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>

In this manner, HttpServletResponse, HttpSession and ServletContext can be automatically bound by defining the setter method. Also, HttpServletRequest, HttpServletResponse, HttpSession and ServletContext can be accessed as components respectively named request, response, session and application by using S2ContainerFilter.

Component Auto-registration

DI configuration can be mostly automated using autobinding. Fine control can be exerted using annotation. Taking this a step further is component auto-registration.

org.seasar.framework.container.autoregister.FileSystemComponentAutoRegister

When we use FileSystemComponentAutoRegister, components will be searched in the file system, and will be automatically registered.

property Description
instanceDef A sample use of XML is as follows.
@org.seasar.framework.container.deployer.InstanceDefFactory@REQUEST
autoBindingDef A sample use of XML is as follows.
@org.seasar.framework.container.assembler.AutoBindingDefFactory@NONE
autoNaming Component to automatically name components from class name. Requires implementation of org.seasar.framework.container.autoregister.AutoNaming interface.

method Description
addClassPattern A class pattern to be auto-registered is registered. The first argument is the component package name. Child packages are searched recursively. The second argument is class name. One may use regular expressions, and multiple entries can be separated by commas.
addIgnoreClassPattern Class patterns to be ignored by auto-registration are registered. The first argument is the component package name. Child packages are searched recursively. The second argument is class name. One may use regular expressions, and multiple entries can be separated by commas.
org.seasar.framework.container.autoregister.JarComponentAutoRegister

When we use JarComponentAutoRegister, components will be searched in the jar file, and will be automatically registered.

property Description
jarFileNames Target jar file names excluded extension are registered. One may use regular expressions, and multiple entries can be separated by commas.
referenceClass Class in jar is entered. Default value is @org.aopalliance.intercept.MethodInterceptor@class.
instanceDef A sample use of XML is as follows.
@org.seasar.framework.container.deployer.InstanceDefFactory@REQUEST
autoBindingDef A sample use of XML is as follows.
@org.seasar.framework.container.assembler.AutoBindingDefFactory@NONE
autoNaming Component to automatically name components from class name. Requires implementation of org.seasar.framework.container.autoregister.AutoNaming interface.

method Description
addClassPattern A class pattern to be auto-registered is registered. The first argument is the component package name. Child packages are searched recursively. The second argument is class name. One may use regular expressions, and multiple entries can be separated by commas.
addIgnoreClassPattern Class patterns to be ignored by auto-registration are registered. The first argument is the component package name. Child packages are searched recursively. The second argument is class name. One may use regular expressions, and multiple entries can be separated by commas.
org.seasar.framework.container.autoregister.ComponentAutoRegister

When we use ComponentAutoRegister, components will be searched in the file system or the jar file, and will be automatically registered.

property Description
instanceDef A sample use of XML is as follows.
@org.seasar.framework.container.deployer.InstanceDefFactory@REQUEST
autoBindingDef A sample use of XML is as follows.
@org.seasar.framework.container.assembler.AutoBindingDefFactory@NONE
autoNaming Component to automatically name components from class name. Requires implementation of org.seasar.framework.container.autoregister.AutoNaming interface.

method Description
addReferenceClass Classes in jars are specified.
addClassPattern A class pattern to be auto-registered is registered. The first argument is the component package name. Child packages are searched recursively. The second argument is class name. One may use regular expressions, and multiple entries can be separated by commas.
addIgnoreClassPattern Class patterns to be ignored by auto-registration are registered. The first argument is the component package name. Child packages are searched recursively. The second argument is class name. One may use regular expressions, and multiple entries can be separated by commas.

AutoNaming automatially names components.

org.seasar.framework.container.autoregister.DefaultAutoNaming
property Description
decapitalize Set to true in order to decapitalize component name. Default value is true.

method Deccription
setCustomizedName Class to be not applied to default rule is registered. The first argument is the class FQDN. The second argument is the component name.
addIgnoreClassSuffix Define the part removed from the tail of the class name. "Impl" and "Bean" are already registered.
addReplaceRule Add the replacement rule by regular expressions. The first argument is the regular expressions. The second argument is the replacement value.
clearReplaceRule The replacement rules that are added by setCustomizedName, addIgnoreClassSuffix and addReplaceRuleare method are cleared.
org.seasar.framework.container.autoregister.QualifiedAutoNaming
property Description
decapitalize Set to true in order to decapitalize component name. Default value is true.

method Description
setCustomizedName Class to be not applied to default rule is registered. The first argument is the class FQDN. The second argument is the component name.
addIgnorePackagePrefix Define the part removed from the head of the package name.
addIgnoreClassSuffix Define the part removed from the tail of the class name. "Impl" and "Bean" are already registered.
addReplaceRule Add the replacement rule by regular expressions. The first argument is the regular expressions. The second argument is the replacement value.
clearReplaceRule The replacement rules that are added by setCustomizedName, addIgnoreClassSuffix and addReplaceRuleare method are cleared.
Example
<component
  class="org.seasar.framework.container.autoregister.FileSystemComponentAutoRegister">
    <property name="autoNaming">
        <component class="org.seasar.framework.container.autoregister.DefaultAutoNaming">
            <initMethod name="setCustomizedName">
                <arg>"examples.di.impl.HogeImpl"</arg>
                <arg>"hoge2"</arg>
            </initMethod>
        </component>
    </property>
    <initMethod name="addClassPattern">
        <arg>"examples.di.impl"</arg>
        <arg>".*Impl"</arg>
    </initMethod>
</component>
<component class="org.seasar.framework.container.autoregister.JarComponentAutoRegister">
    <property name="referenceClass">
        @junit.framework.TestSuite@class
    </property>
    <property name="jarFileNames">"junit.*"</property>
    <initMethod name="addClassPattern">
        <arg>"junit.framework"</arg>
        <arg>"TestSuite"</arg>
    </initMethod>
</component>
<component class="org.seasar.framework.container.autoregister.ComponentAutoRegister">
    <initMethod name="addReferenceClass">
        <arg>@aaa.bbb.ccc.ddd.HogeImpl@class</arg>
    </initMethod>
    <initMethod name="addClassPattern">
        <arg>"aaa.bbb"</arg>
        <arg>".*Impl"</arg>
    </initMethod>
</component>

Aspect auto-registration

Component registration is automated by component auto-registration. Taking this a step further is the automation of aspect auto-registration.

When we combine aspect auto-registration and component auto-registration, we need to write the aspect auto-registration configuration after component auto-registration configuration. The components manually registered are to be written after aspect auto-registeration configuration.

<components>
    <!-- 1.component auto-registration -->
    <component class="org.seasar.framework.container.autoregister.ComponentAutoRegister">
        ...
    </component>

    <!-- 2.aspect auto-registration -->
    <component class="org.seasar.framework.container.autoregister.AspectAutoRegister">
        ...
    </component>

    <!-- 3.component manually-registration -->
    <component class="...">
        ...
    </component>
    ...
<components>
org.seasar.framework.container.autoregister.AspectAutoRegister
property Description
interceptor Specify interceptor. Use InterceptorChain to specify multiple interceptors.
pointcut Specify methods to be targeted by interceptor, separated by commas. When pointcut is not specified, all interfaces implemented methods in component are targeted. Regular expressions (JDK1.4 regex) may be used for method names.
method Description
addClassPattern A class pattern to be auto-registered is registered. The first argument is the component package name. Child packages are searched recursively. The second argument is class name. One may use regular expressions, and multiple entries can be separated by commas.
addIgnoreClassPattern Class patterns to be ignored by auto-registration are registered. The first argument is the component package name. Child packages are searched recursively. The second argument is class name. One may use regular expressions, and multiple entries can be separated by commas.
Example
<include path="aop.dicon"/>
...
<component
class="org.seasar.framework.container.autoregister.AspectAutoRegister">
<property name="interceptor">aop.traceInterceptor</property>
<initMethod name="addClassPattern">
<arg>"examples.di.impl"</arg>
<arg>".*Impl"</arg>
</initMethod>
</component>
org.seasar.framework.container.autoregister.InterfaceAspectAutoRegister
property Description
interceptor Specify interceptor. Use InterceptorChain to specify multiple interceptors.
targetInterface The components implementing target interface are applied above interceptor.
Example
<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>

Meta auto-registration

We can automatically register meta data.

When we combine meta auto-registration and component auto-registration, we need to write the meta auto-registration configuration after component auto-registration configuration. The components manually registered are to be written after meta auto-registeration configuration.

<components>
    <!-- 1.component auto-registration -->
    <component class="org.seasar.framework.container.autoregister.ComponentAutoRegister">
        ...
    </component>

    <!-- 2.meta auto-registration -->
    <component class="org.seasar.framework.container.autoregister.MetaAutoRegister">
        ...
    </component>

    <!-- 3.component manually-registration -->
    <component class="...">
        ...
    </component>
    ...
<components>
org.seasar.framework.container.autoregister.MetaAutoRegister

MetaAutoRegister is a component in order to automatically register meta data in class name patterns.
We write the meta data automatially registered to child meta data named "autoRegister".

method Description
addClassPattern A class pattern to be auto-registered is registered. The first argument is the component package name. Child packages are searched recursively. The second argument is class name. One may use regular expressions, and multiple entries can be separated by commas.
addIgnoreClassPattern Class patterns to be ignored by auto-registration are registered. The first argument is the component package name. Child packages are searched recursively. The second argument is class name. One may use regular expressions, and multiple entries can be separated by commas.
Example
<component
  class="org.seasar.framework.container.autoregister.MetaAutoRegister">
    <meta name="autoRegister">
        <meta name="hoge"</meta>
    </meta>
    <initMethod name="addClassPattern">
        <arg>"examples.di.impl"</arg>
        <arg>".*Impl"</arg>
    </initMethod>
</component>

In this sample, we can automatically register the meta data named "hoge".

Hotswap

Seasar2 realizes Hotswap, then let's try.

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>

Change hotswap mode using "s2container.dicon".

s2container.dicon

<components>
<component
class="org.seasar.framework.container.factory.S2ContainerFactory$DefaultProvider">
<property name="hotswapMode">true</property>
</component>
</components>

"s2container.dicon" is placed in root directory of CLASSPATH.

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

The results will be as follows.

hotswapMode:true
Hello
Let's modify GreetingImpl, then press ENTER.

after modify
Hello2

We need to use inerface in order to hotswap.

S2Container tag reference

DOCTYPE

DOCTYPE is specified after the XML declaration. Please specify as shown below.

<?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 tag (required)

Becomes the root tag.

namespace property (optional)

Specifies a namespace.

<?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 tag (optional)

Used to consolidate separated S2Container definitions.

path property (required)

Specifies the definition file path. It is an absolute path referencing the directory specified in CLASSPATH as root. For example, WEB-INF/classes/aaa.dicon becomes aaa.dicon. WEB-INF/classes/aaa/bbb/ccc.dicon becomes aaa/bbb/ccc.dicon. The separator is '/' in both Windows and UNIX.

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

component tag (optional)

Defines components.

class property (optional)

Specifies a FQDN of a class. The class property can be omitted if components are selected using OGNL expression in the body.

name property (optional)

Specifies a name, usable as a java identifier. Refer to acquiring components for details.

instance property (optional)

Specifies how S2Container manages instances of components. Options are singleton (default), prototype, outer, request, and session. Refer to Instance Management for details.

autobinding property (optional)

Specifies how S2Container will resolve dependencies. Options are auto (default), constructor, property, and none. Refer to Automatic Binding for details.

arg tag (optional)

Becomes a constructor argument when used as a child tag of the component tag. It is passed to the constructor in the order described. Becomes a method argument when used as a child initMethod tag and destroyMethod tag. It is passed to the method in the order described. Either OGNL expressions in the body or the child component tag is used for the value passed as an argument.

property tag (optional)

Used as a child tag of the component tag. Either OGNL expressions in the body or the child component tag is used for the value to be configured as a property.

name property (required)

Specifies a property name.

bindingType property (optional)

Allows fine control of auto-binding per property. Options are must, should (default), may, and none. Refer to Automatic Binding for details.

meta tag (optional)

Used as a child tag in the components, component, arg and property tags. Either OGNL expressions in the body or the child component tag is used for the metadata value.

name property (optional)

Specifies a meta name.

initMethod tag (optional)

Used as a child tag of the component tag. A child arg tag is used for arguments. OGNL expressions can be used to call a component method without writing the name property. The component defined in initMethod itself, System.out, and System.err is respectively represented as #self, #out, and #err. These can be used as valid objects within the initMethod tag.

name property (optional)

Specifies a method 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 tag (optional)

Same as initMethod tag.

aspect tag (optional)

Builds in aspect into a component. Refer to the description of the aspect tag in S2AOP.

interType tag(optional)

Builds in inter-type into a component. Refer to the description of the interType tag in S2AOP.

description tag (optional)

Used as a child tag in the components, component, arg and property tags. Can freely used to hold descriptions or explanations.

OGNL expressions

S2Container uses OGNL as its expression language. It is in order to convert the contents described in XML as strings into a java object.

  • strings are surrounded in ("), as "hoge".
  • char is surrounded by ('), as 'a'.
  • Numeral values are directly entered, as 123.
  • boolean values are directly entered, as true false.
  • A FQDN of class like new java.util.date(0) can be used to call a constructor
  • Results of a static method like @java.lang.Math@max(1,2) can be referenced.
  • Class like @java.lang.String@class can be referenced.
  • Results of a calling a component method like hoge.toString() can be referenced. This example is assuming there is a component named hoge defined somewhere.

Refer to the OGNL Manual for details.



S2Container Annotation Reference

S2Container uses 3 annotation implementations; Tiger annotation, Backport175 annotation, and constant annotation. Generally, annotation refers to Tiger annotation in Java5, but this would exclude users of JDK1.4. backport175 annotation allows tiger (JSR-175) style annotations in JDK1.4, and constant annotation uses public static final constants. These are implemented to give annotations capability to as many users as possible.

Component Annotation

Can be substituted for the component tag.

Tiger annotation is as follows.

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

Backport175 annotation is as follows.

/**
 * @org.seasar.framework.container.annotation.backport175.Component(
 *      name = "xxx",
 *      instance = "prototype",
 *      autoBinding = "property")
 */
public class Xxx {
    ...
}

Constant annotation is as follows.

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

Binding Annotation

Can be substituted for the property tag.

Tiger annotation is as follows.

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

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

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

Backport175 annotation is as follows.

/**

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

Constant annotation specifies the property name_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 Annotation

Can be substituted for the aspect tag. Multiple definitions cannot be made unlike the aspect tag, so use InterceptorChain when multiple interceptors are to be applied.

Tiger annotation is as follows.

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

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

Backport175 annotation is as follows.

/**
 * @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() {
        ...
    }
}

Constant annotation is as follows. Separate multiple pointcuts with \n, as in get.*\nexecute. Only \n may be used as a separator character.

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

InterType Annotation

Can be substituted for the interType tag.

Tiger annotation is as follows.

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

backport175 annotation is as follows.

/**
 * @org.seasar.framework.container.annotation.backport175.InterType(
 *  {"aop.propertyInterType", "aop.someInterType"})
 */
public class Xxx {
    ...
}

Constant Annotation is as follows.

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

InitMethod Annotation

Can be substituted for the initMethod tag.

Tiger annotation is as follows.

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

Backport175 annotation is as follows.

public class Xxx {
    ...
    /**
     * @org.seasar.framework.container.annotation.backport175.InitMethod
     */
    public void init() {
        ...
    }
}

Constant annotation is as follows. Separate multiple initialization methods with commas.

public static final String INIT_METHOD = "init";

DestroyMethod Annotation

Can be substituted for the destroyMethod tag.

Tiger annotation is as follows.

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

Backport175 annotation is as follows.

public class Xxx {
    ...
    /**
     * @org.seasar.framework.container.annotation.backport175.DestroyMethod
     */
    public void destroy() {
        ...
    }
}

Constant annotation is as follows. Separate multiple initialization methods with commas.

public static final String DESTROY_METHOD = "destroy";