快速入门
seasar2是一个依赖注入(DI)操作的轻量级容器。
第一步
让我们试试看,将使用下列对象。
Greeting Class
返回一个问候字符串。Greeting Client Class
输出从问候类到控制台的字符串。Greeting Main Class
启动类。还构造问候和问候客户端类。
Greeting.java
Greeting interface.
package examples.di;
public interface Greeting {
String greet();
}
GreetingImpl.java
Getting的实现
package examples.di.impl;
import examples.di.Greeting;
public class GreetingImpl implements Greeting {
public String greet() {
return "Hello World!";
}
}
GreetingClient.java
使用Getting的客户端接口
package examples.di;
public interface GreetingClient {
void execute();
}
GreetingClientImpl.java
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!
如上所示,是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.
我们将会把配置信息写入配置文件中。在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="greetingClient"
class="examples.di.impl.GreetingClientImpl">
<property name="greeting">greeting</property>
</component>
等价于
GreetingClientImpl greetingClient = new GreetingClientImpl();
greetingClient.setGreeting(greeting);
下面使用S2Container方式启动程序:
GreetingMain2.java
package examples.di.main;
import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.factory.S2ContainerFactory;
import examples.di.GreetingClient;
public class GreetingMain2 {
private static final String PATH =
"examples/di/dicon/GreetingMain2.dicon";
public static void main(String[] args) {
S2Container container =
S2ContainerFactory.create(PATH);
GreetingClient greetingClient = (GreetingClient)
container.getComponent("greetingClient");
greetingClient.execute();
}
}
S2Container 通过 S2ContainerFactory#create(String Path) 被创建。
组件(greetingClient)通过S2Container#getComponent(String componentName)被获取。
其运行结果如下:
Hello World!
AOP经常和DI一起使用,AOP是指将日志等的输出分散到复数个类中的逻辑模块化的一种技术。我们将从 GreetingImpl中输入一个log (trace),但是却不改变GreetingClientImpl 现有的源代码。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 path="aop.dicon"/>
我们在组件中定义标签应用AOP,在body中指定AOP的模块名,我们将使用aop.traceInterceptor作为模块名。
<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();
}
}
运行结果如下,我们看到log的输出并没有改变原来的代码。
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还具有以下概念来减少配置文件中的文字。
- Convention over Configuration
这实现了一些公约,因此,一些程序可以离开配置运行,只要它遵守这些规定,举个例子,之前的配置文件明确定义属性标记如下:
<component name="greetingClient"
class="examples.di.impl.GreetingClientImpl">
<property name="greeting">greeting</property>
</component>
只要属性类型是一个接口,并且在容器中有一个接口的实现,S2Container 有自动DI的功能。这意味着S2Container将自动处理组件,只要他们遵循接口定义属性类型的DI约定。
“公约”听起来很烦人,但它们不仅被推荐,而且使开发更容易。
我们可以从上面简化配置如下:
<component name="greetingClient"
class="examples.di.impl.GreetingClientImpl">
</component>
其实,“公约优先于配置”很早就在AOP中被运用。通常情况下,在AOP模块被应用的地方或方法定义切入点。ll 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. 这就是为什么在前面例子中没有必要定义一个模块的切入点。
在配置使用公约将简化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>
在S2Container中,这个 FileSystemComponentAutoRegister 组件从file system 中搜索在 addClassPattern 中被定义的类,并自动注册他们
addClassPattern方法中的第一个参数是自动注册组件的包名,递归搜索子包。第二个参数是类的名字,可能是正则表达式,多个定义用逗号隔开。
组件的自动注册减少了总工作量,因为程序猿不需要配置新的组件。
当我们自动化组件的注册,下一个重点就是自动化方面的注册,GreetingImpl 和 GreetingClientImp的Aspect自动注册的配置文件如下:
<include path="aop.dicon"/>
...
<component
class="org.seasar.framework.container.autoregister.AspectAutoRegister">
<property name="interceptor">aop.traceInterceptor</property>
<initMethod name="addClassPattern">
<arg>"examples.di.impl"</arg>
<arg>".*Impl"</arg>
</initMethod>
</component>
我们在 interceptor 属性中指定AOP模块的名称。我们将跳过 addClassPattern 方法的解释,因为它与组件自动注册相同。组件和面的自动注册的结合,看起来如下
GreetingMain4.dicon
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.3//EN"
"http://www.seasar.org/dtd/components23.dtd">
<components>
<include path="aop.dicon"/>
<component
class="org.seasar.framework.container.autoregister.FileSystemComponentAutoRegister">
<initMethod name="addClassPattern">
<arg>"examples.di.impl"</arg>
<arg>".*Impl"</arg>
</initMethod>
</component>
<component
class="org.seasar.framework.container.autoregister.AspectAutoRegister">
<property name="interceptor">aop.traceInterceptor</property>
<initMethod name="addClassPattern">
<arg>"examples.di.impl"</arg>
<arg>".*Impl"</arg>
</initMethod>
</component>
</components>
我们现在运行GreetingMain4,在使用自动注册的情况下我们必须调用 S2Container#init() 和 S2Container#destroy()。
GreetingMain4.java
package examples.di.main;
import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.factory.S2ContainerFactory;
import examples.di.GreetingClient;
public class GreetingMain4 {
private static final String PATH =
"examples/di/dicon/GreetingMain4.dicon";
public static void main(String[] args) {
S2Container container =
S2ContainerFactory.create(PATH);
container.init();
try {
GreetingClient greetingClient = (GreetingClient)
container.getComponent("greetingClient");
greetingClient.execute();
} finally {
container.destroy();
}
}
}
其结果如下,与GreetingMain3相同
DEBUG 2005-10-12 16:00:08,093 [main] BEGIN examples.di.impl.GreetingClientImpl#execute()
DEBUG 2005-10-12 16:00:08,103 [main] BEGIN examples.di.impl.GreetingImpl#greet()
DEBUG 2005-10-12 16:00:08,103 [main] END examples.di.impl.GreetingImpl#greet() : Hello World!
Hello World!
DEBUG 2005-10-12 16:00:08,103 [main] END examples.di.impl.GreetingClientImpl#execute() : null
自动注册和自动绑定将在多数情况下工作良好。组件可以使用 addIgnoreClassPattern method 从自动注册中排除。
绑定注释可用于微调配置设置,而无需使用配置文件以排除自动绑定的属性。
热插拔可立即重试不重新启动应用程序,开发更高效。
现在我们已经掌握了先进的使用s2container。请根据需要参考手册。