基本對(duì)象
1.
?
Command
接口。它是
Commons Chain
中最重要的接口,表示在
Chain
中的具體某一步要執(zhí)行的命令。它只有一個(gè)方法:
boolean execute(Context context)
。如果返回
true
,那么表示
Chain
的處理結(jié)束,
Chain
中的其他命令不會(huì)被調(diào)用;返回
false
,則
Chain
會(huì)繼續(xù)調(diào)用下一個(gè)
Command
,直到:
-
?????????
Command
返回
true
;
-
?????????
Command
拋出異常;
-
?????????
Chain
的末尾;
2.
?
Context
接口。它表示命令執(zhí)行的上下文,在命令間實(shí)現(xiàn)共享信息的傳遞。
Context
接口的父接口是
Map
,
ContextBase
實(shí)現(xiàn)了
Context
。對(duì)于
web
環(huán)境,可以使用
WebContext
類及其子類(
FacesWebContext
、
PortletWebContext
和
ServletWebContext
)。
3.
?
Chain
接口。它表示“命令鏈”,要在其中執(zhí)行的命令,需要先添加到
Chain
中。
Chain
的父接口是
Command
,
ChainBase
實(shí)現(xiàn)了它。
4.
?
Filter
接口。它的父接口是
Command
,它是一種特殊的
Command
。除了
Command
的
execute
,它還包括一個(gè)方法:
boolean postprocess(Context context, Exception exception)
。
Commons Chain
會(huì)在執(zhí)行了
Filter
的
execute
方法之后,執(zhí)行
postprocess
(不論
Chain
以何種方式結(jié)束)。
Filter
的執(zhí)行
execute
的順序與
Filter
出現(xiàn)在
Chain
中出現(xiàn)的位置一致,但是執(zhí)行
postprocess
順序與之相反。如:如果連續(xù)定義了
filter1
和
filter2
,那么
execute
的執(zhí)行順序是:
filter1 -> filter2
;而
postprocess
的執(zhí)行順序是:
filter2 -> filter1
。
5.
?
Catalog
接口。它是邏輯命名的
Chain
和
Command
集合。通過(guò)使用它,
Command
的調(diào)用者不需要了解具體實(shí)現(xiàn)
Command
的類名,只需要通過(guò)名字就可以獲取所需要的
Command
實(shí)例。
基本使用
1.
????????
執(zhí)行由順序的命令組成的流程,假設(shè)這條流程包含
1
、
2
和
3
步。
t
???????
實(shí)現(xiàn)要執(zhí)行的命令步驟:
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;
??? }
}
|
?
t
???????
注冊(cè)命令,創(chuàng)建執(zhí)行的
Chain
:
public class CommandChain extends ChainBase {
??? //
增加命令的順序也決定了執(zhí)行命令的順序
??? 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
。除了在程序中注冊(cè)命令之外,還可以使用配置文件來(lái)完成。
t
???????
對(duì)于例
1
,配置文件可以寫成:
<?xml version="1.0" encoding="gb2312"?>
<catalog>
?????? <chain name="CommandChain">
??????? <!--
定義的順序決定執(zhí)行的順序
-->
????????????? <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>
|
t
???????
裝入配置文件的代碼如下:
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
應(yīng)用。為了在
web
應(yīng)用中加載
Catalog
,需要在對(duì)應(yīng)的
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
會(huì)被加載到
Servlet Context
中,對(duì)應(yīng)的屬性名字是“
catalog
”。因此獲取
Catalog
:
Catalog catalog = (Catalog) request.getSession()
???????
????????????????????.getServletContext().getAttribute("catalog");
4.
????????
Filter
的使用。
Filter
是一種特殊的
Command
,它除了
execute
方法會(huì)被執(zhí)行之外,同時(shí)還會(huì)在
Chain
執(zhí)行完畢之后(不論是正常結(jié)束還是異常結(jié)束)執(zhí)行
postprocess
。因此,可以將它和
Servlet
中的
Filter
做類比:
execute
相當(dāng)于處理前操作(相對(duì)下一個(gè)
Command
來(lái)說(shuō)),
postprocess
相當(dāng)于處理后操作。
Filter
的使用以及配置和
Command
完全一樣,為了在
Command1
之前添加一個(gè)
Filter
:
t
???????
定義
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;
??? }
}
|
?
t
???????
修改配置文件,在上述的配置文件中的
command1
之前添加:
<command id="filter1" className= "chain.Filter1"/>
?????? Filter
的還有一個(gè)常用的用法:對(duì)于異常的過(guò)濾。當(dāng)
Command
拋出異常時(shí),最終中會(huì)返回到最開(kāi)始的調(diào)用處。有時(shí)期望不拋出這些異常,而在內(nèi)部消化掉,那么就可以利用
Filter
。因?yàn)?/span>
Commons Chain
確保會(huì)調(diào)用已經(jīng)執(zhí)行了
execute
方法的
Filter
的
postprocess
方法,即使在出現(xiàn)異常時(shí)也是如此。因此,對(duì)應(yīng)的
postprocess
方法可以寫為:
?????? public boolean postprocess(Context arg0, Exception arg1) {
??????? //
返回
true
,表示非空異常已被處理,無(wú)需再拋出。
??????? //
否則,異常會(huì)被拋出
??????? if( null!= arg1) return true;
??????? else return false;
??? }
5.
????????
對(duì)于復(fù)雜的
Chain
,可能需要使用內(nèi)嵌的
Chain
,內(nèi)嵌
Chain
可以類比一個(gè)子過(guò)程。此時(shí),可以使用
LookupCommand
。以例
1
為例,假設(shè)其中的
command2
需要擴(kuò)展成為一個(gè)子過(guò)程,那么配置文件修改如下:
<?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
如果設(shè)為
true
,那么如果沒(méi)有找到對(duì)應(yīng)的類時(shí),程序不會(huì)拋出異常。此時(shí),仿佛命令不存在一樣。如果為
false
,那么在找不到對(duì)應(yīng)的類時(shí),會(huì)拋出異常。
6.
????????
<define>
的使用。配置文件的引入,使得
Commons Chain
的靈活性大大的提高。在實(shí)際的使用過(guò)程中,存在著同一個(gè)
Command
被多個(gè)
Chain
使用的情形。如果每次都書寫
Command
的類名,尤其是前面的包名特別長(zhǎng)的情況下,是非常枯燥的。而
<define>
的作用就是為了解決這樣的麻煩。通過(guò)定義
Command
和
Chain
的別名,來(lái)簡(jiǎn)化書寫。例
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>
|
?
總結(jié)
?????? Commons Chain
實(shí)現(xiàn)了
Chain of Responsebility
和
Command
模式,其中的
Catalog +
配置文件的方式使得調(diào)用方和
Command
的實(shí)現(xiàn)方的耦合度大大的降低,提高了靈活性。對(duì)于配置文件,通常可以:
-
?????????
作為
Command
的索引表,需要時(shí)按名字索引創(chuàng)建實(shí)例。
-
?????????
利用
Chain
以及內(nèi)嵌
Chain
,完成一組連續(xù)任務(wù)和
Command
的復(fù)用,引入
Filter
可以獲得與
Servlet Filter
一樣的好處。
-
?????????
使用
<define>
定義別名,簡(jiǎn)化書寫。