疯狂Activiti6连载(7)Activiti的设计模式

来源:互联网 发布:centos怎么安装服务器 编辑:程序博客网 时间:2024/06/03 22:03

  本文节选自《疯狂工作流讲义(第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流程中各个实例进行操作,实际可以看作是对数据进行相应的操作,在此过程中,Activiti使用了设计模式中的命令模式,每一个操作数据库的过程,均可被看作为一个命令,然后交由命令执行者去完成。除此之外,为了能让使用者可以对这些命令进行相应的拦截(进行个性化处理),Activiti还使用了设计模式中的责任链模式,使用者可以添加相应的拦截器(责任链模式中的处理者)。为了让读者对这些知识有更深入的了解,在本小节,将先讲解命令模式与责任链模式。

注:对于初学者,可跳过本小节,掌握前面的流程引擎配置即可。

命令模式

GoF的设计模式中,命令模式属于行为模式,它把一个请求或者操作封装到命令对象中,这些请求或者操作内容包括接收者信息,然后将该命令对象交由执行者执行,执行者不需要关心命令的接收人或者命令的具体内容,因这些信息均被封装到命令对象中。命令模式中涉及的角色以及其作用如下:

     命令接口(Command):声明执行操作的接口。

     接口实现(ConcreteCommand命令接口实现,需要保存接收者的相应操作,并执行相应的操作

     命令执行者(Invoker):要求命令执行此次请求。

     命令接收人(Receiver):由命令的实现维护实例,并在命令执行时处理相应的任务。

接下来,编写一个最简单的命令模式。代码清单4-19为命令接口。

代码清单4-19codes\04\4.5\gof-command\src\org\crazyit\activiti\Command.java

public interface Command {

 

/**

*执行命令,参数为命令接收人

* @param receiver

*/

void execute(CommandReceiver receiver);

}

然后创建命令接收者,请看代码清单4-20

代码清单4-20codes\04\4.5\gof-command\src\org\crazyit\activiti\CommandReceiver.java

public interface CommandReceiver {

 

//命令执行者方法A

void doSomethingA();

 

//命令执行者方法B

void doSomethingB();

}

本例中命令接者只有一个实现,方法AdoSomething)打印“命令接收人执行命令A,方法BdoSomethingB)打印“命令接收人执行命令B,接下来创建命令执行者,如代码清单4-21所示。

代码清单4-21codes\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的实现类接下来,为命令接口提供两个实现CommandACommandB,代码清单4-22CommandA的实现。

代码清单4-22codes\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的实现中,直接让命令接收执行方法AdoSomethingA),CommandB的实现与CommandA类似,只是执行命令接收者的方法BdoSomethingB)。到此,命令模式的各个角色已经创建完毕,接下来编写客户端代码,让命令执行者执行相应的命令。代码清单4-23为客户端代码。

代码清单4-23codes\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-24codes\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-25codes\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-26codes\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-27codes\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-28codes\04\4.5\gof-chain\src\org\crazyit\activiti\Client.java

public static void main(String[] args) {

//创建第一个请求处理者集合

List<Handler> handlers = new ArrayList<Handler>();

//添加请求处理者到集合中

handlers.add(new HandlerA());

handlers.add(new HandlerB());

//将最终的处理者添加到集合中

handlers.add(new ActualHandler());

//处理集合中的请求处理者,按集合的顺序为它们设置下一任请求处理者,并返回第一任处理人

Handler first = setNext(handlers);

//第一任处理开始处理请求

first.execute(new Request());

}

//按照集合的顺序,设置下一任处理者,并返回第一任处理者

static Handler setNext(List<Handler> handlers) {

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-29codes\04\4.5\custom-interceptor\src\org\crazyit\activiti\InterceptorA.java

/**

* 拦截器实现A

*

*/

public class InterceptorA implements CommandInterceptor {

 

private CommandInterceptor next;

@Override

public <T> T execute(CommandConfig config, Command<T> command) {

//输出字符串和命令

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-30codes\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<CommandInterceptor>();

//依次将AB两个拦截器加入集合(责任链)

customPreCommandInterceptors.add(new InterceptorA());

customPreCommandInterceptors.add(new InterceptorB());

//再调用父类的实始化方法

super.initCommandInterceptors();

}

}

代码清单4-30initCommandInterceptors方法,用于初始化命令拦截器集合我们的自定义集合,加上Activiti的默认集合,形成拥有多个拦截器集合,也就是一条责任链。Activiti的默认集合,会加入日志拦截器、createTransactionInterceptor方法返回的拦截器、包含业务操作的命令、事务拦截器。代码清单4-31为运行代码。

代码清单4-31codes\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<String, Object> vars = new HashMap<String, Object>();

vars.put("day", 10);

//开始流程

engine.getRuntimeService().startProcessInstanceByKey("vacationProcess",

vars);

代码清单4-31中,部署了一个简单的流程,此时运行该测试程序,可以看到拦截器打印的效果如图4-2所示。


图4-2

在图4-2中可以看到,每执行一个命令,都会经过我们定义的拦截器AB。从命令名称不能看出,输出的命令与代码清单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

阅读全文
0 0