- 浏览: 611611 次
- 性别:
- 来自: 成都
文章分类
最新评论
-
niuqiang2008:
谢了谢了 就是要找这个问题
struts2的<s:set><s:if>标签应用 -
love_zongming:
赞一个! 谢谢分享
向eclipse中添加user library的方式 -
MyPointOne:
后面的异常为什么出现?好像没说清楚!
Jboss中的web-console与jmx-console的配置 -
nysin:
顶一个,现在明白了...
JAVA静态和非静态内部类 -
chenhua_1984:
很不错!
Cause: net.sf.cglib.beans.BulkBeanException异常
关键字: chain
使用Commons Chain
作为程序开发人员,我们经常需要对一个实际上程序性的系统应用面向对象的方法。商业分析家和管理人员描述这样的系统时通常不使用类层次和序列图,而是使用流程图和工作流图表。但是不论如何,使用面向对象的方法解决这些问题时会带来更多的灵活性。面向对象的设计模式提供了有用的结构和行为来描述这种顺序的处理,比如模版方法(Template Method)[GoF]和责任链(Chain of Responsibility)[GoF]。
Jakarta Commons的子项目Chain将上述两个模式组合成一个可复用的Java框架用于描述顺序的处理流程。这个在Jakarta Commons project社区中开发的框架,已经被广泛的接受并且使用于许多有趣的应用中,特别的是他被Struts和Shale应用框架作为处理HTTP请求处理的基础机制。你可以在需要定义和执行一组连续的步骤时使用Commons Chain。
至于经典设计模式,开发者和架构师普遍使用模版方法(Template Method)造型顺序处理。模版方法(Template Method)中使用一个抽象的父类定义使用的算法:处理的步骤,具体实现交给子类。当然,父类也可以为算法所使用的方法提供一个缺省实现。
由于模版方法(Template Method)依赖继承——子类必须继承定义了算法的父类——因此使用这个模式的软件表现出紧耦合而且缺少灵活性。又由于实现类添加自己的行为前必须扩展父类,沟每⑷嗽北幌拗朴诶嗖愦沃校佣拗屏顺绦蛏杓频牧榛钚浴ommons Chain使用配置文件定义算法,在程序运行时解析配置文件,从而很好的解决了这个问题。
现在来看一下Commons Chain是怎样工作的,我们从一个人造的例子开始:二手车销售员的商业流程。下面是销售流程的步骤:
得到用户信息
试车
谈判销售
安排财务
结束销售
现在假设使用模版方法(Template Method)造型这个流程。首先建立一个定义了算法的抽象类:
Java代码
public abstract class SellVehicleTemplate {
public void sellVehicle() {
getCustomerInfo();
testDriveVehicle();
negotiateSale();
arrangeFinancing();
closeSale();
}
public abstract void getCustomerInfo();
public abstract void testDriveVehicle();
public abstract void negotiateSale();
public abstract void arrangeFinancing();
public abstract void closeSale();
}
public abstract class SellVehicleTemplate {
public void sellVehicle() {
getCustomerInfo();
testDriveVehicle();
negotiateSale();
arrangeFinancing();
closeSale();
}
public abstract void getCustomerInfo();
public abstract void testDriveVehicle();
public abstract void negotiateSale();
public abstract void arrangeFinancing();
public abstract void closeSale();
}
现在来看一下怎样用Commons Chain实现这个流程。首先,下载Commons Chain。你可以直接下载最新的zip或tar文件,也可以从CVS或者SubVersion源码库检出Commons Chain模块得到最新的代码。解压缩打包文件,将commons-chain.jar放入你的classpath中。
使用Commons Chain实现这个商业流程,必须将流程中的每一步写成一个类,这个类需要有一个public的方法execute()。这和传统的命令模式(Command pattern)实现相同。下面简单实现了“得到用户信息”:
Java代码
package com.jadecove.chain.sample;
import org.apache.commons.chain.Command;
import org.apache.commons.chain.Context;
public class GetCustomerInfo implements Command {
public boolean execute(Context ctx) throws Exception
System.out.println("Get customer info");
ctx.put("customerName","George Burdell");
return false;
}
}
package com.jadecove.chain.sample;
import org.apache.commons.chain.Command;
import org.apache.commons.chain.Context;
public class GetCustomerInfo implements Command {
public boolean execute(Context ctx) throws Exception
System.out.println("Get customer info");
ctx.put("customerName","George Burdell");
return false;
}
}
由于只是演示,这个类并没有做很多工作。这里将用户名放入了Context对象ctx中。这个Context对象连接了各个命令。暂时先将这个对象想象成根据关键字存取值的哈希表。所有后来的命令可以通过它访问刚才放入的用户名。TestDriveVehicle,NegotiateSale和 ArrangeFinancing命令的实现只是简单的打印了将执行什么操作。
Java代码
package com.jadecove.chain.sample;
import org.apache.commons.chain.Command;
import org.apache.commons.chain.Context;
public class TestDriveVehicle implements Command {
public boolean execute(Context ctx) throws Exception {
System.out.println("Test drive the vehicle");
return false;
}
}
public class NegotiateSale implements Command {
public boolean execute(Context ctx) throws Exception {
System.out.println("Negotiate sale");
return false;
}
}
public class ArrangeFinancing implements Command {
public boolean execute(Context ctx) throws Exception {
System.out.println("Arrange financing");
return false;
}
}
package com.jadecove.chain.sample;
import org.apache.commons.chain.Command;
import org.apache.commons.chain.Context;
public class TestDriveVehicle implements Command {
public boolean execute(Context ctx) throws Exception {
System.out.println("Test drive the vehicle");
return false;
}
}
public class NegotiateSale implements Command {
public boolean execute(Context ctx) throws Exception {
System.out.println("Negotiate sale");
return false;
}
}
public class ArrangeFinancing implements Command {
public boolean execute(Context ctx) throws Exception {
System.out.println("Arrange financing");
return false;
}
}
CloseSale从Context对象中取出GetCustomerInfo放入的用户名,并将其打印。
Java代码
package com.jadecove.chain.sample;
import org.apache.commons.chain.Command;
import org.apache.commons.chain.Context;
public class CloseSale implements Command {
public boolean execute(Context ctx) throws Exception {
System.out.println("Congratulations " + ctx.get("customerName") +", you bought a new car!");
return false;
}
}
package com.jadecove.chain.sample;
import org.apache.commons.chain.Command;
import org.apache.commons.chain.Context;
public class CloseSale implements Command {
public boolean execute(Context ctx) throws Exception {
System.out.println("Congratulations " + ctx.get("customerName") +", you bought a new car!");
return false;
}
}
现在你可以将这个流程定义成一个序列(或者说“命令链”)。
Java代码
package com.jadecove.chain.sample;
import org.apache.commons.chain.impl.ChainBase;
import org.apache.commons.chain.Command;
import org.apache.commons.chain.Context;
import org.apache.commons.chain.impl.ContextBase;
public class SellVehicleChain extends ChainBase {
public SellVehicleChain() {
super();
addCommand(new GetCustomerInfo());
addCommand(new TestDriveVehicle());
addCommand(new NegotiateSale());
addCommand(new ArrangeFinancing());
addCommand(new CloseSale());
}
public static void main(String[] args) throws Exception {
Command process = new SellVehicleChain();
Context ctx = new ContextBase();
process.execute(ctx);
}
}
package com.jadecove.chain.sample;
import org.apache.commons.chain.impl.ChainBase;
import org.apache.commons.chain.Command;
import org.apache.commons.chain.Context;
import org.apache.commons.chain.impl.ContextBase;
public class SellVehicleChain extends ChainBase {
public SellVehicleChain() {
super();
addCommand(new GetCustomerInfo());
addCommand(new TestDriveVehicle());
addCommand(new NegotiateSale());
addCommand(new ArrangeFinancing());
addCommand(new CloseSale());
}
public static void main(String[] args) throws Exception {
Command process = new SellVehicleChain();
Context ctx = new ContextBase();
process.execute(ctx);
}
}
运行这个类将会输出以下结果:
Get customer info
Test drive the vehicle
Negotiate sale
Arrange financing
Congratulations George Burdell, you bought a new car!
在进一步深入之前,让我们来看一下我们使用了的Commons Chain的类和接口。
Command 类和Chain类的关系就是组合模式(Composite pattern)[GoF]的例子:Chain不仅由多个Command组成,而且自己也是Command。这使你可以非常简单得将单个命令(Command)替换成由多个命令(Command)组成的链(Chain)。这个由Command对象唯一操作定义的方法代表了一个直接的命令:
public boolean execute(Context context);
参数context仅仅是一个存放了名称-值对的集合。接口Context在这里作为一个标记接口:它扩展了java.util.Map但是没有添加任何特殊的行为。于此相反,类ContextBase不仅提供了对Map的实现而且增加了一个特性:属性-域透明。这个特性可以通过使用Map的put和get 方法操作JavaBean的域,当然这些域必须使用标准的getFoo和setFoo方法定义。那些通过JavaBean的“setter”方法设置的值,可以通过对应的域名称,用Map的get方法得到。同样,那些用Map的put方法设置的值可以通过JavaBean的“getter”方法得到。
例如,我们可以创建一个专门的context提供显式的customerName属性支持。
Java代码
package com.jadecove.chain.sample;
import org.apache.commons.chain.impl.ContextBase;
public class SellVehicleContext extends ContextBase {
private String customerName;
public String getCustomerName() {
return customerName;
}
public void setCustomerName(String name) {
this.customerName = name;
}
}
package com.jadecove.chain.sample;
import org.apache.commons.chain.impl.ContextBase;
public class SellVehicleContext extends ContextBase {
private String customerName;
public String getCustomerName() {
return customerName;
}
public void setCustomerName(String name) {
this.customerName = name;
}
}
现在你既可以进行Map的一般属性存取操作同时也可以使用显式的JavaBean的访问和修改域的方法,这两个将产生同样的效果。但是首先你需要在运行SellVehicleChain时实例化SellVehiceContext而不是ContextBase。
Java代码
public static void main(String[] args) throws Exception {
Command process = new SellVehicleChain();
Context ctx = new SellVehicleContext();
process.execute(ctx);
}
public static void main(String[] args) throws Exception {
Command process = new SellVehicleChain();
Context ctx = new SellVehicleContext();
process.execute(ctx);
}
尽管你不改变GetCustomerInfo中存放用户名的方法——仍然使用ctx.put("customerName", "George Burdell")——你可以在CloseSale中使用getCustomerName()方法得到用户名。
Java代码
public boolean execute(Context ctx) throws Exception {
SellVehicleContext myCtx = (SellVehicleContext) ctx;
System.out.println("Congratulations " + myCtx.getCustomerName() + ", you bought a new car!");
return false;
}
public boolean execute(Context ctx) throws Exception {
SellVehicleContext myCtx = (SellVehicleContext) ctx;
System.out.println("Congratulations " + myCtx.getCustomerName() + ", you bought a new car!");
return false;
}
那些依赖类型安全和context的显式域的命令(Command)可以利用标准的getter和setter方法。当一些新的命令(Command)被添加时,它们可以不用考虑context的具体实现,直接通过Map的get和put操作属性。不论采用何种机制,ContextBase类都可以保证命令(Command)间可以通过context互操作。
下面这个例子展示了如何使用Commons Chain的API建立并执行一组顺序的命令。当然,和现在大多数Java软件一样,Commons Chain可以使用XML文件作为配置文件。你可以将“汽车销售”流程的步骤在XML文件中定义。这个文件有个规范的命名chain- config.xml。
Xml代码
<catalog>
<chain name="sell-vehicle">
<command id="GetCustomerInfo" className="com.jadecove.chain.sample.GetCustomerInfo"/>
<command id="TestDriveVehicle" className="com.jadecove.chain.sample.TestDriveVehicle"/>
<command id="NegotiateSale" className="com.jadecove.chain.sample.NegotiateSale"/>
<command id="ArrangeFinancing" className="com.jadecove.chain.sample.ArrangeFinancing"/>
<command id="CloseSale" className="com.jadecove.chain.sample.CloseSale"/>
</chain>
</catalog>
<catalog>
<chain name="sell-vehicle">
<command id="GetCustomerInfo" className="com.jadecove.chain.sample.GetCustomerInfo"/>
<command id="TestDriveVehicle" className="com.jadecove.chain.sample.TestDriveVehicle"/>
<command id="NegotiateSale" className="com.jadecove.chain.sample.NegotiateSale"/>
<command id="ArrangeFinancing" className="com.jadecove.chain.sample.ArrangeFinancing"/>
<command id="CloseSale" className="com.jadecove.chain.sample.CloseSale"/>
</chain>
</catalog>
Chain的配置文件可以包含多个链定义,这些链定义可以集合进不同的编目中。在这个例子中,链定义在一个默认的编目中定义。事实上,你可以在这个文件中定义多个名字的编目,每个编目可拥有自己的链组。
现在你可以使用Commons Chain提供的类载入编目并得到指定的链,而不用像SellVehicleChain中那样自己在程序中定义一组命令:
Java代码
package com.jadecove.chain.sample;
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;
public class CatalogLoader {
private static final String CONFIG_FILE = "/com/jadecove/chain/sample/chain-config.xml";
private ConfigParser parser;
private Catalog catalog;
public CatalogLoader() {
parser = new ConfigParser();
}
public Catalog getCatalog() throws Exception {
if (catalog == null) {
parser.parse(this.getClass().getResource(CONFIG_FILE));
}
catalog = CatalogFactoryBase.getInstance().getCatalog();
return catalog;
}
public static void main(String[] args) throws Exception {
CatalogLoader loader = new CatalogLoader();
Catalog sampleCatalog = loader.getCatalog();
Command command = sampleCatalog.getCommand("sell-vehicle");
Context ctx = new SellVehicleContext();
command.execute(ctx);
}
}
package com.jadecove.chain.sample;
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;
public class CatalogLoader {
private static final String CONFIG_FILE = "/com/jadecove/chain/sample/chain-config.xml";
private ConfigParser parser;
private Catalog catalog;
public CatalogLoader() {
parser = new ConfigParser();
}
public Catalog getCatalog() throws Exception {
if (catalog == null) {
parser.parse(this.getClass().getResource(CONFIG_FILE));
}
catalog = CatalogFactoryBase.getInstance().getCatalog();
return catalog;
}
public static void main(String[] args) throws Exception {
CatalogLoader loader = new CatalogLoader();
Catalog sampleCatalog = loader.getCatalog();
Command command = sampleCatalog.getCommand("sell-vehicle");
Context ctx = new SellVehicleContext();
command.execute(ctx);
}
}
Chain 使用Commons Digester来读取和解析配置文件。因此你需要将Commons Digester.jar加入classpath中。我使用了1.6版本并且工作得很好。Digester使用了Commons Collectios(我使用的版本是3.1),Commons Logging(版本1.0.4),Commons BeanUtils(1.7.0),因此你也需要将它们的jar文件加入classpath中。在加入这些jar后,CatalogLoader就可以被编译和运行,它的输出和另外两个测试完全相同。
现在你可以在XML文件中定义链,并可以在程序中得到这个链(别忘了链也是命令),这样扩展的可能性和程序的灵活性可以说是无限的。假设过程“安排财务”实际上由一个完全分离的商业部门处理。这个部门希望为这种销售建立自己的工作流程。 Chain提供了嵌套链来实现这个要求。因为链本身就是命令,因此你可以用指向另一个链的引用替换一个单一用途的命令。下面是增加了新流程的链的定义:
Xml代码
<catalog name="auto-sales">
<chain name="sell-vehicle">
<command id="GetCustomerInfo" className="com.jadecove.chain.sample.GetCustomerInfo"/>
<command id="TestDriveVehicle" className="com.jadecove.chain.sample.TestDriveVehicle"/>
<command id="NegotiateSale" className="com.jadecove.chain.sample.NegotiateSale"/>
<command className="org.apache.commons.chain.generic.LookupCommand" catalogName="auto-sales" name="arrange-financing" optional="true"/>
<command id="CloseSale" className="com.jadecove.chain.sample.CloseSale"/>
</chain>
<chain name="arrange-financing">
<command id="ArrangeFinancing" className="com.jadecove.chain.sample.ArrangeFinancing"/>
</chain>
</catalog>
<catalog name="auto-sales">
<chain name="sell-vehicle">
<command id="GetCustomerInfo" className="com.jadecove.chain.sample.GetCustomerInfo"/>
<command id="TestDriveVehicle" className="com.jadecove.chain.sample.TestDriveVehicle"/>
<command id="NegotiateSale" className="com.jadecove.chain.sample.NegotiateSale"/>
<command className="org.apache.commons.chain.generic.LookupCommand" catalogName="auto-sales" name="arrange-financing" optional="true"/>
<command id="CloseSale" className="com.jadecove.chain.sample.CloseSale"/>
</chain>
<chain name="arrange-financing">
<command id="ArrangeFinancing" className="com.jadecove.chain.sample.ArrangeFinancing"/>
</chain>
</catalog>
Commons Chain提供了一个常用的命令LookupCommand来查找和执行另一个链。属性optional用于控制当指定的嵌套链没有找到时如何处理。 optional=true时,即使链没找到,处理也会继续。反之,LookupCommand将抛出 IllegalArgumentException,告知指定的命令未找到。
在下面三种情况下,命令链将结束:
命令的execute方法返回true
运行到了链的尽头
命令抛出异常
当链完全处理完一个过程后,命令就返回true。这是责任链模式(Chain of Responsibility)的基本概念。处理从一个命令传递到另一个命令,直到某个命令(Command)处理了这个命令。如果在到达命令序列尽头时仍没有处理返回true,也假设链已经正常结束。
当有命令抛出错误时链就会非正常结束。在Commons Chain中,如果有命令抛出错误,链的执行就会中断。不论是运行时错误(runtime exception)还是应用错误(application exception),都会抛出给链的调用者。但是许多应用都需要对在命令之外定义的错误做明确的处理。Commons Chain提供了Filter接口来满足这个要求。Filter继承了Command,添加了一个名为postprocess的方法。
public boolean postprocess(Context context, Exception exception);
只要Filter的execute方法被调用,不论链的执行过程中是否抛出错误,Commons Chain都将保证Filter的postprocess方法被调用。和servlet的过滤器(filter)相同,Commons Chain的Filter按它们在链中的顺序依次执行。同样,Filter的postprocess方法按倒序执行。你可以使用这个特性实现自己的错误处理。下面是一个用于处理我们例子中的错误的Filter:
Java代码
package com.jadecove.chain.sample;
import org.apache.commons.chain.Context;
import org.apache.commons.chain.Filter;
public class SellVehicleExceptionHandler implements Filter {
public boolean execute(Context context) throws Exception {
System.out.println("Filter.execute() called.");
return false;
}
public boolean postprocess(Context context, Exception exception) {
if (exception == null)
return false;
System.out.println("Exception " + exception.getMessage() + " occurred.");
return true;
}
}
package com.jadecove.chain.sample;
import org.apache.commons.chain.Context;
import org.apache.commons.chain.Filter;
public class SellVehicleExceptionHandler implements Filter {
public boolean execute(Context context) throws Exception {
System.out.println("Filter.execute() called.");
return false;
}
public boolean postprocess(Context context, Exception exception) {
if (exception == null)
return false;
System.out.println("Exception " + exception.getMessage() + " occurred.");
return true;
}
}
Filter在配置文件中的定义就和普通的命令(Command)定义相同:
Xml代码
<chain name="sell-vehicle">
<command id="ExceptionHandler" className = "com.jadecove.chain.sample.SellVehicleExceptionHandler"/>
<command id="GetCustomerInfo" className="com.jadecove.chain.sample.GetCustomerInfo"/>
<chain name="sell-vehicle">
<command id="ExceptionHandler" className = "com.jadecove.chain.sample.SellVehicleExceptionHandler"/>
<command id="GetCustomerInfo" className="com.jadecove.chain.sample.GetCustomerInfo"/>
Filter 的execute方法按定义的序列调用。然而,它的postprocess方法将在链执行完毕或抛出错误后执行。当一个错误被抛出时, postprocess方法处理完后会返回true,表示错误处理已经完成。链的执行并不会就此结束,但是本质上来说这个错误被捕捉而且不会再向外抛出。如果postprocess方法返回false,那错误会继续向外抛出,然后链就会非正常结束。
让我们假设ArrangeFinancing因为用户信用卡损坏抛出错误。SellVehicleExceptionHandler就能捕捉到这个错误,程序输出如下:
Filter.execute() called.
Get customer info
Test drive the vehicle
Negotiate sale
Exception Bad credit occurred.
结合了过滤器(filter)和子链技术后,你就可以造型很复杂的工作流程。
使用Commons Chain
作为程序开发人员,我们经常需要对一个实际上程序性的系统应用面向对象的方法。商业分析家和管理人员描述这样的系统时通常不使用类层次和序列图,而是使用流程图和工作流图表。但是不论如何,使用面向对象的方法解决这些问题时会带来更多的灵活性。面向对象的设计模式提供了有用的结构和行为来描述这种顺序的处理,比如模版方法(Template Method)[GoF]和责任链(Chain of Responsibility)[GoF]。
Jakarta Commons的子项目Chain将上述两个模式组合成一个可复用的Java框架用于描述顺序的处理流程。这个在Jakarta Commons project社区中开发的框架,已经被广泛的接受并且使用于许多有趣的应用中,特别的是他被Struts和Shale应用框架作为处理HTTP请求处理的基础机制。你可以在需要定义和执行一组连续的步骤时使用Commons Chain。
至于经典设计模式,开发者和架构师普遍使用模版方法(Template Method)造型顺序处理。模版方法(Template Method)中使用一个抽象的父类定义使用的算法:处理的步骤,具体实现交给子类。当然,父类也可以为算法所使用的方法提供一个缺省实现。
由于模版方法(Template Method)依赖继承——子类必须继承定义了算法的父类——因此使用这个模式的软件表现出紧耦合而且缺少灵活性。又由于实现类添加自己的行为前必须扩展父类,沟每⑷嗽北幌拗朴诶嗖愦沃校佣拗屏顺绦蛏杓频牧榛钚浴ommons Chain使用配置文件定义算法,在程序运行时解析配置文件,从而很好的解决了这个问题。
现在来看一下Commons Chain是怎样工作的,我们从一个人造的例子开始:二手车销售员的商业流程。下面是销售流程的步骤:
得到用户信息
试车
谈判销售
安排财务
结束销售
现在假设使用模版方法(Template Method)造型这个流程。首先建立一个定义了算法的抽象类:
Java代码
public abstract class SellVehicleTemplate {
public void sellVehicle() {
getCustomerInfo();
testDriveVehicle();
negotiateSale();
arrangeFinancing();
closeSale();
}
public abstract void getCustomerInfo();
public abstract void testDriveVehicle();
public abstract void negotiateSale();
public abstract void arrangeFinancing();
public abstract void closeSale();
}
public abstract class SellVehicleTemplate {
public void sellVehicle() {
getCustomerInfo();
testDriveVehicle();
negotiateSale();
arrangeFinancing();
closeSale();
}
public abstract void getCustomerInfo();
public abstract void testDriveVehicle();
public abstract void negotiateSale();
public abstract void arrangeFinancing();
public abstract void closeSale();
}
现在来看一下怎样用Commons Chain实现这个流程。首先,下载Commons Chain。你可以直接下载最新的zip或tar文件,也可以从CVS或者SubVersion源码库检出Commons Chain模块得到最新的代码。解压缩打包文件,将commons-chain.jar放入你的classpath中。
使用Commons Chain实现这个商业流程,必须将流程中的每一步写成一个类,这个类需要有一个public的方法execute()。这和传统的命令模式(Command pattern)实现相同。下面简单实现了“得到用户信息”:
Java代码
package com.jadecove.chain.sample;
import org.apache.commons.chain.Command;
import org.apache.commons.chain.Context;
public class GetCustomerInfo implements Command {
public boolean execute(Context ctx) throws Exception
System.out.println("Get customer info");
ctx.put("customerName","George Burdell");
return false;
}
}
package com.jadecove.chain.sample;
import org.apache.commons.chain.Command;
import org.apache.commons.chain.Context;
public class GetCustomerInfo implements Command {
public boolean execute(Context ctx) throws Exception
System.out.println("Get customer info");
ctx.put("customerName","George Burdell");
return false;
}
}
由于只是演示,这个类并没有做很多工作。这里将用户名放入了Context对象ctx中。这个Context对象连接了各个命令。暂时先将这个对象想象成根据关键字存取值的哈希表。所有后来的命令可以通过它访问刚才放入的用户名。TestDriveVehicle,NegotiateSale和 ArrangeFinancing命令的实现只是简单的打印了将执行什么操作。
Java代码
package com.jadecove.chain.sample;
import org.apache.commons.chain.Command;
import org.apache.commons.chain.Context;
public class TestDriveVehicle implements Command {
public boolean execute(Context ctx) throws Exception {
System.out.println("Test drive the vehicle");
return false;
}
}
public class NegotiateSale implements Command {
public boolean execute(Context ctx) throws Exception {
System.out.println("Negotiate sale");
return false;
}
}
public class ArrangeFinancing implements Command {
public boolean execute(Context ctx) throws Exception {
System.out.println("Arrange financing");
return false;
}
}
package com.jadecove.chain.sample;
import org.apache.commons.chain.Command;
import org.apache.commons.chain.Context;
public class TestDriveVehicle implements Command {
public boolean execute(Context ctx) throws Exception {
System.out.println("Test drive the vehicle");
return false;
}
}
public class NegotiateSale implements Command {
public boolean execute(Context ctx) throws Exception {
System.out.println("Negotiate sale");
return false;
}
}
public class ArrangeFinancing implements Command {
public boolean execute(Context ctx) throws Exception {
System.out.println("Arrange financing");
return false;
}
}
CloseSale从Context对象中取出GetCustomerInfo放入的用户名,并将其打印。
Java代码
package com.jadecove.chain.sample;
import org.apache.commons.chain.Command;
import org.apache.commons.chain.Context;
public class CloseSale implements Command {
public boolean execute(Context ctx) throws Exception {
System.out.println("Congratulations " + ctx.get("customerName") +", you bought a new car!");
return false;
}
}
package com.jadecove.chain.sample;
import org.apache.commons.chain.Command;
import org.apache.commons.chain.Context;
public class CloseSale implements Command {
public boolean execute(Context ctx) throws Exception {
System.out.println("Congratulations " + ctx.get("customerName") +", you bought a new car!");
return false;
}
}
现在你可以将这个流程定义成一个序列(或者说“命令链”)。
Java代码
package com.jadecove.chain.sample;
import org.apache.commons.chain.impl.ChainBase;
import org.apache.commons.chain.Command;
import org.apache.commons.chain.Context;
import org.apache.commons.chain.impl.ContextBase;
public class SellVehicleChain extends ChainBase {
public SellVehicleChain() {
super();
addCommand(new GetCustomerInfo());
addCommand(new TestDriveVehicle());
addCommand(new NegotiateSale());
addCommand(new ArrangeFinancing());
addCommand(new CloseSale());
}
public static void main(String[] args) throws Exception {
Command process = new SellVehicleChain();
Context ctx = new ContextBase();
process.execute(ctx);
}
}
package com.jadecove.chain.sample;
import org.apache.commons.chain.impl.ChainBase;
import org.apache.commons.chain.Command;
import org.apache.commons.chain.Context;
import org.apache.commons.chain.impl.ContextBase;
public class SellVehicleChain extends ChainBase {
public SellVehicleChain() {
super();
addCommand(new GetCustomerInfo());
addCommand(new TestDriveVehicle());
addCommand(new NegotiateSale());
addCommand(new ArrangeFinancing());
addCommand(new CloseSale());
}
public static void main(String[] args) throws Exception {
Command process = new SellVehicleChain();
Context ctx = new ContextBase();
process.execute(ctx);
}
}
运行这个类将会输出以下结果:
Get customer info
Test drive the vehicle
Negotiate sale
Arrange financing
Congratulations George Burdell, you bought a new car!
在进一步深入之前,让我们来看一下我们使用了的Commons Chain的类和接口。
Command 类和Chain类的关系就是组合模式(Composite pattern)[GoF]的例子:Chain不仅由多个Command组成,而且自己也是Command。这使你可以非常简单得将单个命令(Command)替换成由多个命令(Command)组成的链(Chain)。这个由Command对象唯一操作定义的方法代表了一个直接的命令:
public boolean execute(Context context);
参数context仅仅是一个存放了名称-值对的集合。接口Context在这里作为一个标记接口:它扩展了java.util.Map但是没有添加任何特殊的行为。于此相反,类ContextBase不仅提供了对Map的实现而且增加了一个特性:属性-域透明。这个特性可以通过使用Map的put和get 方法操作JavaBean的域,当然这些域必须使用标准的getFoo和setFoo方法定义。那些通过JavaBean的“setter”方法设置的值,可以通过对应的域名称,用Map的get方法得到。同样,那些用Map的put方法设置的值可以通过JavaBean的“getter”方法得到。
例如,我们可以创建一个专门的context提供显式的customerName属性支持。
Java代码
package com.jadecove.chain.sample;
import org.apache.commons.chain.impl.ContextBase;
public class SellVehicleContext extends ContextBase {
private String customerName;
public String getCustomerName() {
return customerName;
}
public void setCustomerName(String name) {
this.customerName = name;
}
}
package com.jadecove.chain.sample;
import org.apache.commons.chain.impl.ContextBase;
public class SellVehicleContext extends ContextBase {
private String customerName;
public String getCustomerName() {
return customerName;
}
public void setCustomerName(String name) {
this.customerName = name;
}
}
现在你既可以进行Map的一般属性存取操作同时也可以使用显式的JavaBean的访问和修改域的方法,这两个将产生同样的效果。但是首先你需要在运行SellVehicleChain时实例化SellVehiceContext而不是ContextBase。
Java代码
public static void main(String[] args) throws Exception {
Command process = new SellVehicleChain();
Context ctx = new SellVehicleContext();
process.execute(ctx);
}
public static void main(String[] args) throws Exception {
Command process = new SellVehicleChain();
Context ctx = new SellVehicleContext();
process.execute(ctx);
}
尽管你不改变GetCustomerInfo中存放用户名的方法——仍然使用ctx.put("customerName", "George Burdell")——你可以在CloseSale中使用getCustomerName()方法得到用户名。
Java代码
public boolean execute(Context ctx) throws Exception {
SellVehicleContext myCtx = (SellVehicleContext) ctx;
System.out.println("Congratulations " + myCtx.getCustomerName() + ", you bought a new car!");
return false;
}
public boolean execute(Context ctx) throws Exception {
SellVehicleContext myCtx = (SellVehicleContext) ctx;
System.out.println("Congratulations " + myCtx.getCustomerName() + ", you bought a new car!");
return false;
}
那些依赖类型安全和context的显式域的命令(Command)可以利用标准的getter和setter方法。当一些新的命令(Command)被添加时,它们可以不用考虑context的具体实现,直接通过Map的get和put操作属性。不论采用何种机制,ContextBase类都可以保证命令(Command)间可以通过context互操作。
下面这个例子展示了如何使用Commons Chain的API建立并执行一组顺序的命令。当然,和现在大多数Java软件一样,Commons Chain可以使用XML文件作为配置文件。你可以将“汽车销售”流程的步骤在XML文件中定义。这个文件有个规范的命名chain- config.xml。
Xml代码
<catalog>
<chain name="sell-vehicle">
<command id="GetCustomerInfo" className="com.jadecove.chain.sample.GetCustomerInfo"/>
<command id="TestDriveVehicle" className="com.jadecove.chain.sample.TestDriveVehicle"/>
<command id="NegotiateSale" className="com.jadecove.chain.sample.NegotiateSale"/>
<command id="ArrangeFinancing" className="com.jadecove.chain.sample.ArrangeFinancing"/>
<command id="CloseSale" className="com.jadecove.chain.sample.CloseSale"/>
</chain>
</catalog>
<catalog>
<chain name="sell-vehicle">
<command id="GetCustomerInfo" className="com.jadecove.chain.sample.GetCustomerInfo"/>
<command id="TestDriveVehicle" className="com.jadecove.chain.sample.TestDriveVehicle"/>
<command id="NegotiateSale" className="com.jadecove.chain.sample.NegotiateSale"/>
<command id="ArrangeFinancing" className="com.jadecove.chain.sample.ArrangeFinancing"/>
<command id="CloseSale" className="com.jadecove.chain.sample.CloseSale"/>
</chain>
</catalog>
Chain的配置文件可以包含多个链定义,这些链定义可以集合进不同的编目中。在这个例子中,链定义在一个默认的编目中定义。事实上,你可以在这个文件中定义多个名字的编目,每个编目可拥有自己的链组。
现在你可以使用Commons Chain提供的类载入编目并得到指定的链,而不用像SellVehicleChain中那样自己在程序中定义一组命令:
Java代码
package com.jadecove.chain.sample;
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;
public class CatalogLoader {
private static final String CONFIG_FILE = "/com/jadecove/chain/sample/chain-config.xml";
private ConfigParser parser;
private Catalog catalog;
public CatalogLoader() {
parser = new ConfigParser();
}
public Catalog getCatalog() throws Exception {
if (catalog == null) {
parser.parse(this.getClass().getResource(CONFIG_FILE));
}
catalog = CatalogFactoryBase.getInstance().getCatalog();
return catalog;
}
public static void main(String[] args) throws Exception {
CatalogLoader loader = new CatalogLoader();
Catalog sampleCatalog = loader.getCatalog();
Command command = sampleCatalog.getCommand("sell-vehicle");
Context ctx = new SellVehicleContext();
command.execute(ctx);
}
}
package com.jadecove.chain.sample;
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;
public class CatalogLoader {
private static final String CONFIG_FILE = "/com/jadecove/chain/sample/chain-config.xml";
private ConfigParser parser;
private Catalog catalog;
public CatalogLoader() {
parser = new ConfigParser();
}
public Catalog getCatalog() throws Exception {
if (catalog == null) {
parser.parse(this.getClass().getResource(CONFIG_FILE));
}
catalog = CatalogFactoryBase.getInstance().getCatalog();
return catalog;
}
public static void main(String[] args) throws Exception {
CatalogLoader loader = new CatalogLoader();
Catalog sampleCatalog = loader.getCatalog();
Command command = sampleCatalog.getCommand("sell-vehicle");
Context ctx = new SellVehicleContext();
command.execute(ctx);
}
}
Chain 使用Commons Digester来读取和解析配置文件。因此你需要将Commons Digester.jar加入classpath中。我使用了1.6版本并且工作得很好。Digester使用了Commons Collectios(我使用的版本是3.1),Commons Logging(版本1.0.4),Commons BeanUtils(1.7.0),因此你也需要将它们的jar文件加入classpath中。在加入这些jar后,CatalogLoader就可以被编译和运行,它的输出和另外两个测试完全相同。
现在你可以在XML文件中定义链,并可以在程序中得到这个链(别忘了链也是命令),这样扩展的可能性和程序的灵活性可以说是无限的。假设过程“安排财务”实际上由一个完全分离的商业部门处理。这个部门希望为这种销售建立自己的工作流程。 Chain提供了嵌套链来实现这个要求。因为链本身就是命令,因此你可以用指向另一个链的引用替换一个单一用途的命令。下面是增加了新流程的链的定义:
Xml代码
<catalog name="auto-sales">
<chain name="sell-vehicle">
<command id="GetCustomerInfo" className="com.jadecove.chain.sample.GetCustomerInfo"/>
<command id="TestDriveVehicle" className="com.jadecove.chain.sample.TestDriveVehicle"/>
<command id="NegotiateSale" className="com.jadecove.chain.sample.NegotiateSale"/>
<command className="org.apache.commons.chain.generic.LookupCommand" catalogName="auto-sales" name="arrange-financing" optional="true"/>
<command id="CloseSale" className="com.jadecove.chain.sample.CloseSale"/>
</chain>
<chain name="arrange-financing">
<command id="ArrangeFinancing" className="com.jadecove.chain.sample.ArrangeFinancing"/>
</chain>
</catalog>
<catalog name="auto-sales">
<chain name="sell-vehicle">
<command id="GetCustomerInfo" className="com.jadecove.chain.sample.GetCustomerInfo"/>
<command id="TestDriveVehicle" className="com.jadecove.chain.sample.TestDriveVehicle"/>
<command id="NegotiateSale" className="com.jadecove.chain.sample.NegotiateSale"/>
<command className="org.apache.commons.chain.generic.LookupCommand" catalogName="auto-sales" name="arrange-financing" optional="true"/>
<command id="CloseSale" className="com.jadecove.chain.sample.CloseSale"/>
</chain>
<chain name="arrange-financing">
<command id="ArrangeFinancing" className="com.jadecove.chain.sample.ArrangeFinancing"/>
</chain>
</catalog>
Commons Chain提供了一个常用的命令LookupCommand来查找和执行另一个链。属性optional用于控制当指定的嵌套链没有找到时如何处理。 optional=true时,即使链没找到,处理也会继续。反之,LookupCommand将抛出 IllegalArgumentException,告知指定的命令未找到。
在下面三种情况下,命令链将结束:
命令的execute方法返回true
运行到了链的尽头
命令抛出异常
当链完全处理完一个过程后,命令就返回true。这是责任链模式(Chain of Responsibility)的基本概念。处理从一个命令传递到另一个命令,直到某个命令(Command)处理了这个命令。如果在到达命令序列尽头时仍没有处理返回true,也假设链已经正常结束。
当有命令抛出错误时链就会非正常结束。在Commons Chain中,如果有命令抛出错误,链的执行就会中断。不论是运行时错误(runtime exception)还是应用错误(application exception),都会抛出给链的调用者。但是许多应用都需要对在命令之外定义的错误做明确的处理。Commons Chain提供了Filter接口来满足这个要求。Filter继承了Command,添加了一个名为postprocess的方法。
public boolean postprocess(Context context, Exception exception);
只要Filter的execute方法被调用,不论链的执行过程中是否抛出错误,Commons Chain都将保证Filter的postprocess方法被调用。和servlet的过滤器(filter)相同,Commons Chain的Filter按它们在链中的顺序依次执行。同样,Filter的postprocess方法按倒序执行。你可以使用这个特性实现自己的错误处理。下面是一个用于处理我们例子中的错误的Filter:
Java代码
package com.jadecove.chain.sample;
import org.apache.commons.chain.Context;
import org.apache.commons.chain.Filter;
public class SellVehicleExceptionHandler implements Filter {
public boolean execute(Context context) throws Exception {
System.out.println("Filter.execute() called.");
return false;
}
public boolean postprocess(Context context, Exception exception) {
if (exception == null)
return false;
System.out.println("Exception " + exception.getMessage() + " occurred.");
return true;
}
}
package com.jadecove.chain.sample;
import org.apache.commons.chain.Context;
import org.apache.commons.chain.Filter;
public class SellVehicleExceptionHandler implements Filter {
public boolean execute(Context context) throws Exception {
System.out.println("Filter.execute() called.");
return false;
}
public boolean postprocess(Context context, Exception exception) {
if (exception == null)
return false;
System.out.println("Exception " + exception.getMessage() + " occurred.");
return true;
}
}
Filter在配置文件中的定义就和普通的命令(Command)定义相同:
Xml代码
<chain name="sell-vehicle">
<command id="ExceptionHandler" className = "com.jadecove.chain.sample.SellVehicleExceptionHandler"/>
<command id="GetCustomerInfo" className="com.jadecove.chain.sample.GetCustomerInfo"/>
<chain name="sell-vehicle">
<command id="ExceptionHandler" className = "com.jadecove.chain.sample.SellVehicleExceptionHandler"/>
<command id="GetCustomerInfo" className="com.jadecove.chain.sample.GetCustomerInfo"/>
Filter 的execute方法按定义的序列调用。然而,它的postprocess方法将在链执行完毕或抛出错误后执行。当一个错误被抛出时, postprocess方法处理完后会返回true,表示错误处理已经完成。链的执行并不会就此结束,但是本质上来说这个错误被捕捉而且不会再向外抛出。如果postprocess方法返回false,那错误会继续向外抛出,然后链就会非正常结束。
让我们假设ArrangeFinancing因为用户信用卡损坏抛出错误。SellVehicleExceptionHandler就能捕捉到这个错误,程序输出如下:
Filter.execute() called.
Get customer info
Test drive the vehicle
Negotiate sale
Exception Bad credit occurred.
结合了过滤器(filter)和子链技术后,你就可以造型很复杂的工作流程。
发表评论
-
Tomcat6.x + p6spy + IronTrackSQL 监控sql性能
2013-05-15 17:41 1370具体内容见附件!!! <script> (f ... -
说说Statement、PreparedStatement和CallableStatement的异同.
2012-05-15 16:22 23621.Statement、PreparedStatement和C ... -
JSP中Session对象的由来
2012-02-10 16:39 1659如果JSP没有显示的使用 <% @page sessio ... -
Jave的autoboxing与autounboxing
2012-02-03 13:21 1247Java 自动装箱与拆箱 一 ... -
ASCII码表图片
2011-12-08 10:10 4072ASCII对照码表 -
Linux上的Cron命令集合
2011-09-22 10:43 1178关于linux下的Cron 查看当前用户的cron列表命 ... -
Eclipse或MyEclipse设置启动JVM时的字符集
2011-08-31 22:34 3682Eclipse或MyEclipse设置启动JVM时的字符集 ... -
Ajax与JSP防止缓存介绍
2011-06-22 20:36 3213一:清除AJAX缓存的方法介绍: 1.在ajax发送请求前加上 ... -
Eclipse下怎么打jar包 jar怎么运行
2011-01-31 18:05 2857一、怎么打jar包 第一步:选中要打jar包的工程 第二步 ... -
Jboss中的web-console与jmx-console的配置
2010-10-10 15:34 9975JBoss安装成功后,一般 ... -
使用jboss服务器时遇到的相关问题
2010-09-27 23:50 4310Eclipse启动Jboss服务器,如何指定JVM虚拟内存大小 ... -
设置Tomcat,Jboss应用服务器的端口
2010-09-27 13:13 2279Tomcat,Jboss,Oracle端口号的修改 转载于ht ... -
向eclipse中添加user library的方式
2010-09-22 22:33 35571以前做项目都是把需要的jar包直接copy到工程的lib目录下 ... -
用LinkedList实现队列和栈
2010-07-13 11:01 1734队列实现: package com.test.unicode; ... -
摘抄一段关于HashSet的描述
2010-07-13 10:20 1119HashSet介绍: 1.只有类的实例对象要被采用哈希算法进 ... -
JAVA静态和非静态内部类
2010-06-23 15:14 13240一致对java的静态和非静态内部类的东东理解不清楚,今天测试了 ... -
找不到 java 类: org.apache.catalina.startup.Bootstrap
2010-06-18 10:55 17684今天启动服务器时,在catalina.out文件日志中出现了异 ... -
BigDecimal
2010-06-12 17:30 1205前段时间看见一个关于 2.0-1.9非0.1结果的文章。 在此 ... -
设置ECLIPSE自动编译
2010-06-11 12:34 8290设置eclipse自动编译的2中方式: 1. 确保 proje ... -
代码自我审查的一些体会
2010-05-24 14:20 1125代码自我审查的一些体 ...
相关推荐
Apache Commons Chain API 是一个用于构建和执行工作流程的Java库,它源于Apache Jakarta项目,旨在提供一种灵活且可扩展的方式来组织和执行一系列处理任务。这个API的核心概念是“链”(Chain),它允许开发者定义...
Apache Commons Chain 是一个Java库,它提供了一种用于组织和执行命令或操作的框架。这个“commons-chain-1.2-bin”是Apache Commons Chain 1.2版本的二进制发行版,通常包含编译好的类库、文档和其他运行时需要的...
Apache Commons Chain 是一个Java库,它提供了一种用于构建可配置、模块化的应用程序处理流程的框架。这个"commons-chain-1.2-src"是Apache Commons Chain项目的源代码包,版本为1.2。这个框架主要关注的是应用中的...
Apache Commons Chain 是一个Java库,它提供了一种结构化处理任务的方法,允许开发者定义一系列操作(或称为“命令”)并按照特定顺序执行。这个库主要用于构建应用中的业务流程和工作流,它允许灵活地组合和配置...
Apache Commons Chain 是一个Java库,它提供了一种用于构建应用程序业务流程和服务的框架。这个"commons-chain-1.2-src.tar.gz"文件包含了Apache Chain项目的1.2版本的源代码,使得开发者能够深入理解其内部工作原理...
apache commons chain 提供了对CoR模式的基础支持。。CoR模式,是Chain of Responsebility的缩写。CommonsChain实现了Chain of Responsebility和Command模式,其中的Catalog + 配置文件的方式使得调用方和Command的...
commons-chain.jar commons-chain.jar
Commons BeanUtils、Commons Chain 和 Commons Digester 是 Apache 软件基金会开发的三个重要的 Java 开源库,它们在 Java 开发中起到了关键的作用。这三个库通常被用于处理对象属性、构建应用程序流程以及解析XML...
Apache-commons源码其中包括(commons-email-1.5-src、commons-fileupload-1.4-src、commons-io-2.8.0-src、commons-jelly-1.0.1-src、commons-lang3-3.11-src...)
5. **Commons Chain** (commons-chain-1.2-src.zip): Chain 提供了一种可配置的命令链(Chain of Responsibility)模式实现,用于构建和执行复杂的业务流程。开发者可以定义一系列处理命令,并根据需要组合它们,...
类似于Commons Chain和Commons Pipeline这样的Java Pipeline Step Chain用于组织复杂处理流程执行的流行技术。支持通用业务job、services子流程无限制拆分。支持业务子流程串行化、业务子流程并行化,可配置化。...
10. **Chain**:Apache Commons Chain 提供了责任链模式的实现,允许定义一系列操作的执行顺序,使得业务流程更加灵活和可扩展。 11. **DBCP**:Apache Commons DBCP 是一个开源的数据库连接池实现,能够有效地管理...
4. **Commons Chain**: 实现了设计模式中的“责任链模式”,允许构建复杂的处理流程,每个处理者可以决定是否处理请求,或者传递给下一个处理者。 5. **Commons CLI**: 提供命令行参数、选项、选项组等的处理,简化...
commons-chain-1.1.commons-chain-1.1.jarjar
chain-1.2-bin.zip commons-chain-1.2-src.zip commons-cli-1.1-src.zip commons-cli-1.1.zip commons-codec-1.3-src.zip commons-codec-1.3.zip commons-collections-3.2.1-bin.zip commons-...
类似于Commons Chain和Commons Pipeline这样的Java Pipeline Step Chain用于组织复杂处理流程执行的流行技术. Java Pipeline Step Chain like Apache Commons Chain and Commons Pipeline.A popular technique for ...
commons-chain-1.2.0.jar
apache-sanselan-incubating-0.97-bin bcel-5.2 commons-beanutils-1.9.2-bin commons-chain-1.2-bin commons-cli-1.3.1-bin commons-codec-1.10-bin commons-collections4-4.0-bin commons-configuration-1.10-bin...
apache commons jar(commons所有的jar包... apache-sanselan-incubating-0.97-bin bcel-5.2 commons-beanutils-1.9.2-bin commons-chain-1.2-bin commons-cli-1.3.1-bin commons-codec-1.10-bin commons-collections4-4
以下是压缩文件的jar包名称: commons-validator-1.3.0.jar ...commons-chain-1.1.jar commons-beanutils-1.6.jar 包含两个最常用的源码: commons-beanutils-1.6-src.zip commons-collections-3.2.1-src.zip