本文节选自《疯狂工作流讲义(第2版)》
京东购买地址:https://item.jd.com/12246565.html
工作流Activiti6电子书:http://blog.csdn.net/boxiong86/article/details/78488562
工作流Activiti6教学视频:http://blog.csdn.net/boxiong86/article/details/78608585
本文要点
命令模式和责任链模式,以及Activiti如何使用这两种模式
Activiti提供了命令拦截器的功能,外界对Activiti流程中各个实例进行操作,实际可以看作是对数据进行相应的操作,在此过程中,Activiti使用了设计模式中的命令模式,每一个操作数据库的过程,均可被看作为一个命令,然后交由命令执行者去完成。除此之外,为了能让使用者可以对这些命令进行相应的拦截(进行个性化处理),Activiti还使用了设计模式中的责任链模式,使用者可以添加相应的拦截器(责任链模式中的处理者)。为了让读者对这些知识有更深入的了解,在本小节,将先讲解命令模式与责任链模式。
注:对于初学者,可跳过本小节,掌握前面的流程引擎配置即可。
在GoF的设计模式中,命令模式属于行为型模式,它把一个请求或者操作封装到命令对象中,这些请求或者操作内容包括接收者信息,然后将该命令对象交由执行者执行,执行者不需要关心命令的接收人或者命令的具体内容,因为这些信息均被封装到命令对象中。命令模式中涉及的角色以及其作用如下:
命令接口(Command):声明执行操作的接口。
接口实现(ConcreteCommand):命令接口实现,需要保存接收者的相应操作,并执行相应的操作。
命令执行者(Invoker):要求命令执行此次请求。
命令接收人(Receiver):由命令的实现维护实例,并在命令执行时处理相应的任务。
接下来,编写一个最简单的命令模式。代码清单4-19为命令接口。
代码清单4-19:codes\04\4.5\gof-command\src\org\crazyit\activiti\Command.java
public interface Command {
/**
*执行命令,参数为命令接收人
* @param receiver
*/
void execute(CommandReceiver receiver);
}
然后创建命令接收者,请看代码清单4-20。
代码清单4-20:codes\04\4.5\gof-command\src\org\crazyit\activiti\CommandReceiver.java
public interface CommandReceiver {
//命令执行者方法A
void doSomethingA();
//命令执行者方法B
void doSomethingB();
}
本例中命令接者只有一个实现,方法A(doSomething)打印“命令接收人执行命令A”,方法B(doSomethingB)打印“命令接收人执行命令B”,接下来创建命令执行者,如代码清单4-21所示。
代码清单4-21:codes\04\4.5\gof-command\src\org\crazyit\activiti\CommandExecutor.java
public class CommandExecutor {
public void execute(Command command) {
//创建命令接者可以使用其他设计模式
command.execute(new CommandReceiverImpl());
}
}
注意命令执行者的实现中,调使用命令的execute方法,并将相应的命令接收人设置到命令的execute方法参数中。此处创建命令接收者的方式,可以使用其他的设计模式完成,例如工厂模式,单态模式等等,此处为了更加简单,直接new一个CommandReceiver的实现类。接下来,为命令接口提供两个实现CommandA和CommandB,代码清单4-22为CommandA的实现。
代码清单4-22:codes\04\4.5\gof-command\src\org\crazyit\activiti\impl\CommandA.java
public class CommandA implements Command {
public void execute(CommandReceiver receiver) {
receiver.doSomethingA();
}
}
在代码清单4-22中,CommandA的实现中,直接让命令接收执行方法A(doSomethingA),CommandB的实现与CommandA类似,只是执行命令接收者的方法B(doSomethingB)。到此,命令模式的各个角色已经创建完毕,接下来编写客户端代码,让命令执行者执行相应的命令。代码清单4-23为客户端代码。
代码清单4-23:codes\04\4.5\gof-command\src\org\crazyit\activiti\Client.java
public class Client {
public static void main(String[] args) {
//创建命令执行者
CommandExecutor executor = new CommandExecutor();
//创建命令A,交由命令执行者执行
Command commandA = new CommandA();
executor.execute(commandA);
//创建命令B,交由命令执行者执行
Command commandB = new CommandB();
executor.execute(commandB);
}
}
代码清单4-23中,先创建一个命令执行者,然后创建两个命令,并交由命令执行者执行,最终执行结果将输出“命令接收人执行命令A”和“命令接收人执行命令B”。
现在了解了GoF的命令模式,在Activiti中,每一个数据库的CRUD操作,均为一个命令的实现,然后交给Activiti的命令执行者执行。Activiti使用了一个CommandContext类作为命令接收者,该对象维护一系列的Manager对象,这些Manager对象就像J2EE中的DAO对象。除了命令接收者外,Activiti还使用一系列的CommandInterceptor(命令拦截器),这些命令拦截器扮演命令模式中的命令执行者角色。那么这些命令拦截器是如何工作的呢?接下来需要了解责任链模式。
与命令模式一样,责任链模式也是GoF的设计模式之一,同样也是行为型模式。该设计模式为了让多个对象都有机会处理请求,从而避免了请求发送者和请求接收者之间的耦合。这些请求接收者将组成一条,并沿着这条链传递该请求,直到有一个对象处理这个请求为止,这就形成一条责任链。责任链模式有以下参与者:
请求处理者接口(Handler):定义一个处理请求的接口,可以实现后继链。
请求处理者实现(ConcreteHandler):请求处理接口的实现,如果它可以处理请求,就处理,否则就将该请转发给它的后继者。
代码清单4-24中编写一个请求处理的抽象类。
代码清单4-24:codes\04\4.5\gof-chain\src\org\crazyit\activiti\Handler.java
public abstract class Handler {
//下一任处理者
protected Handler next;
public void setNext(Handler next) {
this.next = next;
}
//处理请求的方法,交由子类实现
public abstract void execute(Request request);
}
代码清单4-24定义了一个请求处理者的抽象类,并且定义了请求的处理方法,需要由子类实现,需要注意的是,处理请求方法(execute)的参数为一个Request对象,本例中的Request对象只是一个普通的类,若责任链模式结合命令模式一起使用的话,那么execute方法的参数可以是命令模式中的命令接口。除此之外,Handler还定义了一个next属性,在这里表示下一任处理者的对象,此处提供setter方法,由客户端决定下一任请求处理者是谁。Request对象如代码清单4-25所示。
代码清单4-25:codes\04\4.5\gof-chain\src\org\crazyit\activiti\Request.java
public class Request {
public void doSomething() {
System.out.println("执行请求");
}
}
Request对象只有一个doSomething方法,如果将Request设置为一个接口的话,那么它也可以像命令模式的命令接口一样(见命令模式的Command)有多个实现。接下来,为请求处理接口添加若干个实现,代码清单4-26为其中一个实现。
代码清单4-26:codes\04\4.5\gof-chain\src\org\crazyit\activiti\impl\HandlerA.java
public class HandlerA extends Handler {
public void execute(Request request) {
//处理自己的事,然后交由下一任处理者继续执行请求
System.out.println("请求处理者A处理请求");
next.execute(request);
}
}
代码清单4-26中,HandlerA继承了Handler并且实现了execute方法,当该处理器处理完自己的事情后,再将请求交由下一任处理者继续执行请求。在责任链中,可以新建多个这样的请求处理者,本例中有两个这样的请求处理者,实现均与代码清单4-26中的HandlerA类似,在此不再赘述。
除了若干个请求处理者的实现外,还需要新建一个真实的请求处理者,通过代码清单4-26可以知道,实际上就算再多这样的请求处理者实现,依然没有对请求作任何处理,只是交由下一任处理者执行,因此需要一个真实的请求处理者来终结这条责任链。代码清单4-27为真实任务处理者。
代码清单4-27:codes\04\4.5\gof-chain\src\org\crazyit\activiti\impl\ActualHandler.java
/**
* 最终请求执行者,需要将其设置到责任链的最后一环
*/
public class ActualHandler extends Handler {
public void execute(Request request) {
//直接执行请求
request.doSomething();
}
}
如代码清单4-27所示,最终的请求处理者最终执行了请求,并且不再往下执行(不使用next属性)。下面编写客户端代码,使用这个责任链。客户端代码如代码清单4-28所示。
代码清单4-28:codes\04\4.5\gof-chain\src\org\crazyit\activiti\Client.java
public static void main(String[] args) {
//创建第一个请求处理者集合
List
//添加请求处理者到集合中
handlers.add(new HandlerA());
handlers.add(new HandlerB());
//将最终的处理者添加到集合中
handlers.add(new ActualHandler());
//处理集合中的请求处理者,按集合的顺序为它们设置下一任请求处理者,并返回第一任处理人
Handler first = setNext(handlers);
//第一任处理开始处理请求
first.execute(new Request());
}
//按照集合的顺序,设置下一任处理者,并返回第一任处理者
static Handler setNext(List
for (int i = 0; i < handlers.size() - 1; i++) {
Handler handler = handlers.get(i);
Handler next = handlers.get(i + 1);
handler.setNext(next);
}
return handlers.get(0);
}
在代码清单4-28中,定义了一个请求处理者的集合,然后按照该集合顺序通过setNext方法为每一个请求处理者设置下一任的请求处理者,setNext方法最后返回第一任处理者(HandlerA)。需要注意的是,由于定义了最终的请求处理者为ActualHandler,因此需要将其放到集合的最后,作为终止整个责任链的角色。最终运行顺序为:“请求处理者A处理请求”,“请求处理者B处理请求”,“执行请求”。
前面讲解了命令模式与责任链模式,Activiti的拦截器,就是结合这两种设计模式,达到拦截器的效果,每次Activiti进行业务操作,都会封装为一个Command放到责任链中执行。知道其原理后,可以在实现自定义配置类时,编写自己的拦截器。首先编写第一个拦截器实现,请看代码清单4-29。
代码清单4-29:codes\04\4.5\custom-interceptor\src\org\crazyit\activiti\InterceptorA.java
/**
* 拦截器实现A
*
*/
public class InterceptorA implements CommandInterceptor {
private CommandInterceptor next;
@Override
public
//输出字符串和命令
System.out.println("this is interceptor A:"
+ command.getClass().getName());
//然后让责任链中的下一请求处理者处理命令
return getNext().execute(config, command);
}
public CommandInterceptor getNext() {
return this.next;
}
public void setNext(CommandInterceptor next) {
this.next = next;
}
}
代码清单4-29中的类InterceptorA实现CommandInterceptor接口,实现责任链模式时,在拦截器的execute方法中,执行完拦截器自己的程序后(输出业务命令),会执行责任链的下一个拦截器的execute方法。了解责任链模式后,不难发现,此处的next就是拦截器中的下一任请求处理者,而此处的请求,则是命令模式中的Command接口,编写的InterceptorA就是责任链模式中请求处理者的其中一个实现。
使用同样的方式,创建拦截器B,与拦截器A类似,输出字符串与业务命令,再将请求(此处为Command)交由下一执行者执行。完成了两个拦截器后,再去实现父类的初始化拦截器方法,将我们的拦截器“侵入”到Activiti的责任链中,详细请见代码清单4-30。
代码清单4-30:codes\04\4.5\custom-interceptor\src\org\crazyit\activiti\TestConfiguration.java
/**
* 自定义配置类
*/
public class TestConfiguration extends ProcessEngineConfigurationImpl {
public CommandInterceptor createTransactionInterceptor() {
//不实现事务拦截器
return null;
}
/**
*重写初始化命令拦截器方法
*/
public void initCommandInterceptors() {
//为父类的命令集合添加拦截器
customPreCommandInterceptors = new ArrayList
//依次将A和B两个拦截器加入集合(责任链)
customPreCommandInterceptors.add(new InterceptorA());
customPreCommandInterceptors.add(new InterceptorB());
//再调用父类的实始化方法
super.initCommandInterceptors();
}
}
代码清单4-30中initCommandInterceptors方法,用于初始化命令拦截器集合。我们的自定义集合,加上Activiti的默认集合,形成拥有多个拦截器集合,也就是一条责任链。Activiti的默认集合,会加入日志拦截器、createTransactionInterceptor方法返回的拦截器、包含业务操作的命令、事务拦截器。代码清单4-31为运行代码。
代码清单4-31:codes\04\4.5\custom-interceptor\src\org\crazyit\activiti\MyConfig.java
ProcessEngines.getDefaultProcessEngine();
//创建Activiti配置对象
ProcessEngineConfiguration config = ProcessEngineConfiguration
.createProcessEngineConfigurationFromResource("my-config.xml");
//初始化流程引擎
ProcessEngine engine = config.buildProcessEngine();
//部署一个最简单的流程
engine.getRepositoryService().createDeployment()
.addClasspathResource("bpmn/config.bpmn20.xml").deploy();
//构建流程参数
Map
vars.put("day", 10);
//开始流程
engine.getRuntimeService().startProcessInstanceByKey("vacationProcess",
vars);
代码清单4-31中,部署了一个简单的流程,此时运行该测试程序,可以看到拦截器打印的效果如图4-2所示。
图4-2
在图4-2中可以看到,每执行一个命令,都会经过我们定义的拦截器A和B。从命令名称不能看出,输出的命令与代码清单4-31基本吻合:部署流程模型、查询流程定义、启动流程。
本文节选自《疯狂工作流讲义(第2版)》
京东购买地址:https://item.jd.com/12246565.html
工作流Activiti6电子书:http://blog.csdn.net/boxiong86/article/details/78488562
工作流Activiti6教学视频:http://blog.csdn.net/boxiong86/article/details/78608585
本书代码共享地址:https://gitee.com/yangenxiong/CrazyActiviti