Commons-chain:责任链框架

    Commons-chain是apache commons中的一个子项目,主要被使用在"责任链"(执行链)的场景中,struts中action的调用过程,就是使用了"chain"框架做支撑.如果你的项目中,也有基于此种场景的需求,可以考虑使用它.

    所谓"责任链"就是:一系列有序的command能够按照顺序执行,并能够互相交换或者传递执行结果;和我们常说的"责任链"模式类似.

    commons-chain通过被用在web项目中,或者在基于"规则引擎"的其他模块中,commons-chain本身基于commons-digester作为"规则"的解析引擎,使用起来非常简单.

 

一.API简介:

    1) Command: 顶级接口,一个可执行的"指令",多个command组成"责任链".只有一个方法:

  • boolean execute(Context context):当"责任链"开始调用时,将会依次执行链上的所有Command对象的execute方法,直到结束,或者抛出异常,或者某个command返回true终止调用.context对象表示当前"责任链"的上下文信息,它可以用来保存一些临时变量.此方法如果返回false,表示继续责任链将会继续执行后续command,如果返回true,则表示终止调用,后续的command将不会被执行.

    2) Chain: 顶级接口,表示组织多个Command,形成一个有序的"责任链",它保持了一个command的集合(数组),Chain接口本身也扩展了Command接口,它的实现类为ChainBase,我们通过会继承它来实现一些自定义的操作.

  • void addCommand(Command command):向chain中添加Command节点.责任链中的command顺序将根据其被添加的时间有关.当Chain.execute()被执行时,将会依次执行链中的command.
  • boolean execute(Context context): 依次调用当前chain中的所有command,直到结束,或者抛出异常,或者某个command返回true..command抛出的异常将会被捕获,并交给Chain中的Filter处理,如果Filter未能处理,异常将会被重新抛出.需要提醒,Chain中可以包括Filter组件,Filter的执行顺序为倒序.

    3) Filter: 顶级接口,"过滤器",它本身也扩展了Command接口,能够和Command一样添加到Chain中,需要注意的是Filter.execute方法,仍然会像其他Command一样被"依次"执行;不过Filter中还提供了一个非常有用的方法:

  • boolean postprocess(Context context, Exception exception) : 在Chain执行完Command之后(包括Filter.execute),将会从链的末端倒序依次执行所有的Filter.postprocess()方法,Filter中如果抛出异常将会被忽略;此方法的设计初衷为"清理Context"中参数或者执行结果,或者重置执行结果,如果在Command中存在创建"资源消耗"的对象(比如数据源连接,比如线程资源等),可以在Filter中考虑释放这些资源.当所有的Filter执行完成之后,Context中保持的数据,应该是可靠的.

    4) Context:类似于"session",用来保存当前Chain中需要被保持的"变量参数"或者"Command执行的结果";Context内部存储结构为一个Map,它的数据可以被Chain中的Command操作;Context并非为线程安全的,也不期望此实例能够被重用;每次Chain被调用,都应该创建新的Context实例;其实现类为ContextBase.

    5) Catalog:顶级接口,用来保存Chains列表,一个Catalog可以有多个Chain组成,可以通过"name"的方式获取当前Catalog中的chain.

 

二.代码样例:

    1) 环境:commons-chain-1.2.jar,commons-digester-1.7.0.jar

    2) catalog.xml: 位于/src/main/resources目录,我们可以使用digester引擎来解析此xml文件并获得catalog对象列表:

<?xml version="1.0" ?>
<!-- @see ConfigRuleSet.java -->
<catalog>
	<chain name="printChain">
    	<command name="printDate" className="com.test.chain.PrintDateCommand" />
    	<command name="printTimestamp" className="com.test.chain.PrintTimestampCommand" />
    </chain>
</catalog>
<!-- you can have more catalogs,but everyone should have a unique name -->
<!--  
<catalog name="printCatalog">
	<chain name="test">
    	<command name="printDate" className="com.test.chain.PrintDateCommand" />
    	<command name="printTimestamp" className="com.test.chain.PrintTimestampCommand" />
    </chain>
</catalog>
-->

    每个catalog元素,都可以指定"name"属性,不过name在全局应该是唯一的,上述xml结构为全限定名的样例.

 

    3) 程序样例:

//ConfigRuleSet中定义了"规则"
ConfigParser configParser = new ConfigParser();
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
//位于classpath位置
URL url = classLoader.getResource("catalog.xml");
configParser.parse(url);
//获得默认的catalog
//其中CatalogFactoryBase是单例模式,在parse是会触发CatalogFactory的实例化
Catalog catalog = CatalogFactoryBase.getInstance().getCatalog();
Command chain = catalog.getCommand("printChain");
Context context = new ContextBase();//A session map,can be useed by all commands
//当前chain中,所有的command依次执行,直到结束,或者其中一个command返回true,或者抛出异常
chain.execute(context);

 

三.Chain与servlet

    1) web.xml

<context-param>
  <param-name>org.apache.commons.chain.CONFIG_CLASS_RESOURCE</param-name>
  <!-- 注意自己的classpath配置-->
  <!-- 其他param,请参见ChainLisntener类 -->
  <param-value>catalog.xml</param-value>
</context-param>
<listener>
  <listener-class>org.apache.commons.chain.web.ChainListener</listener-class>
</listener>

    2) 获取Catalog:

//从ServletContext中获取Catalog,请注意并发环境下使用
//catalog,chain,command,filter的设计需要考虑到"并发"操作
Catalog catalog = (Catalog) request.getSession().getServletContext().getAttribute("catalog");
Command command = catalog.getCommand("printCatalog");
try{  
    Context context = new ContextBase();
	//context.put("dataSource",dataSource);//
	boolean stop = command.execute(context);
	if(stop){
		//如果某个Command执行异常,你可以尝试将"异常信息"放入context中,以便在此处处理
		//Throwable exception = context.get("exception");
		//...
	}
	//你应该把command的执行结果放在context中,以便在此处获取
	//Object result = context.get("result");
	if(result)
}catch(Exception e){
	//please check.
}

 

 --END--

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值