責(zé)任鏈模式及其在flowable源碼中的應(yīng)用

責(zé)任鏈模式(Chain of Responsibility Pattern),簡而言之,就是把具有能力處理某種任務(wù)的類組合成這個鏈,這個鏈一般是有頭有尾的,在此我們稱鏈中的每一個類為節(jié)點(diǎn),當(dāng)需要處理的請求到達(dá)鏈頭時,就會執(zhí)行節(jié)點(diǎn)的處理邏輯,每個節(jié)點(diǎn)的處理情況可分為三種:

  1. 當(dāng)前節(jié)點(diǎn)處理完并直接返回(一般是到尾節(jié)點(diǎn))
  2. 當(dāng)前節(jié)點(diǎn)不作任何處理傳給下一個節(jié)點(diǎn)
  3. 當(dāng)前節(jié)點(diǎn)進(jìn)行相關(guān)處理后再傳給下一個節(jié)點(diǎn)

最近在閱讀開源工作流項目flowable的源碼時發(fā)現(xiàn)大量應(yīng)用了責(zé)任鏈模式,本文先模擬日志級別處理來介紹一個簡單的責(zé)任鏈模式,然后再看flowable源碼中的實(shí)現(xiàn)。

1. 責(zé)任鏈模式之日志級別處理

先以我們比較熟悉的日志系統(tǒng)為例,假如現(xiàn)在我們要實(shí)現(xiàn)一個有Debug, Info, Warn, Error四個級別的日志系統(tǒng),需要滿足以下條件:

a. 系統(tǒng)啟動時可以配置日志級別,打印日志時可以設(shè)置日志的級別。

b. 日志級別從小到大依次為Debug, Info, Warn, Error, 且只有當(dāng)配置日志級別小于或等于某類日志級別時,才會打印相應(yīng)級別的日志,否則不打印。

備注: 為了方便判斷,我們分別用1,2,3,4來表示這四個日志級別。

用責(zé)任鏈實(shí)現(xiàn)的日志系統(tǒng)類圖如下:

image-20181205201713155

首先定義一個接口,如下:

public interface Logger {
    /**
     * 打印日志的方法
     * @param message  具體內(nèi)容
     * @param msgLevel 日志級別
     * @throws Exception 當(dāng)打印消息的日志級別不在范圍內(nèi)時拋出異常
     */
    void excute(String message, int msgLevel) throws Exception;

    /**
     * 獲取日志類的下一個節(jié)點(diǎn)
     * @return
     */
    Logger getNext();

    /**
     * 設(shè)置日志類的下一個節(jié)點(diǎn),以形成責(zé)任鏈
     * @param logger
     * @throws Exception
     */
    void setNext(Logger logger) throws Exception;
}

然后定義一個實(shí)現(xiàn)上面接口的抽象類:

public abstract class AbstractLogger implements Logger{
    /**
     * 日志類的下一個節(jié)點(diǎn)
     */
    protected Logger next;

    /**
     * 系統(tǒng)的日志級別
     */
    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);
    }
}

然后是四個級別對應(yīng)的日志實(shí)現(xiàn)類:

首先是責(zé)任鏈中第一個節(jié)點(diǎn):

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);
        }
    }
}

第二個節(jié)點(diǎn):

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);
        }
    }
}

第三個節(jié)點(diǎn):

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);
        }
    }
}

最后一個節(jié)點(diǎn):

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為最低的級別,在責(zé)任鏈中沒有下一個節(jié)點(diǎn)
     * @return
     */
    @Override
    public Logger getNext() {
        return null;
    }

    /**
     * 當(dāng)嘗試給Debug日志類設(shè)置下一個節(jié)點(diǎn)時拋出異常
     * @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 {
    /**
     * 設(shè)置的系統(tǒng)的日志級別,組合日志處理責(zé)任鏈,返回責(zé)任鏈的首個節(jié)點(diǎn)
     * @param level 系統(tǒng)的日志級別
     * @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)中的數(shù)字設(shè)置日志級別,然后嘗試打印所有級別的日志, 當(dāng)設(shè)置為1即最低時,輸出如下, 打印所有日志,符合預(yù)期

Logger::Error: error message
Logger::Warn: warn message
Logger::Info: info message
Logger::Debug: debug message

當(dāng)設(shè)置日志級別為2時,輸出如下,只打印info及以上級別的日志,符合預(yù)期:

Logger::Error: error message
Logger::Warn: warn message
Logger::Info: info message

當(dāng)設(shè)置日志級別為4即最高時,輸出如下,只打印Error級別的日志,符合預(yù)期:

Logger::Error: error message

所有的日志處理都會通過定義的責(zé)任鏈進(jìn)行處理,每個責(zé)任鏈中的節(jié)點(diǎn)只有在需要時才進(jìn)行處理或者傳遞給下一個節(jié)點(diǎn)處理。

2. flowable中的責(zé)任鏈模式

如下,以RuntimeServiceImpl為例, 在flowable源碼中很多這樣的調(diào)用,這就是典型的責(zé)任鏈模式的應(yīng)用。

image-20181202165433545

此處關(guān)鍵在于實(shí)現(xiàn)了ServiceImpl這個類,如下所示,flowable的幾大主要類都實(shí)現(xiàn)了該接口。

image-20181202165920687

相關(guān)的類結(jié)構(gòu)圖如下:

?
image-20181202170818344

ServiceImpl類有CommandExecutor類型的成員變量,而CommandExecutor的實(shí)現(xiàn)類又有CommandInterceptor類型的成員變量,實(shí)現(xiàn)CommandInterceptor的抽象類中的成員變量next也是一個CommandInterceptor類,通過next既可以實(shí)現(xiàn)責(zé)任鏈模式,AbstractCommandInterceptor的如下實(shí)現(xiàn)類將作為責(zé)任鏈中的節(jié)點(diǎn)。

image.png

那么責(zé)任鏈的前后關(guān)系又是怎樣的呢,這里我們就要查看ProcessEngineConfigurationImpl的源碼了,這個類的initInterceptorChain方法即為責(zé)任鏈的初始處理邏輯,如下:

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);
    }

可以看到責(zé)任鏈的前后關(guān)系是按照列表中的順序的,所以關(guān)鍵點(diǎn)在于傳參,找到調(diào)用這個方法的地方:

public void initCommandExecutor() {
        if (commandExecutor == null) {
            CommandInterceptor first = initInterceptorChain(commandInterceptors);
            commandExecutor = new CommandExecutorImpl(getDefaultCommandConfig(), first);
        }
    }

參數(shù)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;
    }

先添加自定義前置節(jié)點(diǎn),再添加默認(rèn)節(jié)點(diǎn)(先是LogInterceptor, 然后createTransactionInterceptor(),接著CommandContextInterceptor,還有一個成員變量transactionContextFactory),再添加自定義后置節(jié)點(diǎn),最后還加加上CommandInvoker。所以我們?nèi)绻麤]有特殊配置的話這條鏈依次會有LogInterceptor, TransactionContextInterceptor(和使用環(huán)境有關(guān),在Spring下為SpringTransactionInterceptor), CommandContextInterceptor,TransactionContextInterceptor, CommandInvoker這五個節(jié)點(diǎn)。通過調(diào)試源碼可以驗證我們的分析,如下:

image-20181202174009381

接著runtimeService也會進(jìn)行相關(guān)初始化,其中責(zé)任鏈結(jié)構(gòu)如下:

image-20181202174243229

查看前四個節(jié)點(diǎn),如下以LogInterceptor為例,肯定會有next.execute(config, command);, 這也是保證責(zé)任鏈能夠往下走的決定因素。

image-20181202190717182

接下來再看看最后一個節(jié)點(diǎn)CommandInvoker的源碼,關(guān)鍵是執(zhí)行了command的execute方法,沒有next.execute了。

image-20181202191155755

同時可以發(fā)現(xiàn)getNext()返回為null, 即獲取不到下一個節(jié)點(diǎn),并且在嘗試設(shè)置下一個節(jié)點(diǎn)時會拋出異常,這也正是責(zé)任鏈鏈尾節(jié)點(diǎn)應(yīng)有的特征。

image-20181202191355899

由此也可以看出所有業(yè)務(wù)邏輯的處理最終會通過comand.execute來完成,所以接下來就是要搞清楚Command的處理邏輯,具體請待下一篇分析。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 設(shè)計模式概述 在學(xué)習(xí)面向?qū)ο笃叽笤O(shè)計原則時需要注意以下幾點(diǎn):a) 高內(nèi)聚、低耦合和單一職能的“沖突”實(shí)際上,這兩者...
    彥幀閱讀 3,793評論 0 14
  • 關(guān)于Mongodb的全面總結(jié) MongoDB的內(nèi)部構(gòu)造《MongoDB The Definitive Guide》...
    中v中閱讀 32,038評論 2 89
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,973評論 19 139
  • 2009年的平安夜,我收到了他的蘋果,我記得那晚我很開心,我期待著圣誕節(jié)我們的開場白。果真,那天出乎意料的美好,我...
    張小壞zh閱讀 213評論 0 0
  • 正則:/^(13[0-9]|14[5-9]|15[012356789]|166|17[0-8]|18[0-9]|1...
    _韓小妖閱讀 512評論 0 0