简介
activiti里的api服务核心是它的命令和拦截器,所以我们有必要了解它的运行机制。
activiti里用到的设计模式
activiti的整体架构采用了外观模式(Facede)、命令模式(Command)、拦截器模式(Interceptor)这三种主要的设计模式,activiti里的所有API均是在此基础上实现的。
外观模式的体现:activiti的api设计指导思想是“面向接口编程”,我们常说的七大Service(activiti6里是七个service,activiti7里有所改动)都是用抽象定义的方式来提供,但引擎的内部实现不对外公开,开发人员只要调用相应的接口实现功能即可。如果有自定义的需求实现,可以实现对应的接口来覆盖引擎内部默认的实现。
命令模式的体现:翻开activiti的源码,我们可以看到activiti的每一个操作都对应一个命令接口实现类,这样把不同的功能分散在N个命令中,易于维护和扩展。当调用某个XXXService的时候,其实是调用的对应的Command,但接口并不是直接调用命令,而是把命令交给CommandExecutor统一执行,因为通过它可以在命令执行过程中执行若干拦截器(类似j2ee里的Filter链)。
这边以最常用的taskService.complete(taskId)来看看源码。
拦截器的体现:拦截器大家应该都不会陌生,spring里经常会用到拦截器,例如spring的事务管理。activiti里也是类似,通过拦截器可以为所有的Command准备好命令上下文(CommandContext)对象,从而可以在Command实现类中获取到引擎配置对象、会话对象以及其它扩展属性,还可以为Command提供事务管理器、乐观锁自动重试等功能。
命令与拦截器的源码分析
通过上面的源码查看,可以知道,每个命令都实现了org.activiti.engine.impl.interceptor.Command
接口,就是下图。
可以看到该接口只有一个方法execute(CommandContext commandContext)
,里面的参数CommandContext却非常重要,里面提供了获取数据库、事务管理器、扩展属性等资源。
activiti引擎在初始化时会初始化一系列命令拦截器,其中最重要的是CommandContextInterceptor,接口Command的CommandContext参数就是由它来提供。
若要分析它是如何来提供的,可以看源码里的这个类ProcessEngineConfigurationImpl。
默认拦截器的代码片段
读取所有拦截器的方法
commandInvoker是最终调用命令的处理类,在调用命令时会同时把CommandContext对象传递给具体的命令处理类。
commandInvoker就是一个CommandInterceptor,所有的拦截器都要实现CommandInterceptor接口。
可以看到CommandInterceptor接口里面有getNext、setNext两个方法,这个Next是下一个拦截器的对象引用,这和j2ee里的Filter链有异曲同工之妙,N个拦截器通过Next就串联成了一个拦截器链。
那么,请问,第一个拦截器是什么?
上面getDefaultCommandInterceptors()源码截图有写明,是日志拦截器,然后如果有事务拦截器,它就是第二个,最后才轮到上下文拦截器。
那么,再请问,第一个拦截器是如何被调用的呢?
还记得前面有说所有的命令最后都是交给CommandExecutor来执行的,该类在初始化时把first变量作为构造参数传递。
所以CommandExecutor是命令执行的入口。
实战
《 自定义命令实现任务的回退、跳转 》
博主,赞