責任鏈模式(Chain of Responsibility Pattern),簡而言之,就是把具有能力處理某種任務的類組合成這個鏈,這個鏈一般是有頭有尾的,在此我們稱鏈中的每一個類為節點,當需要處理的請求到達鏈頭時,就會執行節點的處理邏輯,每個節點的處理情況可分為三種:
- 當前節點處理完并直接返回(一般是到尾節點)
- 當前節點不作任何處理傳給下一個節點
- 當前節點進行相關處理后再傳給下一個節點
最近在閱讀開源工作流項目flowable的源碼時發現大量應用了責任鏈模式,本文先模擬日志級別處理來介紹一個簡單的責任鏈模式,然后再看flowable源碼中的實現。
1. 責任鏈模式之日志級別處理
先以我們比較熟悉的日志系統為例,假如現在我們要實現一個有Debug, Info, Warn, Error四個級別的日志系統,需要滿足以下條件:
a. 系統啟動時可以配置日志級別,打印日志時可以設置日志的級別。
b. 日志級別從小到大依次為Debug, Info, Warn, Error, 且只有當配置日志級別小于或等于某類日志級別時,才會打印相應級別的日志,否則不打印。
備注: 為了方便判斷,我們分別用1,2,3,4來表示這四個日志級別。
用責任鏈實現的日志系統類圖如下:
首先定義一個接口,如下:
public interface Logger {
/**
* 打印日志的方法
* @param message 具體內容
* @param msgLevel 日志級別
* @throws Exception 當打印消息的日志級別不在范圍內時拋出異常
*/
void excute(String message, int msgLevel) throws Exception;
/**
* 獲取日志類的下一個節點
* @return
*/
Logger getNext();
/**
* 設置日志類的下一個節點,以形成責任鏈
* @param logger
* @throws Exception
*/
void setNext(Logger logger) throws Exception;
}
然后定義一個實現上面接口的抽象類:
public abstract class AbstractLogger implements Logger{
/**
* 日志類的下一個節點
*/
protected Logger next;
/**
* 系統的日志級別
*/
protected int configLevel;
public AbstractLogger(int configLevel) throws Exception {
if(configLevel < 1 || configLevel > 4){
throw new Exception("Unsupported logger config level, only 1 to 4 is allowed!");
}
this.configLevel = configLevel;
}
@Override
public Logger getNext() {
return next;
}
@Override
public void setNext(Logger logger) throws Exception {
this.next = logger;
}
/**
* 提供給子類用的公共方法
* @param message
*/
protected void print(String message){
System.out.println(message);
}
}
然后是四個級別對應的日志實現類:
首先是責任鏈中第一個節點:
public class ErrorLogger extends AbstractLogger {
public static final int level = 4;
public ErrorLogger(int configLevel) throws Exception {
super(configLevel);
}
@Override
public void excute(String message, int msgLevel) throws Exception {
if(msgLevel == level){
print("Logger::Error: " + message);
}else if(msgLevel >= configLevel){
next.excute(message, msgLevel);
}
}
}
第二個節點:
public class InfoLogger extends AbstractLogger {
public static final int level = 2;
public InfoLogger(int configLevel) throws Exception {
super(configLevel);
}
@Override
public void excute(String message, int msgLevel) throws Exception {
if(msgLevel == level){
print("Logger::Info: " + message);
}else if(msgLevel >= configLevel){
next.excute(message, msgLevel);
}
}
}
第三個節點:
public class WarnLogger extends AbstractLogger {
public static final int level = 3;
public WarnLogger(int configLevel) throws Exception {
super(configLevel);
}
@Override
public void excute(String message, int msgLevel) throws Exception {
if(msgLevel == level){
print("Logger::Warn: " + message);
}else if(msgLevel >= configLevel){
next.excute(message, msgLevel);
}
}
}
最后一個節點:
public class DebugLogger extends AbstractLogger {
public static final int level = 1;
public DebugLogger(int configLevel) throws Exception {
super(configLevel);
}
@Override
public void excute(String message, int msgLevel) throws Exception {
if(msgLevel == level){
print("Logger::Debug: " + message);
}
}
/**
* Debug為最低的級別,在責任鏈中沒有下一個節點
* @return
*/
@Override
public Logger getNext() {
return null;
}
/**
* 當嘗試給Debug日志類設置下一個節點時拋出異常
* @param logger
* @throws Exception
*/
@Override
public void setNext(Logger logger) throws Exception {
throw new Exception("Next Logger in not Allowed in here!");
}
}
測試類的代碼如下:
public class ChainPatternDemo {
/**
* 設置的系統的日志級別,組合日志處理責任鏈,返回責任鏈的首個節點
* @param level 系統的日志級別
* @return
* @throws Exception
*/
public static Logger initLogConfig(int level) throws Exception {
try{
Logger root = new ErrorLogger(level);
Logger warnLogger = new WarnLogger(level);
Logger infoLogger = new InfoLogger(level);
Logger debugLogger = new DebugLogger(level);
root.setNext(warnLogger);
warnLogger.setNext(infoLogger);
infoLogger.setNext(debugLogger);
return root;
}catch (Exception e){
throw e;
}
}
public static void main(String[] args) {
try{
Logger logger = initLogConfig(4);
logger.excute("error message", 4);
logger.excute("warn message", 3);
logger.excute("info message", 2);
logger.excute("debug message", 1);
}catch (Exception e){
System.out.println(e.getMessage());
}
}
}
通過Logger logger = initLogConfig(4)
中的數字設置日志級別,然后嘗試打印所有級別的日志, 當設置為1即最低時,輸出如下, 打印所有日志,符合預期
Logger::Error: error message
Logger::Warn: warn message
Logger::Info: info message
Logger::Debug: debug message
當設置日志級別為2時,輸出如下,只打印info及以上級別的日志,符合預期:
Logger::Error: error message
Logger::Warn: warn message
Logger::Info: info message
當設置日志級別為4即最高時,輸出如下,只打印Error級別的日志,符合預期:
Logger::Error: error message
所有的日志處理都會通過定義的責任鏈進行處理,每個責任鏈中的節點只有在需要時才進行處理或者傳遞給下一個節點處理。
2. flowable中的責任鏈模式
如下,以RuntimeServiceImpl為例, 在flowable源碼中很多這樣的調用,這就是典型的責任鏈模式的應用。
此處關鍵在于實現了ServiceImpl這個類,如下所示,flowable的幾大主要類都實現了該接口。
相關的類結構圖如下:
?ServiceImpl類有CommandExecutor類型的成員變量,而CommandExecutor的實現類又有CommandInterceptor類型的成員變量,實現CommandInterceptor的抽象類中的成員變量next也是一個CommandInterceptor類,通過next既可以實現責任鏈模式,AbstractCommandInterceptor的如下實現類將作為責任鏈中的節點。
那么責任鏈的前后關系又是怎樣的呢,這里我們就要查看ProcessEngineConfigurationImpl的源碼了,這個類的initInterceptorChain方法即為責任鏈的初始處理邏輯,如下:
public CommandInterceptor initInterceptorChain(List<CommandInterceptor> chain) {
if (chain == null || chain.isEmpty()) {
throw new FlowableException("invalid command interceptor chain configuration: " + chain);
}
for (int i = 0; i < chain.size() - 1; i++) {
chain.get(i).setNext(chain.get(i + 1));
}
return chain.get(0);
}
可以看到責任鏈的前后關系是按照列表中的順序的,所以關鍵點在于傳參,找到調用這個方法的地方:
public void initCommandExecutor() {
if (commandExecutor == null) {
CommandInterceptor first = initInterceptorChain(commandInterceptors);
commandExecutor = new CommandExecutorImpl(getDefaultCommandConfig(), first);
}
}
參數commandInterceptors是ProcessEngineConfigurationImpl類的一個成員變量,所以接下來要看看哪里初始化這個成員變量,如下:
public void initCommandInterceptors() {
if (commandInterceptors == null) {
commandInterceptors = new ArrayList<CommandInterceptor>();
if (customPreCommandInterceptors != null) {
commandInterceptors.addAll(customPreCommandInterceptors);
}
commandInterceptors.addAll(getDefaultCommandInterceptors());
if (customPostCommandInterceptors != null) {
commandInterceptors.addAll(customPostCommandInterceptors);
}
commandInterceptors.add(commandInvoker);
}
}
public Collection<? extends CommandInterceptor> getDefaultCommandInterceptors() {
List<CommandInterceptor> interceptors = new ArrayList<CommandInterceptor>();
interceptors.add(new LogInterceptor());
CommandInterceptor transactionInterceptor = createTransactionInterceptor();
if (transactionInterceptor != null) {
interceptors.add(transactionInterceptor);
}
if (commandContextFactory != null) {
interceptors.add(new CommandContextInterceptor(commandContextFactory, this));
}
if (transactionContextFactory != null) {
interceptors.add(new TransactionContextInterceptor(transactionContextFactory));
}
return interceptors;
}
先添加自定義前置節點,再添加默認節點(先是LogInterceptor, 然后createTransactionInterceptor(),接著CommandContextInterceptor,還有一個成員變量transactionContextFactory),再添加自定義后置節點,最后還加加上CommandInvoker。所以我們如果沒有特殊配置的話這條鏈依次會有LogInterceptor, TransactionContextInterceptor(和使用環境有關,在Spring下為SpringTransactionInterceptor), CommandContextInterceptor,TransactionContextInterceptor, CommandInvoker這五個節點。通過調試源碼可以驗證我們的分析,如下:
接著runtimeService也會進行相關初始化,其中責任鏈結構如下:
查看前四個節點,如下以LogInterceptor為例,肯定會有next.execute(config, command);
, 這也是保證責任鏈能夠往下走的決定因素。
接下來再看看最后一個節點CommandInvoker的源碼,關鍵是執行了command的execute方法,沒有next.execute了。
同時可以發現getNext()返回為null, 即獲取不到下一個節點,并且在嘗試設置下一個節點時會拋出異常,這也正是責任鏈鏈尾節點應有的特征。
由此也可以看出所有業務邏輯的處理最終會通過comand.execute來完成,所以接下來就是要搞清楚Command的處理邏輯,具體請待下一篇分析。