基本对象
1.
Command
接口。它是
Commons Chain
中最重要的接口,表示在
Chain
中的具体某一步要执行的命令。它只有一个方法:
boolean execute(Context context)
。如果返回
true
,那么表示
Chain
的处理结束,
Chain
中的其他命令不会被调用;返回
false
,则
Chain
会继续调用下一个
Command
,直到:
-
Command
返回
true
;
-
Command
抛出异常;
-
Chain
的末尾;
2.
Context
接口。它表示命令执行的上下文,在命令间实现共享信息的传递。
Context
接口的父接口是
Map
,
ContextBase
实现了
Context
。对于
web
环境,可以使用
WebContext
类及其子类(
FacesWebContext
、
PortletWebContext
和
ServletWebContext
)。
3.
Chain
接口。它表示“命令链”,要在其中执行的命令,需要先添加到
Chain
中。
Chain
的父接口是
Command
,
ChainBase
实现了它。
4.
Filter
接口。它的父接口是
Command
,它是一种特殊的
Command
。除了
Command
的
execute
,它还包括一个方法:
boolean postprocess(Context context, Exception exception)
。
Commons Chain
会在执行了
Filter
的
execute
方法之后,执行
postprocess
(不论
Chain
以何种方式结束)。
Filter
的执行
execute
的顺序与
Filter
出现在
Chain
中出现的位置一致,但是执行
postprocess
顺序与之相反。如:如果连续定义了
filter1
和
filter2
,那么
execute
的执行顺序是:
filter1 -> filter2
;而
postprocess
的执行顺序是:
filter2 -> filter1
。
5.
Catalog
接口。它是逻辑命名的
Chain
和
Command
集合。通过使用它,
Command
的调用者不需要了解具体实现
Command
的类名,只需要通过名字就可以获取所需要的
Command
实例。
基本使用
1.
执行由顺序的命令组成的流程,假设这条流程包含
1
、
2
和
3
步。
þ
实现要执行的命令步骤:
public class Command1 implements Command {
public boolean execute(Context arg0) throws Exception {
System.out.println("Command1 is done!");
return false;
}
}
|
public class Command2 implements Command {
public boolean execute(Context arg0) throws Exception {
System.out.println("Command2 is done!");
return false;
}
}
|
public class Command3 implements Command {
public boolean execute(Context arg0) throws Exception {
System.out.println("Command3 is done!");
return true;
}
}
|
þ
注册命令,创建执行的
Chain
:
public class CommandChain extends ChainBase {
//
增加命令的顺序也决定了执行命令的顺序
public CommandChain(){
addCommand( new Command1());
addCommand( new Command2());
addCommand( new Command3());
}
public static void main(String[] args) throws Exception{
Command process = new CommandChain();
Context ctx= new ContextBase();
process.execute( ctx);
}
}
|
2.
使用配置文件加载
Command
。除了在程序中注册命令之外,还可以使用配置文件来完成。
þ
对于例
1
,配置文件可以写成:
<?xml version="1.0" encoding="gb2312"?>
<catalog>
<chain name="CommandChain">
<!--
定义的顺序决定执行的顺序
-->
<command id="command1" className= "chain.Command1"/>
<command id="command2" className= "chain.Command2"/>
<command id="command3" className= "chain.Command3"/>
</chain>
<command name="command4" className="chain.Command1"/>
</catalog>
|
þ
装入配置文件的代码如下:
public class CatalogLoader {
static final String cfgFile= "/chain/chain-cfg.xml";
public static void main(String[] args) throws Exception{
CatalogLoader loader= new CatalogLoader();
ConfigParser parser= new ConfigParser();
parser.parse( loader.getClass().getResource( cfgFile));
Catalog catalog= CatalogFactoryBase.getInstance().getCatalog();
//
加载
Chain
Command cmd= catalog.getCommand("CommandChain");
Context ctx= new ContextBase();
cmd.execute( ctx);
//
加载
Command
cmd= catalog.getCommand( "command4");
cmd.execute( ctx);
}
}
|
注意:使用配置文件的话,需要使用
Commons Digester
。而
Digester
则依赖:
Commons Collections
、
Commons Logging
和
Commons BeanUtils
。
3.
加载
Catalog
到
web
应用。为了在
web
应用中加载
Catalog
,需要在对应的
web.xml
中添加:
<context-param>
<param-name>org.apache.commons.chain.CONFIG_CLASS_RESOURCE</param-name>
<param-value>resources/catalog.xml</param-value>
</context-param>
<listener>
<listener-class>org.apache.commons.chain.web.ChainListener</listener-class>
</listener>
|
缺省情况下,
Catalog
会被加载到
Servlet Context
中,对应的属性名字是“
catalog
”。因此获取
Catalog
:
Catalog catalog = (Catalog) request.getSession()
.getServletContext().getAttribute("catalog");
4.
Filter
的使用。
Filter
是一种特殊的
Command
,它除了
execute
方法会被执行之外,同时还会在
Chain
执行完毕之后(不论是正常结束还是异常结束)执行
postprocess
。因此,可以将它和
Servlet
中的
Filter
做类比:
execute
相当于处理前操作(相对下一个
Command
来说),
postprocess
相当于处理后操作。
Filter
的使用以及配置和
Command
完全一样,为了在
Command1
之前添加一个
Filter
:
þ
定义
Filter
public class Filter1 implements Filter {
public boolean postprocess(Context arg0, Exception arg1) {
System.out.println("Filter1 is after done!");
return false;
}
public boolean execute(Context arg0) throws Exception {
System.out.println("Filter1 is done!");
return false;
}
}
|
þ
修改配置文件,在上述的配置文件中的
command1
之前添加:
<command id="filter1" className= "chain.Filter1"/>
Filter
的还有一个常用的用法:对于异常的过滤。当
Command
抛出异常时,最终中会返回到最开始的调用处。有时期望不抛出这些异常,而在内部消化掉,那么就可以利用
Filter
。因为
Commons Chain
确保会调用已经执行了
execute
方法的
Filter
的
postprocess
方法,即使在出现异常时也是如此。因此,对应的
postprocess
方法可以写为:
public boolean postprocess(Context arg0, Exception arg1) {
//
返回
true
,表示非空异常已被处理,无需再抛出。
//
否则,异常会被抛出
if( null!= arg1) return true;
else return false;
}
5.
对于复杂的
Chain
,可能需要使用内嵌的
Chain
,内嵌
Chain
可以类比一个子过程。此时,可以使用
LookupCommand
。以例
1
为例,假设其中的
command2
需要扩展成为一个子过程,那么配置文件修改如下:
<?xml version="1.0" encoding="UTF-8"?>
<catalog>
<chain name="CommandChain">
<command id="command1" className= "chain.Command1"/>
<command id="filter1" className= "chain.Filter1"/>
<command
className="org.apache.commons.chain.generic.LookupCommand"
name="chain_command3"
optional="true"/>
<command id="command2" className= "chain.Command2"/>
</chain>
<chain name="chain_command3">
<command id="command3" className= "chain.Command3"/>
</chain>
</catalog>
|
其中,
optional
如果设为
true
,那么如果没有找到对应的类时,程序不会抛出异常。此时,仿佛命令不存在一样。如果为
false
,那么在找不到对应的类时,会抛出异常。
6.
<define>
的使用。配置文件的引入,使得
Commons Chain
的灵活性大大的提高。在实际的使用过程中,存在着同一个
Command
被多个
Chain
使用的情形。如果每次都书写
Command
的类名,尤其是前面的包名特别长的情况下,是非常枯燥的。而
<define>
的作用就是为了解决这样的麻烦。通过定义
Command
和
Chain
的别名,来简化书写。例
5
的配置文件,可以书写成:
<?xml version="1.0" encoding="gb2312"?>
<catalog>
<!-- Command
的别名,以后直接使用即可
-->
<define name="command1" className="chain.Command1"/>
<define name="command2" className="chain.Command2"/>
<define name="command3" className="chain.Command3"/>
<define name="filter1" className="chain.Filter1"/>
<define name="lookupCommand"
className="org.apache.commons.chain.generic.LookupCommand"/>
<chain name="CommandChain">
<command1 id="1"/>
<filter1 id="2"/>
<lookupCommand name="chain_command3" optional="true"/>
<command2 id="3"/>
</chain>
<chain name="chain_command3">
<command3 id="3"/>
</chain>
<command1 name="command4"/>
</catalog>
|
总结
Commons Chain
实现了
Chain of Responsebility
和
Command
模式,其中的
Catalog +
配置文件的方式使得调用方和
Command
的实现方的耦合度大大的降低,提高了灵活性。对于配置文件,通常可以:
-
作为
Command
的索引表,需要时按名字索引创建实例。
-
利用
Chain
以及内嵌
Chain
,完成一组连续任务和
Command
的复用,引入
Filter
可以获得与
Servlet Filter
一样的好处。
-
使用
<define>
定义别名,简化书写。