基本对象 

 

1Command接口。它是Commons Chain中最重要的接口,表示在Chain中的具体某一步要执行的命令。它只有一个方法:boolean execute(Context context)。如果返回true,那么表示Chain的处理结束,Chain中的其他命令不会被调用;返回false,则Chain会继续调用下一个Command,直到:

        - Command返回true

        - Command抛出异常;

        - Chain的末尾;

2Context接口。它表示命令执行的上下文,在命令间实现共享信息的传递。Context接口的父接口是MapContextBase实现了Context。对于web环境,可以使用WebContext类及其子类(FacesWebContextPortletWebContextServletWebContext)。

3Chain接口。它表示命令链,要在其中执行的命令,需要先添加到Chain中。Chain的父接口是CommandChainBase实现了它。 

4Filter接口。它的父接口是Command,它是一种特殊的Command。除了Commandexecute,它还包括一个方法:boolean postprocess(Context context, Exception exception)Commons Chain会在执行了Filterexecute方法之后,执行postprocess(不论Chain以何种方式结束)。Filter的执行execute的顺序与Filter出现在Chain中出现的位置一致,但是执行postprocess顺序与之相反。如:如果连续定义了filter1filter2,那么execute的执行顺序是:filter1 -> filter2;而postprocess的执行顺序是:filter2 -> filter1

5Catalog接口。它是逻辑命名的ChainCommand集合。通过使用它,Command的调用者不需要了解具体实现Command的类名,只需要通过名字就可以获取所需要的Command实例。

 

基本使用 

1. 执行由顺序的命令组成的流程,假设这条流程包含123步。

 

package com.wgy.chain;

import org.apache.commons.chain.Command;

import org.apache.commons.chain.Context;

public class Command1 implements Command {

        @Override

        public boolean execute(Context arg0) throws Exception {

                System.out.println("Command1 has done!");

                return false;

        }

}

package com.wgy.chain;
import org.apache.commons.chain.Command;
import org.apache.commons.chain.Context;
public class Command2 implements Command {
        @Override
        public boolean execute(Context arg0) throws Exception {
                System.out.println("Command2 has done!");
                return false;
        }
}

package com.wgy.chain;

import org.apache.commons.chain.Command;

import org.apache.commons.chain.Context;

public class Command3 implements Command {

        @Override

        public boolean execute(Context arg0) throws Exception {

                System.out.println("Command3 has done!");

                return false;

        }

}

注册命令,创建执行的Chain

 

package com.wgy.chain;

import org.apache.commons.chain.Command;

import org.apache.commons.chain.Context;

import org.apache.commons.chain.impl.ChainBase;

import org.apache.commons.chain.impl.ContextBase;

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 command = new CommandChain();

                Context context = new ContextBase();

                command.execute(context);

        }

}

 

2. 使用配置文件加载Command。除了在程序中注册命令之外,还可以使用配置文件来完成。

对于例1,配置文件catalog.xml可以写成:

 

<?xml version="1.0" encoding="UTF-8"?>

<catalog>

        <chain name="CommandChain">

                <!-- 定义的顺序决定执行的顺序 -->

                <command id="command1" className="com.wgy.chain.Command1" />

                <command id="command2" className="com.wgy.chain.Command2" />

                <command id="command3" className="com.wgy.chain.Command3" />

        </chain>

        <command name="command4" className="com.wgy.chain.Command1" />

</catalog>

装入配置文件的代码如下:

 

package com.wgy.chain;

import org.apache.commons.chain.Catalog;

import org.apache.commons.chain.Command;

import org.apache.commons.chain.Context;

import org.apache.commons.chain.config.ConfigParser;

import org.apache.commons.chain.impl.CatalogFactoryBase;

import org.apache.commons.chain.impl.ContextBase;

public class CatalogLoader {

        static final String cfgFile = "catalog.xml";

        public static void main(String[] args) throws Exception {

                ConfigParser parser = new ConfigParser();

                parser.parse(CatalogLoader.class.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);

        }

}

注意:上面使用到的jar
图片 

3. 加载Catalogweb应用。为了在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

 

package com.wgy.chain;

import org.apache.commons.chain.Context;

import org.apache.commons.chain.Filter;

public class Filter1 implements Filter {

        @Override

        public boolean execute(Context arg0) throws Exception {

                System.out.println("Filter1 is done!");

                return false;

        }

        @Override

        public boolean postprocess(Context arg0, Exception arg1) {

                System.out.println("Filter1 has been done!");

                return false;

        }

}

       修改配置文件,在上述的配置文件中的command1之前添加:

     <command id="filter1" className= "com.wgy.chain.Filter1"/>

    Filter还有一个常用的用法:对于异常的过滤。当Command抛出异常时,最终会返回到最开始的调用处。有时期望不抛出这些异常,而在内部消化掉,那么就可以利用Filter。因为Commons Chain确保会调用已经执行了execute方法的Filterpostprocess方法,即使在出现异常时也是如此。因此,对应的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= "com.wgy.chain.Command1"/>

                <command id="filter1" className= "com.wgy.chain.Filter1"/>

                <command className="org.apache.commons.chain.generic.LookupCommand" 

 

                        name="chain_command3" optional="true"/>

 

                <command id="command2" className= "com.wgy.chain.Command2"/>

        </chain>

        <chain name="chain_command3">

                <command id="command3" className= "com.wgy.chain.Command3"/> 

        </chain>

</catalog>

  其中,optional如果设为true,那么未找到对应的类时,程序不会抛出异常。如果为false,那么在找不到对应的类时,会抛出异常。

6. <define>的使用。配置文件的引入,使得Commons Chain的灵活性大大的提高。在实际的使用过程中,存在着同一个Command被多个Chain使用的情形。如果每次都书写Command的类名,尤其是前面的包名特别长的情况下,是非常枯燥的。而<define>的作用就是为了解决这样的麻烦。通过定义CommandChain的别名,来简化书写。例5的配置文件,可以书写成:

<?xml version="1.0" encoding="UTF-8"?>

<catalog>

        <!-- Command的别名,以后直接使用即可 -->

        <define name="command1" className="com.wgy.chain.Command1"/>

        <define name="command2" className="com.wgy.chain.Command2"/>

 

        <define name="command3" className="com.wgy.chain.Command3"/>

 

        <define name="filter1" className="com.wgy.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 ResponsebilityCommand模式,其中的Catalog 配置文件的方式使得调用方和Command的实现方的耦合度大大的降低,提高了灵活性。对于配置文件,通常可以:

作为Command的索引表,需要时按名字索引创建实例

利用Chain以及内嵌Chain,完成一组连续任务和Command的复用,引入Filter可以获得与Servlet Filter一样的好处

使用<define>定义别名,简化书写。