文章出自:http://www.blogjava.net/lijiajia418/archive/2006/08/22/64999.html
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> 定义别名,简化书写。