背景
-
《瘋狂工作流講義》:“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的自定義攔截器。理解這個攔截器底層的工作過程。