您好,登錄后才能下訂單哦!
本篇內(nèi)容介紹了“如何建立工作流引擎中責(zé)任鏈模式”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
網(wǎng)上關(guān)于責(zé)任鏈模式的介紹很多,菜鳥(niǎo)教程上是這樣說(shuō)的:責(zé)任鏈模式(Chain of Responsibility Pattern)為請(qǐng)求創(chuàng)建了一個(gè)接收者對(duì)象的鏈。在這種模式中,通常每個(gè)接收者都包含對(duì)另一個(gè)接收者的引用。如果一個(gè)對(duì)象不能處理該請(qǐng)求,那么它會(huì)把相同的請(qǐng)求傳給下一個(gè)接收者,依此類推。
這個(gè)概念術(shù)語(yǔ)比較抽象。
我曾經(jīng)在 深入理解Spring Security授權(quán)機(jī)制原理 一文中提到Spring Security在授權(quán)過(guò)程中有使用到過(guò)濾器的概念,過(guò)濾器鏈就像一條鐵鏈,中間的每個(gè)過(guò)濾器都包含對(duì)另一個(gè)過(guò)濾器的引用,從而把相關(guān)的過(guò)濾器鏈接起來(lái),像一條鏈的樣子。這時(shí)請(qǐng)求線程就如螞蟻一樣,會(huì)沿著這條鏈一直爬過(guò)去-----即,通過(guò)各過(guò)濾器調(diào)用另一個(gè)過(guò)濾器引用方法chain.doFilter(request, response),實(shí)現(xiàn)一層嵌套一層地將請(qǐng)求傳遞下去,當(dāng)該請(qǐng)求傳遞到能被處理的的過(guò)濾器時(shí),就會(huì)被處理,處理完成后轉(zhuǎn)發(fā)返回。通過(guò)過(guò)濾器鏈,可實(shí)現(xiàn)在不同的過(guò)濾器當(dāng)中對(duì)請(qǐng)求request做處理,且過(guò)濾器之間彼此互不干擾。
整個(gè)流程大致如下:
這個(gè)過(guò)濾器鏈的概念,其實(shí)就是責(zé)任鏈設(shè)計(jì)模式在Spring Security中的體現(xiàn)。
摘錄一段網(wǎng)上關(guān)于職責(zé)鏈模式介紹,其主要包含以下角色:
抽象處理者(Handler)角色:定義一個(gè)處理請(qǐng)求的接口,包含抽象處理方法和一個(gè)后繼連接。
具體處理者(Concrete Handler)角色:實(shí)現(xiàn)抽象處理者的處理方法,判斷能否處理本次請(qǐng)求,如果可以處理請(qǐng)求則處理,否則將該請(qǐng)求轉(zhuǎn)給它的后繼者。
客戶類(Client)角色:創(chuàng)建處理鏈,并向鏈頭的具體處理者對(duì)象提交請(qǐng)求,它不關(guān)心處理細(xì)節(jié)和請(qǐng)求的傳遞過(guò)程。
最近在研究Activiti工作流框架,發(fā)現(xiàn)其所有實(shí)現(xiàn)都是采用命令模式實(shí)現(xiàn),而命令模式當(dāng)中的Invoker角色又是采用攔截器鏈?zhǔn)侥J剑搭愃粕厦嫣岬降倪^(guò)濾器鏈,即設(shè)計(jì)模式里的責(zé)任鏈模式。
這里的Activiti工作流版本是6.0。
CommandInterceptor是一個(gè)攔截器接口,包含三個(gè)方法:
setNext()方法是在初始化時(shí),設(shè)置每個(gè)攔截器對(duì)象中包含了下一個(gè)攔截器對(duì)象,最后形成一條攔截器鏈;
getNext()可在每個(gè)攔截器對(duì)象中調(diào)用下一個(gè)攔截器對(duì)象;
execute()是每個(gè)攔截器對(duì)請(qǐng)求的處理。若在上一個(gè)攔截器鏈?zhǔn)嚼锊荒芴幚碓撜?qǐng)求話,就會(huì)通過(guò)next.execute(CommandConfig var1, Command
public interface CommandInterceptor { <T> T execute(CommandConfig var1, Command<T> var2); CommandInterceptor getNext(); void setNext(CommandInterceptor var1); }
抽象類AbstractCommandInterceptor實(shí)現(xiàn)了CommandInterceptor攔截器接口,在責(zé)任鏈模式當(dāng)中充當(dāng)抽象處理者(Handler)角色。該類最主要的屬性是 protected CommandInterceptor next,在同一包下,直接通過(guò)next即可調(diào)用下一個(gè)攔截器對(duì)象。
public abstract class AbstractCommandInterceptor implements CommandInterceptor { protected CommandInterceptor next; public AbstractCommandInterceptor() { } public CommandInterceptor getNext() { return this.next; } public void setNext(CommandInterceptor next) { this.next = next; } }
接下來(lái),將會(huì)分析攔截器鏈?zhǔn)侨绾纬跏蓟c工作的。
SpringBoot集成Activiti配置如下:
@Configuration public class SpringBootActivitiConfig { @Bean public ProcessEngine processEngine() { ProcessEngineConfiguration pro = ProcessEngineConfiguration.createStandaloneProcessEngineConfiguration(); pro.setJdbcDriver("com.mysql.jdbc.Driver"); pro.setJdbcUrl("xxxx"); pro.setJdbcUsername("xxxx"); pro.setJdbcPassword("xxx"); //避免發(fā)布的圖片和xml中文出現(xiàn)亂碼 pro.setActivityFontName("宋體"); pro.setLabelFontName("宋體"); pro.setAnnotationFontName("宋體"); //數(shù)據(jù)庫(kù)更更新策略 pro.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE); return pro.buildProcessEngine(); } }
這時(shí),啟動(dòng)項(xiàng)目后,pro.buildProcessEngine()這行代碼會(huì)初始化Activiti框架,進(jìn)入里面,會(huì)發(fā)現(xiàn)它有三種實(shí)現(xiàn),默認(rèn)是第二種,即ProcessEngineConfigurationImpl。
點(diǎn)進(jìn)去,Activiti框架具體構(gòu)建buildProcessEngine方法如下,其中 this.init()的作用是環(huán)境初始化,包括配置設(shè)置、JDBC連接、bean裝載等的:
public ProcessEngine buildProcessEngine() { this.init(); ProcessEngineImpl processEngine = new ProcessEngineImpl(this); if (this.isActiviti5CompatibilityEnabled && this.activiti5CompatibilityHandler != null) { Context.setProcessEngineConfiguration(processEngine.getProcessEngineConfiguration()); this.activiti5CompatibilityHandler.getRawProcessEngine(); } this.postProcessEngineInitialisation(); return processEngine; }
在this.init()方法里,涉及到責(zé)任鏈模式初始化的方法是this.initCommandExecutors(),里面詳情如下:
public void initCommandExecutors() { this.initDefaultCommandConfig(); this.initSchemaCommandConfig(); //初始化命令調(diào)用器 this.initCommandInvoker(); //List存放進(jìn)涉及到的攔截器 this.initCommandInterceptors(); //初始化命令執(zhí)行器 this.initCommandExecutor(); }
這里只需要關(guān)注最后三個(gè)方法——
this.initCommandInvoker()
initCommandInvoker()初始化構(gòu)建了一個(gè)CommandInvoker攔截器,它繼承上邊提到的攔截器抽象類AbstractCommandInterceptor。這個(gè)攔截器在整條過(guò)濾器鏈中是最重要和關(guān)鍵,它排在了整條鏈的最后,其實(shí),它才是最終執(zhí)行請(qǐng)求的,前邊幾個(gè)攔截器都是傳遞請(qǐng)求而已。
public void initCommandInvoker() { if (this.commandInvoker == null) { if (this.enableVerboseExecutionTreeLogging) { this.commandInvoker = new DebugCommandInvoker(); } else { //初始化執(zhí)行該行代碼 this.commandInvoker = new CommandInvoker(); } } }
這里 new CommandInvoker()一個(gè)對(duì)象,然后將地址復(fù)制給this.commandInvoker對(duì)象引用,注意,該引用將會(huì)用在接下來(lái)的initCommandInterceptors()方法里——
this.initCommandInterceptors();
initCommandInterceptors方法主要作用是創(chuàng)建一個(gè)List集合,然后將需要用到的攔截器都保存到該List集合里——
public void initCommandInterceptors() { if (this.commandInterceptors == null) { this.commandInterceptors = new ArrayList(); if (this.customPreCommandInterceptors != null) { //用戶自定義前置攔截器 this.commandInterceptors.addAll(this.customPreCommandInterceptors); } //框架自帶默認(rèn)的攔截器 this.commandInterceptors.addAll(this.getDefaultCommandInterceptors()); if (this.customPostCommandInterceptors != null) { this.commandInterceptors.addAll(this.customPostCommandInterceptors); } //命令調(diào)用器,在攔截器鏈最后一個(gè) this.commandInterceptors.add(this.commandInvoker); } }
this.getDefaultCommandInterceptors()的代碼如下:
public Collection<? extends CommandInterceptor> getDefaultCommandInterceptors() { List<CommandInterceptor> interceptors = new ArrayList(); //日志攔截器 interceptors.add(new LogInterceptor()); CommandInterceptor transactionInterceptor = this.createTransactionInterceptor(); if (transactionInterceptor != null) { interceptors.add(transactionInterceptor); } // if (this.commandContextFactory != null) { interceptors.add(new CommandContextInterceptor(this.commandContextFactory, this)); } //事務(wù)攔截器 if (this.transactionContextFactory != null) { interceptors.add(new TransactionContextInterceptor(this.transactionContextFactory)); } return interceptors; }
可見(jiàn),方法里的 this.commandInterceptors 就是一個(gè)專門(mén)儲(chǔ)存攔截器對(duì)象的List集合——
protected List<CommandInterceptor> commandInterceptors;
這里只需要重點(diǎn)關(guān)注this.commandInterceptors.add(this.commandInvoker)這行代碼,就是將上邊創(chuàng)建的CommandInvoker攔截器對(duì)象存儲(chǔ)到List里,它是放在initCommandInterceptors()方法最后,某種程度也就意味著,這個(gè)攔截器在整條鏈當(dāng)中處在最后面的位置。
執(zhí)行完該this.initCommandInterceptors()方法后,就可獲取到所有的攔截器對(duì)象,到這一步時(shí),各攔截器還是互相獨(dú)立的,仍無(wú)法通過(guò)next()來(lái)進(jìn)行調(diào)用傳遞,那么,究竟是如何將它們串起來(lái)形成一條鏈呢?
接下來(lái)的this.initCommandExecutor()方法,就是實(shí)現(xiàn)將各攔截器串起來(lái)形成一條長(zhǎng)鏈。
this.initCommandExecutor();
該方法有兩個(gè)作用,一個(gè)是生成Interceptor攔截器鏈,一個(gè)是創(chuàng)建命令執(zhí)行器commandExecutor。
public void initCommandExecutor() { if (this.commandExecutor == null) { CommandInterceptor first = this.initInterceptorChain(this.commandInterceptors); this.commandExecutor = new CommandExecutorImpl(this.getDefaultCommandConfig(), first); } }
this.initInterceptorChain(this.commandInterceptors)是將集合里的攔截器初始化生成一條攔截器鏈,先循環(huán)獲取List集合里的攔截器對(duì)象chain.get(i),然后通過(guò)setNext()方法在該攔截器對(duì)象chain.get(i)里設(shè)置下一個(gè)攔截器引用,這樣,就可實(shí)現(xiàn)責(zé)任鏈里所謂每個(gè)接收者都包含對(duì)另一個(gè)接收者的引用的功能。
public CommandInterceptor initInterceptorChain(List<CommandInterceptor> chain) { if (chain != null && !chain.isEmpty()) { for(int i = 0; i < chain.size() - 1; ++i) { ((CommandInterceptor)chain.get(i)).setNext((CommandInterceptor)chain.get(i + 1)); } return (CommandInterceptor)chain.get(0); } else { throw new ActivitiException("invalid command interceptor chain configuration: " + chain); } }
那么,這條攔截器鏈當(dāng)中,都有哪些攔截器呢?
直接debug到這里,可以看到,總共有4個(gè)攔截器對(duì)象,按照順序排,包括LogInterceptor,CommandContextInterceptor,TransactionContextInterceptor,CommandInvoker(在命令模式里,該類相當(dāng)Invoker角色)。這四個(gè)攔截器對(duì)象在責(zé)任鏈模式當(dāng)中充當(dāng)了具體處理者(Concrete Handler)角色。
責(zé)任鏈模式里剩余客戶類(Client)角色應(yīng)該是命令執(zhí)行器this.commandExecutor。
因此,工作流引擎當(dāng)中的責(zé)任鏈模式結(jié)構(gòu)圖如下:
組成一條攔截器鏈如下圖所示——
生成攔截器鏈后,會(huì)返回一個(gè)(CommandInterceptor)chain.get(0),即攔截器LogInterceptor,為什么只返回第一個(gè)攔截器呢,這是一個(gè)很巧妙的地方,因?yàn)樵摂r截器里已經(jīng)一層一層地嵌套進(jìn)其他攔截器了,因此,只需要返回第一個(gè)攔截器,賦值給first即可。
接下來(lái),就會(huì)創(chuàng)建命令執(zhí)行器——
this.commandExecutor = new CommandExecutorImpl(this.getDefaultCommandConfig(), first);
這個(gè)命令執(zhí)行器是整個(gè)引擎的底層靈魂,通過(guò)它,可以實(shí)現(xiàn)責(zé)任鏈模式與命令模式——
攔截器鏈初始化介紹完成后,接下來(lái)開(kāi)始介紹攔截器鏈在引擎里的應(yīng)用方式。
Activiti引擎的各操作方法其底層基本都是以命令模式來(lái)實(shí)現(xiàn)的,即調(diào)用上面創(chuàng)建的命令執(zhí)行器this.commandExecutor的execute方法來(lái)實(shí)現(xiàn)的,例如自動(dòng)生成28張數(shù)據(jù)庫(kù)表的方法,就是通過(guò)命令模式去做具體實(shí)現(xiàn)的——
this.commandExecutor.execute(processEngineConfiguration.getSchemaCommandConfig(), new SchemaOperationsProcessEngineBuild());
進(jìn)入到commandExecutor方法里,會(huì)發(fā)現(xiàn)前邊new CommandExecutorImpl(this.getDefaultCommandConfig(), first)建立命令執(zhí)行器時(shí),已將配置對(duì)象和嵌套其他攔截器的LogInterceptor攔截器對(duì)象,通過(guò)構(gòu)造器CommandExecutorImpl(CommandConfig defaultConfig, CommandInterceptor first)生成對(duì)象時(shí),傳參賦值給了相應(yīng)的對(duì)象屬性,其中first引用指向LogInterceptor,即攔截器鏈上的第一個(gè)攔截器——
public class CommandExecutorImpl implements CommandExecutor { protected CommandConfig defaultConfig; protected CommandInterceptor first; public CommandExecutorImpl(CommandConfig defaultConfig, CommandInterceptor first) { this.defaultConfig = defaultConfig; this.first = first; } public CommandInterceptor getFirst() { return this.first; } public void setFirst(CommandInterceptor commandInterceptor) { this.first = commandInterceptor; } public CommandConfig getDefaultConfig() { return this.defaultConfig; } public <T> T execute(Command<T> command) { return this.execute(this.defaultConfig, command); } public <T> T execute(CommandConfig config, Command<T> command) { return this.first.execute(config, command); } }
當(dāng)引擎執(zhí)行this.commandExecutor.execute(xxx,xxx))類似方法時(shí),其實(shí)是執(zhí)行了this.first.execute(config, command)方法,這里的this.first在構(gòu)建命令執(zhí)行器時(shí)是通過(guò)LogInterceptor傳進(jìn)來(lái)的,因此,執(zhí)行代碼其實(shí)是調(diào)用了LogInterceptor內(nèi)部的execute()方法,也就是說(shuō),開(kāi)始攔截器鏈上的第一個(gè)LogInterceptor攔截器傳遞方法execute()請(qǐng)求——
進(jìn)入到攔截器鏈上的第一個(gè)攔截器LogInterceptor。
根據(jù)其內(nèi)部代碼可以看出,這是一個(gè)跟日志有關(guān)的攔截器,內(nèi)部并沒(méi)有多少增強(qiáng)功能,只是做了一個(gè)判斷是否需要debug日志打印。若需要,則進(jìn)行debug打印,若不需要,直接進(jìn)入到 if (!log.isDebugEnabled()) 為true的作用域內(nèi)部,進(jìn)而執(zhí)行this.next.execute(config, command)用以將請(qǐng)求傳遞給下一個(gè)攔截器做處理。
public class LogInterceptor extends AbstractCommandInterceptor { private static Logger log = LoggerFactory.getLogger(LogInterceptor.class); public LogInterceptor() { } public <T> T execute(CommandConfig config, Command<T> command) { if (!log.isDebugEnabled()) { return this.next.execute(config, command); } else { log.debug("\n"); log.debug("--- starting {} --------------------------------------------------------", command.getClass().getSimpleName()); Object var3; try { var3 = this.next.execute(config, command); } finally { log.debug("--- {} finished --------------------------------------------------------", command.getClass().getSimpleName()); log.debug("\n"); } return var3; } } }
這里有一個(gè)小地方值得稍微打斷說(shuō)下,就這個(gè) if (!log.isDebugEnabled())判斷。眾生周知,若集成第三方日志插件如logback之類,若其配置里去除debug的打印,即時(shí)代碼里 存在log.debug("xxxxx")也不會(huì)打印到控制臺(tái),那么,這里增加一個(gè)判斷 if (!log.isDebugEnabled())是否多次一舉呢?
事實(shí)上,這里并非多此一舉,增加這個(gè)判斷,是可以提升代碼執(zhí)行效率的。因?yàn)閘og.debug("xxxxx")里的字符串拼接早于log.debug("xxxxx")方法執(zhí)行的,也就是說(shuō),即使該log.debug("xxxxx")不會(huì)打印,但其內(nèi)部的字符串仍然會(huì)進(jìn)行拼接,而拼接,是需要時(shí)間的,雖然很細(xì)微,但同樣屬于影響性能范疇內(nèi)的。因此,增加一個(gè)if判斷,若無(wú)需要打印debug日志時(shí),那么就無(wú)需讓其內(nèi)部的字符串進(jìn)行自動(dòng)拼接。
這是一個(gè)很小的知識(shí)點(diǎn),但面試過(guò)程中其實(shí)是有可能會(huì)遇到這類與日志相關(guān)的面試題的。
接下來(lái),讓我們繼續(xù)回到攔截器鏈的傳遞上來(lái)。
LogInterceptor攔截器調(diào)用this.next.execute(config, command),意味著將請(qǐng)求傳遞到下一個(gè)攔截器上進(jìn)行處理,根據(jù)前邊分析,可知下一個(gè)攔截器是CommandContextInterceptor,根據(jù)代碼大概可知,這個(gè)攔截器內(nèi)主要是獲取上下文配置對(duì)象和信息相關(guān)的,這些都是在工作流引擎初始化時(shí)生成的,它們被保存在Stack棧里,具體都保存了哪些信息暫不展開(kāi)分析——
public class CommandContextInterceptor extends AbstractCommandInterceptor { ...... public <T> T execute(CommandConfig config, Command<T> command) { CommandContext context = Context.getCommandContext(); boolean contextReused = false; if (config.isContextReusePossible() && context != null && context.getException() == null) { contextReused = true; context.setReused(true); } else { context = this.commandContextFactory.createCommandContext(command); } try { Context.setCommandContext(context); Context.setProcessEngineConfiguration(this.processEngineConfiguration); if (this.processEngineConfiguration.getActiviti5CompatibilityHandler() != null) { Context.setActiviti5CompatibilityHandler(this.processEngineConfiguration.getActiviti5CompatibilityHandler()); } //繼續(xù)將命令請(qǐng)求傳遞到下一個(gè)攔截器 Object var5 = this.next.execute(config, command); return var5; } catch (Exception var31) { context.exception(var31); } finally { ...... } return null; } }
CommandContextInterceptor攔截器沒(méi)有對(duì)命令請(qǐng)求做處理,它繼續(xù)將請(qǐng)求傳遞到下一個(gè)攔截器TransactionContextInterceptor,根據(jù)名字就大概可以猜到,這個(gè)攔截器主要是增加與事務(wù)有關(guān)的功能——
public <T> T execute(CommandConfig config, Command<T> command) { CommandContext commandContext = Context.getCommandContext(); boolean isReused = commandContext.isReused(); Object var9; try { if (this.transactionContextFactory != null && !isReused) { TransactionContext transactionContext = this.transactionContextFactory.openTransactionContext(commandContext); Context.setTransactionContext(transactionContext); commandContext.addCloseListener(new TransactionCommandContextCloseListener(transactionContext)); } var9 = this.next.execute(config, command); } finally { ...... } return var9; }
TransactionContextInterceptor攔截器同樣沒(méi)有對(duì)命令請(qǐng)求做處理,而是繼續(xù)傳遞到下一個(gè)攔截器,也就是最后一個(gè)攔截器CommandInvoker,根據(jù)名字可以大概得知,這是一個(gè)與命令請(qǐng)求有關(guān)的攔截器,傳遞過(guò)來(lái)的請(qǐng)求將會(huì)在這個(gè)攔截器里處理——
public class CommandInvoker extends AbstractCommandInterceptor { ...... public <T> T execute(CommandConfig config, final Command<T> command) { final CommandContext commandContext = Context.getCommandContext(); commandContext.getAgenda().planOperation(new Runnable() { public void run() { commandContext.setResult(command.execute(commandContext)); } }); this.executeOperations(commandContext); if (commandContext.hasInvolvedExecutions()) { Context.getAgenda().planExecuteInactiveBehaviorsOperation(); this.executeOperations(commandContext); } return commandContext.getResult(); } }
進(jìn)入到其內(nèi)部,可以發(fā)現(xiàn),這里沒(méi)有再繼續(xù)調(diào)用this.next.execute(config, command)這樣的請(qǐng)求進(jìn)行傳遞,而是直接執(zhí)行command.execute(commandContext),然后將返回值進(jìn)行返回,其中,command是請(qǐng)求參數(shù)當(dāng)中的第二個(gè)參數(shù),讓我們回過(guò)頭看下該請(qǐng)求案例最開(kāi)始的調(diào)用——
this.commandExecutor.execute(processEngineConfiguration.getSchemaCommandConfig(), new SchemaOperationsProcessEngineBuild());
這里的第二個(gè)參數(shù)是new SchemaOperationsProcessEngineBuild(),不妨進(jìn)入到SchemaOperationsProcessEngineBuild類中,是吧,其內(nèi)部同樣有一個(gè)execute方法——
public final class SchemaOperationsProcessEngineBuild implements Command<Object> { public SchemaOperationsProcessEngineBuild() { } public Object execute(CommandContext commandContext) { DbSqlSession dbSqlSession = commandContext.getDbSqlSession(); if (dbSqlSession != null) { dbSqlSession.performSchemaOperationsProcessEngineBuild(); } return null; } }
可見(jiàn),CommandInvoker攔截器內(nèi)部執(zhí)行command.execute(commandContext),就相當(dāng)于執(zhí)行了new SchemaOperationsProcessEngineBuild().execute(commandContext),也就是——
public Object execute(CommandContext commandContext) { DbSqlSession dbSqlSession = commandContext.getDbSqlSession(); if (dbSqlSession != null) { dbSqlSession.performSchemaOperationsProcessEngineBuild(); } return null; }
這是一種命令模式的實(shí)現(xiàn)。
“如何建立工作流引擎中責(zé)任鏈模式”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。