Vincent

Vicent's blog
随笔 - 74, 文章 - 0, 评论 - 5, 引用 - 0
数据加载中……

Apache Commons Chain简明手册

基本对象

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> 定义别名,简化书写。

posted on 2006-08-22 10:47 Binary 阅读(2148) 评论(0)  编辑  收藏 所属分类: Apache jakarta


只有注册用户登录后才能发表评论。


网站导航: