论坛首页 Java企业应用论坛

开源框架 Seasar2

浏览 8292 次
该帖已经被评为隐藏帖
作者 正文
   发表时间:2008-04-25   最后修改:2008-12-07

在日本,Seasar2这个框架十分的流行。Seasar2其实就是类似于Spring的一个开源框架

大家有兴趣的话,可以去官方网站看看: http://www.seasar.org/index.html

中文版现在还没完善,大家可能要用日文或则英文来了解

下面简单介绍一下:

所谓“Seasar2”就是一个“轻量级容器”,面向无法摆脱“Java 应用开发”之烦恼的所谓“开发者”,它能够保证开发的“高生产率和高品质”。并且同“其它轻量级容器”不同的是,“完全不需要书写设定文件”,“就算是应用程序发生改动也无需再次起动即可直接识别变更,因此具有脚本语言的灵活性”。

为了不用写设定文件也能够运行,Convention over Configuration 的思想得以采用。Convention over Configuration就是指,“只要遵守一个适当的规约,即使不用进行非常麻烦的设定,框架结构也可以自动替我们搞定的思想”,这一思想是Ruby on Rails 中所倡导的。Seasar2的Convention over Configuration是从Ruby on Rails 那里得到的提示而产生的。

使用Seasar2的话,对于仅仅需要维护数据表这样简单的应用,可以在不到3分钟的时间里作成。

应用程序发生改动之时也无需启动便可立即识别变更的机能在Seasar2里被称为HOT deploy



安装:

S2需要安装JDK1.4 or JDK1.5。

将S2xxx.zip解压之后的seasar2目录引入到Eclipse、「文件→导入→既存的工程」。

使用Seasar2基本功能(S2Container, S2AOP)的时候、CLASSPATH的下面必须包含以下文件。

  • lib/aopalliance-1.0.jar
  • lib/commons-logging-1.1.jar
  • lib/javassist-3.4.ga.jar
  • lib/ognl-2.6.9-patch-20070624.jar
  • lib/s2-framework-2.x.x.jar
  • lib/geronimo-j2ee_1.4_spec-1.0.jar (参考下面)
  • lib/portlet-api-1.0.jar (任选项)
  • lib/log4j-1.2.13.jar (任选项)
  • resources/log4j.properties (任选项)
  • resources/aop.dicon (任选项)

使用Seasar2的扩张机能(S2JTA, S2DBCP, S2JDBC, S2Unit, S2Tx, S2DataSet)的时候必须要将以下文件追加到CLASSPATH里面。

  • lib/junit-3.8.2.jar
  • lib/poi-2.5-final-20040804.jar
  • lib/s2-extension-2.x.x.jar
  • lib/geronimo-jta_1.1_spec-1.0.jar (参考下面)
  • lib/geronimo-ejb_2.1_spec-1.0.jar (参考下面)
  • resources/jdbc.dicon

根据应用软件所需的执行环境、选择以下需要引用的文件[geronimo-j2ee_1.4_spec-1.0.jar、geronimo-jta_1.0.1B_spec-1.0.jar、geronimo-ejb_2.1_spec-1.0.jar]

 

环境 geronimo-j2ee_1.4_spec-1.0.jar geronimo-jta_1.1_spec-1.0.jar geronimo-ejb_2.1_spec-1.0.jar
不完全对应J2EE的Servlet container
(Tomcat等)
不要
(使用S2JTA,S2Tx的时候)

(使用S2Tiger的时候)
完全对应J2EE的应用服务器
(JBoss, WebSphere, WebLogic等)
不要 不要 不要
独立
(使用S2JTA,S2Tx时候)
不要 不要

 

为了让大家更简单的体验数据库机能、使用了HSQLDB作为RDBMS。为了能够体验Oracle机能、准备了hsql/sql/demo-oracle.sql。SQL*Plus等执行了之后、请根据环境的需要改写jdbc.dicon的XADataSourceImpl的设定项目

请使用S2Container用的插件Kijimuna

想使用EJB3anoteshon的情况下、将 S2TigerXXX.zip解压缩后的s2-tiger目录引入Eclipse、「文件→导入→既存的工程」。 在Seasar2的设定基础上、必需要将以下的文件追加到CLASSPATH里面。

  • lib/s2-tiger-x.x.x.jar
  • resources/jdbc.dicon

想使用Tigeranoteshon的情况、将S2TigerXXX.zip解冻后的s2-tiger目录引入Eclipse、「文件→进口→既存的项目」。 在Seasar2的设定基础上、必需要将以下的文件追加到CLASSPATH里面。

  • lib/s2-tiger-x.x.x.jar

 

快速上手

S2Container,就是进行Dependency Injection(注:依赖注入——译者)(以后略称为DI)的一个轻量级容器。DI,就是Interface和实装分离,程序相互之间仅通过Interface来会话的一种思考方式。

最初的一步

让我们赶快试一试吧。登场人物如下。

  • 问候语类
    • 返回问候语的字符串。
  • 问候客户端类
    • 从问候类获得问候语(字符串)并输出到终端屏幕。
  • 问候语应用主类
    • 启动用的类。用来组织问候语类和问候语使用者类的组成方式。
Greeting.java

问侯语的Interface。

package examples.di;

public interface Greeting {

    String greet();
}
GreetingImpl.java

问候语的实装。

package examples.di.impl;

import examples.di.Greeting;

public class GreetingImpl implements Greeting {

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

使用问候语的使用者客户端Interface。

package examples.di;

public interface GreetingClient {

    void execute();
}
GreetingClientImpl.java

使用问候语的客户端的实装。不是直接使用这个GreetngImpl(实装),而是通过Greeting(Interface)来实现问候的机能。

package examples.di.impl;

import examples.di.Greeting;
import examples.di.GreetingClient;

public class GreetingClientImpl implements GreetingClient {

    private Greeting greeting;

    public void setGreeting(Greeting greeting) {
        this.greeting = greeting;
    }

    public void execute() {
        System.out.println(greeting.greet());
    }
}

机能提供端和使用端的准备都完成了。下面我们就执行一下试试吧。

GreetingMain.java
package examples.di.main;

import examples.di.Greeting;
import examples.di.impl.GreetingClientImpl;
import examples.di.impl.GreetingImpl;

public class GreetingMain {

    public static void main(String[] args) {
        Greeting greeting = new GreetingImpl();
        GreetingClientImpl greetingClient = new GreetingClientImpl();
        greetingClient.setGreeting(greeting);
        greetingClient.execute();
    }
}

实行结果如下。

Hello World!

象这样机能的使用者(GreetingClientImpl)经由Interface(Greeting)的中介来使用机能,具体的机能对象(既Interface的实装类)在实行的时候由第三者(在这里是GreetingMain)来提供的情况,就是DI的基本思考方法。

但是,如果象GreetingMain中那样实装类的设定内容直接被写出来的话,一旦实装类需要变更的时候源代码也必须跟着修正。为了避免这个麻烦,DIContainer就登场了。把实装设定抽出到一个设定文件中,由DIContainer把这个设定文件读入并组织对象运行。

那么,让我们试着把刚才的提到的那个设定文件的内容写一下。S2Container中,设定文件的后缀是".dicon"。

GreetingMain2.dicon
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC
    "-//SEASAR//DTD S2Container 2.3//EN"
    "http://www.seasar.org/dtd/components23.dtd">
<components>
    <component name="greeting"
        class="examples.di.impl.GreetingImpl"/>
    <component name="greetingClient"
        class="examples.di.impl.GreetingClientImpl">
        <property name="greeting">greeting</property>
    </component>
</components>

 

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

上文记载的是组件的定义。在这里,相当于如下的Java代码。

Greeting greeting = new GreetingImpl();

component标签的name属性指定了组件的名称,class属性指定了组件的Java类文件名。下文就是greetingClient的设定。

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

property标签的name属性指定了组件Java类中的属性名,标签的定义体则指定了一个组件名称。这个设定相当于如下Java代码。组件名要注意不要用["]括起来。用["]括起来的话就会被当作字符串来处理了。

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

利用S2Container的起动类的内容如下。

GreetingMain2.java
package examples.di.main;

import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.factory.S2ContainerFactory;

import examples.di.GreetingClient;

public class GreetingMain2 {

    private static final String PATH =
        "examples/di/dicon/GreetingMain2.dicon";

    public static void main(String[] args) {
        S2Container container =
            S2ContainerFactory.create(PATH);
        container.init();
        GreetingClient greetingClient = (GreetingClient)
            container.getComponent("greetingClient");
        greetingClient.execute();
    }
}

S2Container,是由S2ContainerFactory#create(String path)做成的。更加详细的内容请参照S2Container的生成

组件(greetingClient),是由S2Container#getComponent(String componentName)的方法取得的。详细内容请参照组件的取得

实行结果同先前一样表示如下。

Hello World!

经常同DI一起使用的是AOP。AOP是指、将日志等的输出分散到复数个类中的逻辑模块化的一种技术。那么、让我们不修改已经作成的GreetingImpl、GreetingClinetImpl的源代码?试着将日志(追踪)输出。 适用于AOP的设定文件如下。

GreetingMain3.dicon
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC
    "-//SEASAR//DTD S2Container 2.3//EN"
    "http://www.seasar.org/dtd/components23.dtd">
<components>
    <include path="aop.dicon"/>
    <component name="greeting"
        class="examples.di.impl.GreetingImpl">
        <aspect>aop.traceInterceptor</aspect>
    </component>
    <component name="greetingClient"
        class="examples.di.impl.GreetingClientImpl">
        <property name="greeting">greeting</property>
        <aspect>aop.traceInterceptor</aspect>
    </component>
</components>

Seasar2中,经常使用的AOP模块在aop.dicon中预先定义。 象下面这样、使用include标签。 更加详细的?敬请参照S2Container定义的分解和引入

<include path="aop.dicon"/>

对于在组件中适用的AOP来说?我们component标签 的字标签 aspect标签 的正文中指定AOP的模块名称。aop.traceInterceptor是AOP模块的名字。

<aspect>aop.traceInterceptor</aspect>

AOP的设定如上所述。那么就让我们执行一下GreetingMain3吧。同GreetingMain2不同的仅仅是设定文件的路径而已。

GreetingMain3.java
package examples.di.main;

import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.factory.S2ContainerFactory;

import examples.di.GreetingClient;

public class GreetingMain3 {

    private static final String PATH =
        "examples/di/dicon/GreetingMain3.dicon";

    public static void main(String[] args) {
        S2Container container =
            S2ContainerFactory.create(PATH);
        GreetingClient greetingClient = (GreetingClient)
            container.getComponent("greetingClient");
        greetingClient.execute();
    }
}

执行结果如下。可以明白一点,没有修改源代码,日志就被输出了。

DEBUG 2005-10-11 21:01:49,655 [main] BEGIN examples.di.impl.GreetingClientImpl#execute()
DEBUG 2005-10-11 21:01:49,665 [main] BEGIN examples.di.impl.GreetingImpl#greet()
DEBUG 2005-10-11 21:01:49,665 [main] END examples.di.impl.GreetingImpl#greet() : Hello World!
Hello World!
DEBUG 2005-10-11 21:01:49,675 [main] END examples.di.impl.GreetingClientImpl#execute() : null

这样、S2Container的基本使用方法就被掌握了。

更进一步

但是,不管怎么说书写设定文件都是一件麻烦的事啊。在S2Container中,为了尽可能的减少设定文件的记述量、采用了如下的概念。

就是说制定一个适当的规约,遵守这个规约的话?无需什么设定也可以运作。比如说,刚才的设定文件中,象下面这样明确地指定属性的部分存在着。

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

S2Container中、属性的类型是Interface的情形下? 如果要将属性类型的实装组件注册进软件容器中, 不需要什么特殊的设定也可以自动得运作DI的机能。 这就是,如果遵守DI中推荐的所谓“属性类型用Interface定义”的规则 ,S2Container会自动地处理一切。

虽然一说到规约就容易产生麻烦之类的想法,“推荐而已,如果遵守的话就能使开发愉快”的话,遵守规约的动机就产生了。这才是问题的重点。

如上的设定,可以做如下化简

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

实际上?刚才的AOP的例子也适用“Convention over Configuration”。 通常在AOP中,AOP的模块在什么地方适用是由pointcut指定的,S2AOP 的情况下? 如果遵守所谓“使用Interface”这个推荐的规约 ,不指定pointcut,自动的适用于在Interface中定义的所有方法。因为有这个机能,在刚才的那个例子中,就没有必要指定pointcut。

虽然根据“Convention over Configuration”,DI和AOP的设定可以得以简化,需要处理的组件数增加了、仅仅组件的注册也会变成一个非常累的作业。那么这个组件注册自动化就叫做组件自动注册 机能。 刚才的GreetingImpl、GreetingClientImpl的注册自动化如下。

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

FileSystemComponentAutoRegister组件将addClassPattern方法指定的类从文件系统中探寻出来,自动注册到S2Container中。关于initMethod标签,请参照方法函数注入

addClassPattern方法的第一个参数是想要注册的组件的包的名字。 子包的内容也会用回归的方式检索。第二个参数是类的名字。可以使用正则表达式。也可以用“,”做分隔符指定复数个设定。

根据组件自动注册原则,即使后续追加组件的情况下,也没有必要追加设定,这样手续就大大地简化了。

如果组件的自动化注册可以了,接下来就会想让AOP的注册也自动化了吧。刚才的GreetingImpl、GreetingClientImp的AOP注册自动化的设定如下。

<include path="aop.dicon"/>
...
<component
  class="org.seasar.framework.container.autoregister.AspectAutoRegister">
    <property name="interceptor">aop.traceInterceptor</property>
    <initMethod name="addClassPattern">
        <arg>"examples.di.impl"</arg>
        <arg>".*Impl"</arg>
    </initMethod>
</component>

用interceptor属性指定AOP的名称。addClassPattern方法同组件的自动化注册时的用法一样,这里就不做特殊的说明了。 组件自动化注册和AOP自动化注册的例子如下。

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

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

    <component
      class="org.seasar.framework.container.autoregister.AspectAutoRegister">
        <property name="interceptor">aop.traceInterceptor</property>
        <initMethod name="addClassPattern">
            <arg>"examples.di.impl"</arg>
            <arg>".*Impl"</arg>
        </initMethod>
    </component>
</components>

那么来执行一下GreetingMain4吧。 自动注册的情况下,S2Container#init()和S2Container#destroy()的调用是必要的。

GreetingMain4.java
package examples.di.main;

import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.factory.S2ContainerFactory;

import examples.di.GreetingClient;

public class GreetingMain4 {

    private static final String PATH =
        "examples/di/dicon/GreetingMain4.dicon";

    public static void main(String[] args) {
        S2Container container =
            S2ContainerFactory.create(PATH);
        container.init();
        try {
            GreetingClient greetingClient = (GreetingClient)
                container.getComponent("greetingClient");
            greetingClient.execute();
        } finally {
            container.destroy();
        }
    }
}

执行的结果同GreetingMain3一样如下列出。

DEBUG 2005-10-12 16:00:08,093 [main] BEGIN examples.di.impl.GreetingClientImpl#execute()
DEBUG 2005-10-12 16:00:08,103 [main] BEGIN examples.di.impl.GreetingImpl#greet()
DEBUG 2005-10-12 16:00:08,103 [main] END examples.di.impl.GreetingImpl#greet() : Hello World!
Hello World!
DEBUG 2005-10-12 16:00:08,103 [main] END examples.di.impl.GreetingClientImpl#execute() : null

大多数的情况下?自动注册和自动绑定的组合方式都能顺利的进行下去。不想要自动注册的组件存在的情况下,自动注册组件中准备了addIgnoreClassPattern方法,可以指定自动注册外的组件。

不想要自动绑定的属性存在的情况下,使用Binding备注码 ,不使用设定文件也可以做细节的调整。

使用Hotswap 的话?应用程序在运行中重新书写更换类文件,马上就能够直接测试结果。不需要一个一个地将应用程序在启动,因此开发的效率能够得到大幅度的提高。

现在,关于S2Container的高级使用方法也可以掌握了。这之后嘛,只要根据需要参照对应的操作手册就可以了。

S2Container参考

需要作成的文件

为了使用S2Container,定义文件的做成是必要的。定义文件就像是为了组织组件而制作的设计书一样的东西。形式为XML,后缀为dicon。

S2Container的定义

S2Container的定义、象下面这样。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.3//EN"
"http://www.seasar.org/dtd/components23.dtd">
<components>
    <component name="..." class="...">
            ...
    </component>
    <component name="..." class="...">
            ...
    </component>
</components>

DOCTYPE是不能省略的。dicon做成的时候、请将前述例子拷贝粘贴。根是components标签 。每一个组件用component标签 定义。用component标签的class属性指定组件的类的全名。在name属性中、指定组件的名称。详细内容请参照S2Container定义标签参考

<components>
    <component name="hoge" class="examples.dicon.HogeImpl"/>
</components>

S2Container的生成

S2Container的生成方法有两种。

  • 使用SingletonS2ContainerFactory。
  • 使用S2ContainerFactory。

使用SingletonS2ContainerFactory

使用SingletonS2ContainerFactory的情况下,使用如下方法。

- org.seasar.framework.container.factory.SingletonS2ContainerFactory#init()

定义文件使用的是CLASSPATH所指定的路径中存在的app.dicon。

做成的S2Container,无论在什么地方都是可以从如下方法中取得。

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

SingletonS2ContainerFactory.init();
...
S2Container container = SingletonS2ContainerFactory.getContainer();

定义文件的路径需要被指定的情况下应在调用init()之前执行如下方法。

- org.seasar.framework.container.factory.SingletonS2ContainerFactory#setConfigPath(String Path)

参数path是相对于以CLASSPATH指定的路径为根的定义文件的绝对路径。例如,WEB-INF/classes/aaa.dicon 的情况下就是aaa.dicon,WEB-INF/classes/aaa/bbb/ccc.dicon的情况下就是aaa/bbb/ccc.dicon。分隔符在Windows和Unix下都是/。

private static final String PATH = "aaa/bbb/ccc.dicon";
...
SingletonS2ContainerFactory.setConfigPath(PATH);
SingletonS2ContainerFactory.init();
...
S2Container container = SingletonS2ContainerFactory.getContainer();

使用S2ContainerFactory

使用S2ContainerFactory的场合下,使用如下方法。

- org.seasar.framework.container.factory.S2ContainerFactory#create(String path)

S2Container生成之后需要许呼叫下一个方法。

- org.seasar.framework.container.S2Container#init()

private static final String PATH = "aaa/bbb/ccc.dicon";
...
S2Container container = S2ContainerFactory.create(PATH);
container.init();

用这个方法取得的组件的实例,有必要进行在应用中的管理。

组件的取得

从S2Container中将组件取出来,使用下面的方法。

- org.seasar.framework.container.S2Container#getComponent(Object componentKey)

参数中指定的是组件的类或者是组件的名称。详细的请参照component标签 。要指定组件的类,只要是 组件 instanceof 类 的操作返回为true的类就能够指定。但是、S2Container中所指定的类对应了好几个实装的组件的时候,S2Container将不能判断返回哪一个组件为好,这样就会发生TooManyRegistrationRuntimeException。请指定实装组件为唯一的类。也可以用组件名称取得组件。这种情况下也是同样,用一个名称的复数个组件被注册的情况下,将发生TooManyRegistrationRuntimeException。指定组件名的场合下,因为也可能发生拼写错误,所以尽可能的指定组件的类为好。

例)通过指定类来取得组件的场合

S2Container container = S2ContainerFactory.create(PATH);
Hoge hoge = (Hoge) container.getComponent(Hoge.class);

例)通过指定组件名来取得组件场合

S2Container container = S2ContainerFactory.create(PATH);
Hoge hoge = (Hoge) container.getComponent("hoge");

Dependency Injection的类型

在Dependency Injection中,组件的构成所必要的值是用构造函数来设定(Constructor Injection),还是用设定函数来设定(Setter Injection),或者是用初始化函数来设定(Method Injection),这样进行分类。Method Injection是S2Container的本源。S2Container支持以上所有类型和混合类型。

构造函数?注入

对构造函数的参数进行DI,这就是构造函数注入。
S2Container的定义文件中,记述如下内容。

  • 组件的指定
    组件,用component标签来组建。用class指定对应的类。
    也可以用name属性给组件起名称。
  • 构造函数的参数的指定
    组件的构造函数的参数用component标签的子标签arg标签来指定。
    值为字符串的时候,用双引号(")括起来。
<components>
    <component name="..." class="...">
          <arg>...</arg>
    </component>
</components>

设定函数?注入

设定函数注入是指对于任意一个属性变量使用设定函数来行使DI。
S2Container的定义文件中作如下内容的记述。

  • 组件的指定
    组件的指定同构造函数注入相同。
  • 属性变量的指定
    组件的属性变量用component标签的子标签property来指定。
    用name属性来指定变量的名称。
<components>
    <component name="..." class="...">
          <property name="...">...</property>
    </component>
</components>

方法函数?注入

方法函数注入是指,通过任意一个函数的调用来完成DI的功能。
S2Container的定义文件中,记述如下内容。

  • 组件的指定
    组件的指定同构造函数注入相同。
  • 初始化方法函数的指定
    使用initMethod标签,调用组件的任意一个方法函数。在name属性中,指定方法函数的名称。 用arg标签指定参数,name属性省略,在正文中,使用OGNL式 也可以。
<components>
    <component name="..." class="...">
        <initMethod name="...">
            <arg>...</arg>
        </initMethod>
    </component>
</components>

S2Container定义的分割和引入

所有的组件用一个文件来设定的话,很快就会变得臃肿而难以管理。因此,S2Container就具有了将组件的定义进行复数个分割的机能和将多个分割的定义文件引入而组织成一个文件的机能。S2Container定义文件的引入方法如下。

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

include标签 的path属性被用来指定想要引入的S2Container定义文件的路径。详细情况请参照include标签
组件的检索顺序,先是在自身注册的文件中寻找组件,没有找到所需组件的情况下,将按照include的顺序在子定义文件中查找注册到S2Container中的组件,最先找到的那个组件将被返回。

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

命名空间

组件的定义被分割的情况下,为了不让复数个组件的定义名称发生冲突,可以用components标签 的namespace属性指定命名空间。

foo.dicon
<components namespace="foo">
    <component name="aaa" .../>
    <component name="bbb" ...>
        <arg>aaa</arg>
    </component>
</components>
bar.dicon
<components namespace="bar">
    <include path="foo.dicon"/>
    <component name="aaa" .../>
    <component name="bbb" ...>
        <arg>aaa</arg>
    </component>
    <component name="ccc" ...>
        <arg>foo.aaa</arg>
    </component>
</components>
app.dicon
<components>
    <include path="bar.dicon"/>
</components>

在同一个组件定义文件中可以不需要指定命名空间而调用组件。调用其它S2Container文件中定义的组件时,要在组件名前加上命名空间。foo.aaa 和 bar.aaa 虽然有相同名称的组件,但是因为命名空间的不同,就被认为是不同的组件。

实例(instance)管理

在S2Container中,怎么样对实例进行管理,这个设定是用component标签 的instance属性。

 

instance属性 说明
singleton(default) 不论S2Container.getComponent()被调用多少次都返回同一个实例。
prototype S2Container.getComponent()每次被调用的时候都返回一个新的实例。
request 对应每一个请求(request)做成一个实例。用name属性中指定的名称,组件被容纳在请求中。使用request的场合下需要设定S2ContainerFilter
session 对应每一个session做成一个实例。用name属性中指定的名称,组件被容纳在session中。使用session的场合下需要设定S2ContainerFilter
application 使用Servlet的场合下,对应每一个ServletContext做成一个实例。用name属性中指定的名称,组件被容纳在ServletContext中。使用application的场合下需要设定S2ContainerFilter
outer 组件的实例在S2Container之外作成,从而仅仅行使Dependency Injection的功能。Aspect构造函数注入 不能适用。

生存周期

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

自动绑定

组件间的依存关系,类型是interface的场合时,将由容器来自动解决。这是在S2Container被默认的,指定component标签 的autoBinding属性可以进行更加细致的控制。

 

autoBinding 说明
auto(default) 适用于构造函数和属性变量的自动绑定。
constructor 适用于构造函数的自动绑定。
property 适用于属性变量的自动绑定。
none 只能对构造函数、属性变量进行手动绑定。

 

构造函数的自动绑定规则如下所示。

  • 明确指定了构造函数的参数的情况下,自动绑定将不再适用。
  • 不属于上述情况,如果是定义了没有参数的默认的构造函数的话,对于这个构造函数,自动绑定也不适用。
  • 不属于上述情况,参数的类型全是interface并且参数数目最多的构造函数将被使用。 这样,对于从容器中取得参数类型的实装组件,自动绑定是适用的。
  • 如果不是以上情况,自动绑定将不适用。

属性变量的自动绑定规则如下。

  • 明确指定了属性变量的情况下,自动绑定将不适用。
  • 不属于上述情况,如果在容器的注册组件中存在着可以代入属性变量中的同名组件,自动绑定将适用于该组件。
  • 不属于上述情况,属性变量的类型是interface并且该属性类型的实装组件在容器中注册了的话,自动绑定是适用的。
  • 如果不是以上情况,自动绑定将不适用。

用property标签的bindingType属性,可以更加细致的控制属性变量。

 

bindingType 说明
must 自动绑定不适用的情况下?将会发生例外。
should(default) 自动绑定不适用的情况下,将发出警告通知。
may 自动绑定不适用的情况下,什么都不发生。
none autoBinding的属性虽然是auto、property情况下,自动绑定也不适用。

 

在组件中利用S2Container

不想让组件依存于S2Container的情况下,根据组件的具体情况,在组件中需要调用S2Container的方法,这样的场合也许会存在。S2Container自身也以container的名称,自我注册了。所以可以在arg,property标签的正文中指定container,从而取得容器的实例。还有,S2Container类型的setter方法定义好了后也可以做自动绑定的设定。用arg,property标签指定container的情况下,向下面这样进行。

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

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

S2ContainerServlet

到此为止,在Java application中,是用明确表示的方法做成S2Container的,Web application的情况下,由谁来作成S2Container呢?为了达到这个目的,准备了以下的类。

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

为了使用S2ContainerServlet,在web.xml中记述如下项目。

<servlet>
    <servlet-name>s2servlet</servlet-name>
    <servlet-class>org.seasar.framework.container.servlet.S2ContainerServlet</servlet-class>
    <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>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>s2servlet</servlet-name>
    <url-pattern>/s2servlet</url-pattern>
</servlet-mapping>

用configPath来指定作为根的S2Container的定义路径。定义文件将放在WEB-INF/classes中。对于S2ContainerServlet,为了比其它的servlet更早的起动,请做load-on-startup标签的调整。S2ContainerServlet起动之后,可以用如下的方法函数取得S2Container的实例。

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

另外,S2Container的生命周期和S2ContainerServlet是连动的。debug变量被设为true的话,按照以下的方法,可以将运行中的S2Container再次起动。xxx是Web application的context名。

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

在使用了S2ContainerServlet的情况下,ServletContext将会作为一个组件可以用servletContext的名字来访问。

app.dicon的角色

根的S2Container的定义文件,按照惯例用app.dicon的名称。通常放在WEB-INF/classes中就好了。

AOP的适用

在组件中AOP 的适用情况也可以被设定。比如,想要在ArrayList中设定TraceInterceptor 使用的情况下需要象下面这样做。

<components>
    <component name="traceInterceptor"
               class="org.seasar.framework.aop.interceptors.TraceInterceptor"/>
    <component class="java.util.ArrayList">
        <aspect>traceInterceptor</aspect>
    </component>
    <component class="java.util.Date">
        <arg>0</arg>
        <aspect pointcut="getTime, hashCode">traceInterceptor</aspect>
    </component>
</components>

aspect标签的正文中指定Interceptor 的名字。pointcut的属性中可以用逗号做分隔符指定AOP对象的方法的名字。pointcut的属性没有被指定的情况下,组件将把实装的interface的所有方法函数作为AOP的对象。方法函数的名称指定也可以用正则表达式(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

组件中也可以设定InterType 的适用情况。比如,在Hoge中设定PropertyInterType 的适用情况如下进行。

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

在interType标签的正文中指定InterType 的名称。

Meta数据

在components、component、arg、property标签中也可以指定Meta数据。meta标签将作为需要指定Meta数据的标签的字标签来指定Meta数据。例如,想要在components标签中指定Meta数据的情况时,象下面一样设定。

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

request的自动绑定

对于组件来说,也可以进行HttpServletRequest的自动绑定。为了实现这个目的,在组件中,定义了setRequest(HttpServletRequest request)方法。这样的话,S2Container就自动得设定了request。还有,需要象下面这样在web.xml中进行Filter的定义。

<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、ServletContext也是只要定义了setter方法,就可以自动绑定了。而且,使用了S2ContainerFilter的话,HttpServletRequest、HttpServletResponse、HttpSession、ServletContext就可以各自用request、response、session、application的名字来做为组件被自由访问了。

组件的自动注册

根据自动绑定 的原理,DI的设定几乎可以做近乎全部的自动化。 使用备注码 就有可能进行更加细致的设定。 更进一步、对组件的注册也进行自动化的话,就可以称为组件的自动注册机能了。

org.seasar.framework.container.autoregister.FileSystemComponentAutoRegister

是从文件系统中将类检索出来对组件进行自动注册的组件。

 

属性 说明
instanceDef 在自动注册的组件中指定适用的InstanceDef。用XML指定的场合下,
@org.seasar.framework.container.deployer.InstanceDefFactory@REQUEST
这样来指定。
autoBindingDef 在自动注册的组件中指定适用的AutoBindingDef。用XML指定的场合下,
@org.seasar.framework.container.assembler.AutoBindingDefFactory@NONE
这样来指定。
autoNaming 可以根据类名来自动决定组件名的组件。需要实装 org.seasar.framework.container.autoregister.AutoNaming interface。默认状态下,使用org.seasar.framework.container.autoregister.DefaultAutoNaming类的实例。

 

方法 说明
addClassPattern 将想要自动注册的类模式注册。最开始的一个参数是组件所在包的名字。子包也能被以回归的方式进行检索。第二个参数是类的名字。可以使用正则表达式。也可以用“,”分隔做复数个设定。
addIgnoreClassPattern 将不想自动注册的类模式注册。最开始的一个参数是组件所在包的名字。子包也能被以回归的方式进行检索。第二个参数是类的名字。可以使用正则表达式。也可以用“,”分隔做复数个设定。
org.seasar.framework.container.autoregister.JarComponentAutoRegister

从Jar文件中检索类自动注册组件的组件。

 

属性 说明
jarFileNames 指定设定对象的jar文件名。可以使用正则表达式。但是能包含后缀。指定复数个对象的场合下,用“,”做分割符。例如,myapp.*, yourapp.*这样。
referenceClass 用这个属性指定的类所属的jar文件的父路径为基础路径(例如,WEB-INF/lib)。默认的是org.aopalliance.intercept.MethodInterceptor.class。
instanceDef 适用于自动注册的组件的InstanceDef的指定。在XML中如下,
@org.seasar.framework.container.deployer.InstanceDefFactory@REQUEST
这样指定。
autoBindingDef 适用于自动注册的组件的AutoBindingDef的指定。在XML中如下,
@org.seasar.framework.container.assembler.AutoBindingDefFactory@NONE
这样指定。
autoNaming 根据类名自动决定组件的名称的组件。需要对org.seasar.framework.container.autoregister.AutoNaming interface 进行实装。默认的情况下是org.seasar.framework.container.autoregister.DefaultAutoNaming类的实例。

 

方法 说明
addClassPattern 将想要自动注册的类模式注册。最开始的一个参数是组件所在包的名字。子包也能被以回归的方式进行检索。第二个参数是类的名字。可以使用正则表达式。也可以用“,”分隔做复数个设定。
addIgnoreClassPattern 将不想自动注册的类模式注册。最开始的一个参数是组件所在包的名字。子包也能被以回归的方式进行检索。第二个参数是类的名字。可以使用正则表达式。也可以用“,”分隔做复数个设定。
org.seasar.framework.container.autoregister.ComponentAutoRegister

将类从文件系统或者Jar文件中检索出来并将组件自动注册的组件。

 

属性 说明
instanceDef 适用于自动注册的组件的InstanceDef的指定。在XML中如下,@org.seasar.framework.container.deployer.InstanceDefFactory@REQUEST这样指定。
autoBindingDef 适用于自动注册的组件的AutoBindingDef的指定。在XML中如下,
@org.seasar.framework.container.assembler.AutoBindingDefFactory@NONE
这样指定。
autoNaming 从类的名称来自动决定组件的名称的组件。需要对org.seasar.framework.container.autoregister.AutoNaming instance进行实装。默认的是 org.seasar.framework.container.autoregister.DefaultAutoNaming类的实例。

 

方法 说明
addReferenceClass 以这个方法所指定的类所存在的路径或者Jar文件为基点对类进行检索。
addClassPattern 将想要自动注册的类模式注册。最开始的一个参数是组件所在包的名字。子包也能被以回归的方式进行检索。第二个参数是类的名字。可以使用正则表达式。也可以用“,”分隔做复数个设定。
addIgnoreClassPattern 将不想 自动注册的类模式注册。最开始的一个参数是组件所在包的名字。子包也能被以回归的方式进行检索。第二个参数是类的名字。可以使用正则表达式。也可以用“,”分隔做复数个设定。

 

AutoNaming

根据AutoNaming来控制组件名称。

org.seasar.framework.container.autoregister.DefaultAutoNaming

从类的完整合法名称中将类的包的那部分名称去掉,如果结尾是Impl或者Bean也要去掉,之后将开头的字母变成小写做为组件名称来设定。 例如,aaa.HogeImpl类的情况下,组件的名称就成了hoge。

 

属性 说明
decapitalize 组件名的开头字母为小写的情况下指定为true。默认值是true。

 

方法 说明
setCustomizedName 不依从于默认的规则对类进行注册。第一个参数是类的完整合法名。第二个参数是组件的名称。
addIgnoreClassSuffix 指定从类名的尾端消除的部分。注册默认值为Impl以及Bean。
addReplaceRule 根据正则表达式追加替换规则。第一个参数为正则表达式。第二个参数为向要替换的字符串。
clearReplaceRule 用setCustomizedName、addIgnoreClassSuffix、addReplaceRule将注册的变换规则清零。作为默认值被注册的Impl和Bean也被清零。
org.seasar.framework.container.autoregister.QualifiedAutoNaming

将包的名字或者是一部分类的合法名做为组件名称的设定。从类的完整合法名的最后把Impl或者Bean去掉,开头字母小写,分隔点后紧接着的字母变成大写并取掉分隔点,将这个新的单词设定为组件的名称。
可以将包的开头的不要的部分做消除指定。
例如,aaa.bbb.ccc.ddd.HogeImpl类的情况下,将开头的aaa.bbb做消除指定的情况下组件的名称为,cccDddHogeになります。

 

属性 说明
decapitalize 组件名的开头字母为小写的情况下指定为true。默认值是true。

 

方法 说明
setCustomizedName 遵从默认的规则来注册类。第一个参数是类的完整合法名。 第二个参数是组件的名称。
addIgnorePackagePrefix 从包名称的开头开始指定消除的部分。
addIgnoreClassSuffix 类名称的最末尾开始指定消除的部分。默认地将Impl和Bean注册。
addReplaceRule 根据正则表达式追加替换规则。第一个参数为正则表达式。第二个参数是替换的新字符串。
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>

AOP的自动注册

根据组件的自动注册 规则,组件的注册可以做到自动化。进一步,AOP的注册也可以做到自动化,这就是AOP的自动注册机能。

和组件的自动注册功能组和使用的场合下,必须在组件的自动注册设定之后,作AOP的自动注册的设定。对于适用于使用AOP的组件的记述,必须在AOP的自动注册设定之后进行。

<components>
    <!-- 1.组件的自动注册 -->
    <component class="org.seasar.framework.container.autoregister.ComponentAutoRegister">
        ...
    </component>

    <!-- 2.AOP的自动注册 -->
    <component class="org.seasar.framework.container.autoregister.AspectAutoRegister">
        ...
    </component>

    <!-- 3.其它的组件 -->
    <component class="...">
        ...
    </component>
    ...
<components>
org.seasar.framework.container.autoregister.AspectAutoRegister

通过指定类名的模式来进行AOP的自动注册的组件。

 

属性 说明
interceptor 指定interceptor。想要指定复数个interceptor的场合下,请使用InterceptorChain
pointcut 适于使用interceptor的方法用逗号分隔开进行指定。不指定pointcut的情况下,实装组件的interface的所有方法都做为interceptor的对象。对于方法名称也可以使用正则表达式(JDK1.4のregex)来指定。

 

方法 说明
addClassPattern 将想要自动注册的类的模式注册。第一个参数是组件的包的名。子包也可以用回归的方法检索。第二个参数是类名。可以使用正则表达式。用“,”分隔可以做复数个记述。
addIgnoreClassPattern 将不想自动注册的类模式注册。第一个参数是组件的包的名。子包也可以用回归的方法检索。第二个参数是类名。可以使用正则表达式。用“,”分隔可以做复数个记述。

 

<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

针对某个interface的实装类进行AOP的自动注册的组件。

 

属性 说明
interceptor 指定interceptor。想要指定复数个interceptor的场合下,请使用InterceptorChain
targetInterface 针对某一指定的interface的实装组件,使用AOP。
<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的自动注册

META信息也可以自动注册。

同组件的自动注册相组合使用的场合下,必须在组件的自动注册设定之后,做META的自动注册的设定记述。 调用META信息的组件必须在META自动注册的设定之后记述。

<components>
    <!-- 1.组件的自动注册 -->
    <component class="org.seasar.framework.container.autoregister.ComponentAutoRegister">
        ...
    </component>

    <!-- 2.META的自动注册 -->
    <component class="org.seasar.framework.container.autoregister.MetaAutoRegister">
        ...
    </component>

    <!-- 3.其它的组件 -->
    <component class="...">
        ...
    </component>
    ...
<components>
org.seasar.framework.container.autoregister.MetaAutoRegister

通过指定类名的模式来做META自动注册的组件。
被自动注册的META数据,将做为在这个组件自身的定义中一个叫做autoRegister的META数据的子数据来记述。

 

方法 说明
addClassPattern 将想要自动注册的类模式注册。第一个参数是组件所在包的名字。子包也将被用回归的方式所检索。第二个参数是类名。可以使用正则表达式。用“,”做分隔符号,可以做复数个记述。
addIgnoreClassPattern 将不想自动注册的类模式注册。第一个参数是组件所在包的名字。子包也将被用回归的方式所检索。第二个参数是
   发表时间:2008-05-14  
还是有参考价值的。
看来日本人在自由思维上也算不错了
0 请登录后投票
   发表时间:2008-05-15  
楼主辛苦了,
这个框架太。。。太适合快速开发了。
呵呵
0 请登录后投票
   发表时间:2008-05-25  
请教一下,teeda的session怎么取得的?
比如我在struts里面是通过request.getHttpSession()取得session对象,
0 请登录后投票
   发表时间:2008-07-15  
将GreetingClientImpl.java中的
private Greeting greeting改为public Greeting greeting
那个错误可解决
0 请登录后投票
   发表时间:2008-11-21   最后修改:2008-11-21
总结的还不错
0 请登录后投票
   发表时间:2008-11-21  
原来这是抄袭的,鄙视。。
0 请登录后投票
   发表时间:2008-12-20  
原文地址:
http://s2container.seasar.org/2.4/zh/DIContainer.html#Quickstart
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics