原创/朱季谦html
本文须要必定责任链模式的基础,主要分红三部分讲解:java
1、简单理解责任链模式概念
2、Activiti工做流里责任链模式的创建
3、Activiti工做流里责任链模式的应用mysql
网上关于责任链模式的介绍不少,菜鸟教程上是这样说的:责任链模式(Chain of Responsibility Pattern)为请求建立了一个接收者对象的链。在这种模式中,一般每一个接收者都包含对另外一个接收者的引用。若是一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。面试
这个概念术语比较抽象。sql
我曾经在 深刻理解Spring Security受权机制原理 一文中提到Spring Security在受权过程当中有使用到过滤器的概念,过滤器链就像一条铁链,中间的每一个过滤器都包含对另外一个过滤器的引用,从而把相关的过滤器连接起来,像一条链的样子。这时请求线程就如蚂蚁同样,会沿着这条链一直爬过去-----即,经过各过滤器调用另外一个过滤器引用方法chain.doFilter(request, response),实现一层嵌套一层地将请求传递下去,当该请求传递到能被处理的的过滤器时,就会被处理,处理完成后转发返回。经过过滤器链,可实如今不一样的过滤器当中对请求request作处理,且过滤器之间彼此互不干扰。数据库
整个流程大体以下:设计模式
这个过滤器链的概念,其实就是责任链设计模式在Spring Security中的体现。架构
摘录一段网上关于职责链模式介绍,其主要包含如下角色:框架
最近在研究Activiti工做流框架,发现其全部实现都是采用命令模式实现,而命令模式当中的Invoker角色又是采用拦截器链式模式,即相似上面提到的过滤器链,即设计模式里的责任链模式。post
这里的Activiti工做流版本是6.0。
CommandInterceptor是一个拦截器接口,包含三个方法:
public interface CommandInterceptor { <T> T execute(CommandConfig var1, Command<T> var2); CommandInterceptor getNext(); void setNext(CommandInterceptor var1); }
抽象类AbstractCommandInterceptor实现了CommandInterceptor拦截器接口,在责任链模式当中充当抽象处理者(Handler)角色。该类最主要的属性是 protected CommandInterceptor next,在同一包下,直接经过next便可调用下一个拦截器对象。
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; } }
接下来,将会分析拦截器链是如何初始化与工做的。
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"); //避免发布的图片和xml中文出现乱码 pro.setActivityFontName("宋体"); pro.setLabelFontName("宋体"); pro.setAnnotationFontName("宋体"); //数据库更更新策略 pro.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE); return pro.buildProcessEngine(); } }
这时,启动项目后,pro.buildProcessEngine()这行代码会初始化Activiti框架,进入里面,会发现它有三种实现,默认是第二种,即ProcessEngineConfigurationImpl。
点进去,Activiti框架具体构建buildProcessEngine方法以下,其中 this.init()的做用是环境初始化,包括配置设置、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()方法里,涉及到责任链模式初始化的方法是this.initCommandExecutors(),里面详情以下:
public void initCommandExecutors() { this.initDefaultCommandConfig(); this.initSchemaCommandConfig(); //初始化命令调用器 this.initCommandInvoker(); //List存放进涉及到的拦截器 this.initCommandInterceptors(); //初始化命令执行器 this.initCommandExecutor(); }
这里只须要关注最后三个方法——
this.initCommandInvoker()
initCommandInvoker()初始化构建了一个CommandInvoker拦截器,它继承上边提到的拦截器抽象类AbstractCommandInterceptor。这个拦截器在整条过滤器链中是最重要和关键,它排在了整条链的最后,其实,它才是最终执行请求的,前边几个拦截器都是传递请求而已。
public void initCommandInvoker() { if (this.commandInvoker == null) { if (this.enableVerboseExecutionTreeLogging) { this.commandInvoker = new DebugCommandInvoker(); } else { //初始化执行该行代码 this.commandInvoker = new CommandInvoker(); } } }
这里 new CommandInvoker()一个对象,而后将地址复制给this.commandInvoker对象引用,注意,该引用将会用在接下来的initCommandInterceptors()方法里——
this.initCommandInterceptors();
initCommandInterceptors方法主要做用是建立一个List集合,而后将须要用到的拦截器都保存到该List集合里——
public void initCommandInterceptors() { if (this.commandInterceptors == null) { this.commandInterceptors = new ArrayList(); if (this.customPreCommandInterceptors != null) { //用户自定义前置拦截器 this.commandInterceptors.addAll(this.customPreCommandInterceptors); } //框架自带默认的拦截器 this.commandInterceptors.addAll(this.getDefaultCommandInterceptors()); if (this.customPostCommandInterceptors != null) { this.commandInterceptors.addAll(this.customPostCommandInterceptors); } //命令调用器,在拦截器链最后一个 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)); } //事务拦截器 if (this.transactionContextFactory != null) { interceptors.add(new TransactionContextInterceptor(this.transactionContextFactory)); } return interceptors; }
可见,方法里的 this.commandInterceptors 就是一个专门储存拦截器对象的List集合——
protected List<CommandInterceptor> commandInterceptors;
这里只须要重点关注this.commandInterceptors.add(this.commandInvoker)这行代码,就是将上边建立的CommandInvoker拦截器对象存储到List里,它是放在initCommandInterceptors()方法最后,某种程度也就意味着,这个拦截器在整条链当中处在最后面的位置。
执行完该this.initCommandInterceptors()方法后,就可获取到全部的拦截器对象,到这一步时,各拦截器仍是互相独立的,仍没法经过next()来进行调用传递,那么,到底是如何将它们串起来造成一条链呢?
接下来的this.initCommandExecutor()方法,就是实现将各拦截器串起来造成一条长链。
this.initCommandExecutor();
该方法有两个做用,一个是生成Interceptor拦截器链,一个是建立命令执行器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)是将集合里的拦截器初始化生成一条拦截器链,先循环获取List集合里的拦截器对象chain.get(i),而后经过setNext()方法在该拦截器对象chain.get(i)里设置下一个拦截器引用,这样,就可实现责任链里所谓每一个接收者都包含对另外一个接收者的引用的功能。
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); } }
那么,这条拦截器链当中,都有哪些拦截器呢?
直接debug到这里,能够看到,总共有4个拦截器对象,按照顺序排,包括LogInterceptor,CommandContextInterceptor,TransactionContextInterceptor,CommandInvoker(在命令模式里,该类至关Invoker角色)。这四个拦截器对象在责任链模式当中充当了具体处理者(Concrete Handler)角色。
责任链模式里剩余客户类(Client)角色应该是命令执行器this.commandExecutor。
所以,工做流引擎当中的责任链模式结构图以下:
组成一条拦截器链以下图所示——
生成拦截器链后,会返回一个(CommandInterceptor)chain.get(0),即拦截器LogInterceptor,为何只返回第一个拦截器呢,这是一个很巧妙的地方,由于该拦截器里已经一层一层地嵌套进其余拦截器了,所以,只须要返回第一个拦截器,赋值给first便可。
接下来,就会建立命令执行器——
this.commandExecutor = new CommandExecutorImpl(this.getDefaultCommandConfig(), first);
这个命令执行器是整个引擎的底层灵魂,经过它,能够实现责任链模式与命令模式——
拦截器链初始化介绍完成后,接下来开始介绍拦截器链在引擎里的应用方式。
Activiti引擎的各操做方法其底层基本都是以命令模式来实现的,即调用上面建立的命令执行器this.commandExecutor的execute方法来实现的,例如自动生成28张数据库表的方法,就是经过命令模式去作具体实现的——
this.commandExecutor.execute(processEngineConfiguration.getSchemaCommandConfig(), new SchemaOperationsProcessEngineBuild());
进入到commandExecutor方法里,会发现前边new CommandExecutorImpl(this.getDefaultCommandConfig(), first)创建命令执行器时,已将配置对象和嵌套其余拦截器的LogInterceptor拦截器对象,经过构造器CommandExecutorImpl(CommandConfig defaultConfig, CommandInterceptor first)生成对象时,传参赋值给了相应的对象属性,其中first引用指向LogInterceptor,即拦截器链上的第一个拦截器——
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); } }
当引擎执行this.commandExecutor.execute(xxx,xxx))相似方法时,实际上是执行了this.first.execute(config, command)方法,这里的this.first在构建命令执行器时是经过LogInterceptor传进来的,所以,执行代码实际上是调用了LogInterceptor内部的execute()方法,也就是说,开始拦截器链上的第一个LogInterceptor拦截器传递方法execute()请求——
进入到拦截器链上的第一个拦截器LogInterceptor。
根据其内部代码能够看出,这是一个跟日志有关的拦截器,内部并无多少加强功能,只是作了一个判断是否须要debug日志打印。若须要,则进行debug打印,若不须要,直接进入到 if (!log.isDebugEnabled()) 为true的做用域内部,进而执行this.next.execute(config, command)用以将请求传递给下一个拦截器作处理。
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; } } }
这里有一个小地方值得稍微打断说下,就这个 if (!log.isDebugEnabled())判断。众生周知,若集成第三方日志插件如logback之类,若其配置里去除debug的打印,即时代码里 存在log.debug("xxxxx")也不会打印到控制台,那么,这里增长一个判断 if (!log.isDebugEnabled())是否屡次一举呢?
事实上,这里并不是画蛇添足,增长这个判断,是能够提高代码执行效率的。由于log.debug("xxxxx")里的字符串拼接早于log.debug("xxxxx")方法执行的,也就是说,即便该log.debug("xxxxx")不会打印,但其内部的字符串仍然会进行拼接,而拼接,是须要时间的,虽然很细微,但一样属于影响性能范畴内的。所以,增长一个if判断,若无须要打印debug日志时,那么就无需让其内部的字符串进行自动拼接。
这是一个很小的知识点,但面试过程当中实际上是有可能会遇到这类与日志相关的面试题的。
接下来,让咱们继续回到拦截器链的传递上来。
LogInterceptor拦截器调用this.next.execute(config, command),意味着将请求传递到下一个拦截器上进行处理,根据前边分析,可知下一个拦截器是CommandContextInterceptor,根据代码大概可知,这个拦截器内主要是获取上下文配置对象和信息相关的,这些都是在工做流引擎初始化时生成的,它们被保存在Stack栈里,具体都保存了哪些信息暂不展开分析——
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()); } //继续将命令请求传递到下一个拦截器 Object var5 = this.next.execute(config, command); return var5; } catch (Exception var31) { context.exception(var31); } finally { ...... } return null; } }
CommandContextInterceptor拦截器没有对命令请求作处理,它继续将请求传递到下一个拦截器TransactionContextInterceptor,根据名字就大概能够猜到,这个拦截器主要是增长与事务有关的功能——
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拦截器一样没有对命令请求作处理,而是继续传递到下一个拦截器,也就是最后一个拦截器CommandInvoker,根据名字能够大概得知,这是一个与命令请求有关的拦截器,传递过来的请求将会在这个拦截器里处理——
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(); } }
进入到其内部,能够发现,这里没有再继续调用this.next.execute(config, command)这样的请求进行传递,而是直接执行command.execute(commandContext),而后将返回值进行返回,其中,command是请求参数当中的第二个参数,让咱们回过头看下该请求案例最开始的调用——
this.commandExecutor.execute(processEngineConfiguration.getSchemaCommandConfig(), new SchemaOperationsProcessEngineBuild());
这里的第二个参数是new SchemaOperationsProcessEngineBuild(),不妨进入到SchemaOperationsProcessEngineBuild类中,是吧,其内部一样有一个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; } }
可见,CommandInvoker拦截器内部执行command.execute(commandContext),就至关于执行了new SchemaOperationsProcessEngineBuild().execute(commandContext),也就是——
public Object execute(CommandContext commandContext) { DbSqlSession dbSqlSession = commandContext.getDbSqlSession(); if (dbSqlSession != null) { dbSqlSession.performSchemaOperationsProcessEngineBuild(); } return null; }
这是一种命令模式的实现。
本文主要是分析责任链模式在Activiti框架中的实践,故暂不展开分析框架中的其余设计模式,有兴趣的童鞋能够自行深刻研究,在Activiti框架当中,其操做功能底层基本都是以命令模式来实现的。
至此,就大概分析完了责任链模式在Activiti框架的建立和应用,学习完这块内容,我对责任链模式有了更好理解,相对于看网上那些简单以小例子来介绍设计模式的方法,我更喜欢去深刻框架当中学习其设计模式,这更能让我明白,这种设计模式在什么场景下适合应用,同时,能潜移默化地影响到我,让我在设计系统架构时,能明白各设计模式的落地场景具体都是怎样的。