背景
-
《疯狂工作流讲义》:“Activiti提供了命令拦截器功能,外界对Activiti流程中各个实例进行的操作,实际可以看作对数据进行的相应操作,在此过程中,Activiti使用了设计模式中的命令模式,每一个操作数据库的过程,均可被看着一个命令,然后交由命令执行者去完成。除此之外,为了能让使用者可以对这些命令进行相应的拦截(进行个性化处理),Activiti还使用了设计模式中的责任链模式,从而使用者可以添加相应的拦截器(责任链模式中的处理者)”。
-
理解了命令模式和责任莲模式,其实就可以大致理解Activiti是怎么工作的。
-
命令模式和责任链模式,在很多框架代码中是经常被使用的。在实际开发过程中,也是可以运用命令模式的。
过程
命令模式的实现过程
命令接口
public interface Command {
void execute(CommandReceiver receiver);
}
命令接收者
public interface CommandReceiver {
void doSomethingA();
void doSomethingB();
}
命令接收者的实现
public class CommandReceiverImpl implements CommandReceiver{
@Override
public void doSomethingA() {
System.out.println("命令执行者方法A");
}
@Override
public void doSomethingB() {
System.out.println("命令执行者方法B");
}
}
命令执行器
public class CommandExecutor {
public void execute(Command command) {
command.execute(new CommandReceiverImpl());
}
}
命令接口的实现者A
public class CommandA implements Command {
@Override
public void execute(CommandReceiver receiver) {
receiver.doSomethingA();
}
}
命令接口的实现者B
public class CommandB implements Command {
@Override
public void execute(CommandReceiver receiver) {
receiver.doSomethingB();
}
}
测试代码
public static void main(String[] args) {
// 创建命令执行者
CommandExecutor commandExecutor = new CommandExecutor();
// 创建命令A,交由命令执行者执行
Command commandA = new CommandA();
commandExecutor.execute(commandA);
// 创建命令B,交由命令执行者执行
Command commandB = new CommandB();
commandExecutor.execute(commandB);
}
理解:通过使用中间层命令接收者,解耦命令和命令执行者。命令执行者只关心命令。而命令的具体执行细节交给命令的接收人。
使用过程理解:需要自定义具体的命令,并选择命令对应的具体方法。而具体方法过程就交给命令接收人的具体实现即可。
此命令在Activiti中扮演的角色: 在Activiti中,每一个数据库的CRUD操作,均为一个命令的实现,然后交给Activiti的命令执行者执行。Activiti使用了一个CommandContext类作为命令接收者,该对象维护一些列的Manager对象,这些Manager对象就像J2EE中的DAO对象。
责任链模式的实现过程
概述:该设计模式让多个对象都有机会处理请求,从而避免了请求发送者和请求接收者之间的耦合。因为,一般理解就是一个请求发送者对应一个请求接收者。如果把请求接收者组成一条链,并沿着这条链传递请求,直到有一个对象处理这个请求为止,这就形成了一条责任链。
处理器接口
public abstract class Handler {
protected Handler next;
public void setNext(Handler handler) {
this.next = handler;
}
// 处理请求的方法,交由子类实现
public abstract void execute(Request request);
}
处理器接口实现A
public class HandlerA extends Handler {
@Override
public void execute(Request request) {
// 处理自己的事,然后交由下一任处理者继续执行请求
System.out.println("请求处理者A处理请求");
next.execute(request);
}
}
处理器接口实现B
public class HandlerB extends Handler {
@Override
public void execute(Request request) {
// 处理自己的事,然后交由下一任处理者继续执行请求
System.out.println("请求处理者B处理请求");
next.execute(request);
}
}
真实任务处理者(并且也宣告链结束了)
public class ActualHandler extends Handler {
@Override
public void execute(Request request) {
// 直接执行请求
request.doSomething();
}
}
请求(这里的请求其实也可以换成Command,命令接口,这样两个模式就耦合上了)
public class Request {
public void doSomething() {
System.out.println("执行请求");
}
}
测试
public class Test {
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);
}
}
理解:运行顺序,请求处理者A处理请求 -> 请求处理者B处理请求 -> 执行请求。根据业务逻辑,每个handler都处理自己的逻辑,然后层层往后传递即可。这个在Netty中有使用,它是双向链表,显示向后传递,这里的显示是指代码主动调用一个往后传递的方法。
编写自定义拦截器的实现过程(activiti)
Activiti的拦截器就是结合这两种设计模式来达到拦截效果的。每次Activiti进行业务操作,都会将其封装为一个Command放到责任链中执行。
定义拦截器A
public class MyCommandInterceptorA extends AbstractCommandInterceptor {
@Override
public <T> T execute(CommandConfig config, Command<T> command) {
System.out.println("拦截器A执行" + command.getClass().getName());
return this.getNext().execute(config,command);
}
}
定义拦截器B
public class MyCommandInterceptorB extends AbstractCommandInterceptor {
@Override
public <T> T execute(CommandConfig config, Command<T> command) {
System.out.println("拦截器B执行" + command.getClass().getName());
return this.getNext().execute(config,command);
}
}
定义拦截器C
public class MyCommandInterceptorC extends AbstractCommandInterceptor {
@Override
public <T> T execute(CommandConfig config, Command<T> command) {
System.out.println("拦截器C执行" + command.getClass().getName());
return this.getNext().execute(config,command);
}
}
在activiti.cfg.xml中配置进去
<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
<property name="dataSource" ref="dataSource"/>
<property name="databaseSchemaUpdate" value="true"/>
<property name="customPreCommandInterceptors">
<list>
<bean class="com.sanding.activiti.interceptor.MyCommandInterceptorC"></bean>
<bean class="com.sanding.activiti.interceptor.MyCommandInterceptorB"></bean>
<bean class="com.sanding.activiti.interceptor.MyCommandInterceptorA"></bean>
</list>
</property>
</bean>
拦截器添加的顺序就是执行的顺序。
执行结果
拦截器C执行org.activiti.engine.impl.cmd.GetProcessDefinitionInfoCmd
拦截器B执行org.activiti.engine.impl.cmd.GetProcessDefinitionInfoCmd
拦截器A执行org.activiti.engine.impl.cmd.GetProcessDefinitionInfoCmd
问题记录:按照疯狂工作流讲义的代码,是没有把拦截器添加进去的。原因还没有找到。但是按照上面代码的实现是可以把拦截器添加到activiti中的。
小结
- 设计原则多用组合少用继承。而命令模式其实运用的是概念其实是依赖。
- 记录命令模式的写作过程和理解。
- 记录责任链模式的写作过程和理解。
- 学会编写activiti的自定义拦截器。理解这个拦截器底层的工作过程。