工作流引擎,应用于解决流程审批和流程编排方面等问题,有效的提供了扩展性的支撑。而目前来说,工作流领域也有了相对通行化的标准规范,也就是 BPMN2.0。支持这个规范的开源引擎主要有:Activiti,flowable,Jbpm4 等。本文着重对 Activiti 的架构设计进行分析和梳理,同时对流程启动和原子操作的相关代码进行完整走读。
本文的阅读对象需要对 Activiti 有一定的理解并且已经能够初步的使用 Activiti 进行流程流转方面开发。
Activiti 架构分析及源码详解- Activiti 架构分析及源码详解
- 引言
- 一、Activiti 设计解析-架构&领域模型
- 1.1 架构
- 1.1.1 命令模式
- 1.1.2 责任链模式
- 1.1.2.1 事务拦截器
- 1.1.2.2 命令上下文拦截器
- 1.1.3 流程定义解析
- 1.2 领域模型
- 1.2.1 数据集中提交
- 1.2.2 PersistentObject
- 1.2.3 DbSqlSession
- 1.1 架构
- 二、Activiti 设计解析-PVM 执行树
- 2.1 核心理念
- 2.1.1 PVM 对流程定义期的描述
- 2.1.2 PVM 对流程运行期的描述
- 2.1.3PVM 综述
- 2.2 ActivitiImpl 与作用域
- 2.3 ExecutionEntity
- 2.3.1 单独的非作用域节点
- 2.3.2 单独的作用域节点
- 2.3.3 并发的非作用域节点
- 2.3.4 并发的作用域节点
- 2.1 核心理念
- 三、代码解析-流程启动
- 3.1 流程说明
- 3.1.1 流程定义实体创建流程实例
- 3.1.2 流程实例启动
- 3.2 额外补充
- 3.2.1 ActivityImpl 的 parent 属性
- 3.1 流程说明
- 四、代码解析-原子操作
- 4.1 说明
- 4.2 AbstractEventAtomicOperation
- 4.3 AtomicOperationProcessStart
- 4.4 AtomicOperationProcessStartInitial
- 4.5 AtomicOperationTransitionNotifyListenerEnd
- 4.6 AtomicOperationTransitionNotifyListenerStart
- 4.3 AtomicOperationActivityExecute
- 4.4 AtomicOperationTransitionDestroyScope
- 4.5 AtomicOperationTransitionNotifyListenerTake
- 4.6 AtomicOperationTransitionCreateScope
工作流引擎,应用于解决流程审批和流程编排方面等问题,有效的提供了扩展性的支撑。而目前来说,工作流领域也有了相对通行化的标准规范,也就是 BPMN2.0。支持这个规范的开源引擎主要有:Activiti,flowable,Jbpm4 等。本文着重对 Activiti 的架构设计进行分析和梳理,同时对流程启动和原子操作的相关代码进行完整走读。
本文的阅读对象需要对 Activiti 有一定的理解并且已经能够初步的使用 Activiti 进行流程流转方面开发。
如果对文章内容有疑惑,欢迎加入技术交流群 186233599,作者会不定时解答相关问题。
一、Activiti 设计解析-架构&领域模型 1.1 架构Activiti 采用了一个分层架构完成自底向上的包装。架构图如下
大致包括:
- 核心接口层,被 PVM 接口定义。PVM 会在后面的章节中详细讲述。
- 核心实现层,基于 PVM 的思想和接口,定义了一些关键实体包含 ActivityImpl(该类抽象了节点实现),FlowElementBehavior 实现(该类抽象了节点指令动作),ExecutionImpl(流程执行实体类)
- 命令层,Activiti 在编码模式上直接限定整体风格为命令模式。也就是将业务逻辑封装为一个个的 Command 接口实现类。这样新增一个业务功能时只需要新增一个 Command 实现即可。这里需要特别提到的是,命令本身需要运行在命令上下文中,也就是 CommandContext 类对象。
- 命令拦截层,采用责任链模式,通过责任链模式的拦截器层,为命令的执行创造条件。诸如开启事务,创建 CommandContext 上下文,记录日志等
- 业务接口层,面向业务,提供了各种接口。这部分的接口就不再面向框架开发者了,而是面向框架的使用者。
- 部署层,严格来说,这个与上面说到的并不是一个完整的分层体系。但是为了突出重要性,单独拿出来说。流程运转的前提是流程定义。而流程定义解析就是一切的开始。从领域语言解析为 Java 的 POJO 对象依靠的就是部署层。后文还会细说这个环节。
- 流程引擎,所有接口的总入口。上面提到的业务接口层,部署层都可以从流程引擎类中得到。因此这里的流程引擎接口其实类似门面模式,只作为提供入口。
Activit 整体上采用命令模式进行代码功能解耦。将流程引擎的大部分涉及到客户端的需求让外部以具体命令实现类的方式实现。
完成这个编码模式,有几个重点类需要关注
Command
命令接口,所有的具体命令都需要实现该类,最终业务就是执行该类的 execute 方法。CommandContext
命令上下文,为具体命令的执行提供上下文支撑。该上下文的生成是依靠命令拦截器中的上下文拦截器org.activiti.engine.impl.interceptor.CommandContextInterceptor
来生成的。该拦截器会判断是复用当前的上下文还是生成新的上下文。
引擎内的大部分功能都是通过单独的命令完成。
1.1.2 责任链模式Activiti 的命令模式还需要搭配其对应的责任链来完成。具体来说,Activiti 中存在一个命令拦截器链条,该命令拦截器链条由几大块的拦截器实现组成,如下
其中重要的默认拦截器有 2 个:
- 事务拦截器,主要职责是使得后续命令运行在事务环境下。
- CommandContext 拦截器,主要职责是在有必要的时候创建 CommandContext 对象,并在使用完成后关闭该上下文。
事务拦截器是否提供取决于org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl
的子类对方法createTransactionInterceptor
的实现。独立使用时的org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration
该方法返回为空。也就是不提供事务拦截器。此时,命令的运行就无法通过事务拦截器来提供事务环境了。
实现类:org.activiti.engine.impl.interceptor.CommandContextInterceptor。
该拦截器的功能非常重要,可以说是 Activiti 操作的核心之一。其作用是在后续拦截器执行前检查当前上下文环境,如果不存在 CommandContext 对象,则创建一个;在后续拦截器执行后,将 CommandContext 对象 close。CommandContext 包含了本次操作中涉及到所有的数据对象。
1.1.3 流程定义解析Activiti 遵循 BPMN2.0 规范,因此框架中少不了对 BPMN2.0 规范的定义文件(XML 形式)的解析类。Activiti 采用的 STAX 的拉模型进行 XML 解析。这里先不分析其具体的解析类的内在联系,而是概念性的阐述下 Activiti 对解析的概念分层。
首先通过类org.activiti.bpmn.converter.BpmnXMLConverter
进行 XML 解析,解析为org.activiti.bpmn.model
包下面的与各个 XML 元素定义对应的 POJO 类。此时这些 POJO 类仅仅只是 XML 文件的一个 Java 表达。
在通过类org.activiti.engine.impl.bpmn.parser.BpmnParser
聚合不同的解析类,将上面步骤解析出来的 POJO 类进一步解析为可以在框架中利用的org.activiti.engine.impl.pvm.process
包下面的类。典型的代表就是 ActivityImpl 类。
三者之间的关系简单用图表达就是
Activiti 采取了领域中的充血模型作为自己的实现方式。大部分业务逻辑都直接关联在了org.activiti.engine.impl.persistence.entity.ExecutionEntity
中。由于 Activiti 采用了 MyBatis 而非 Hibernate 这样的 O/R Mapping 产品作为持久层,因此 Activiti 在具体的持久化操作上也有自己的独特方式。
Activiti 的持久化机制简单说来就是数据集中提交。集中提交还产生了一个额外的作用:自动提交。换句话说,在内存中的实体,如果更新了属性但是没有显示的执行刷新动作,在一个调用的生命周期结束后也会被持久化其最新的状态到数据库。下面来看下详细解释下这个集中提交机制。
在 Activiti 所有运行期生成的对象都需要实现一个接口org.activiti.engine.impl.interceptor.Session
,其定义如下
public interface Session{ void flush(); void close();}
而 Session 对象则是由接口org.activiti.engine.impl.interceptor.SessionFactory
的方法进行生成,其定义如下
public interface SessionFactory { Class getSessionType(); Session openSession();}
流程引擎内部持有各种 SessionFactory 实现,用户也可以自定义注册自己的 SessionFactory 实现,如果用户希望自定义的对象也可以被集中提交机制处理的话。
在 CommandContext 中存在一个 Map存储,存储着该 CommandContext 生命周期内新建的所有 Session 对象。当一个命令执行完毕后,最终命令上下文 CommandContext 的 close 方法会被调用。当执行 CommandContext.close()方法时,其内部会按照顺序执行flushSessions
,closeSessions
方法。从名字可以看到,第一个方法内部就是执行所有 Session 对象的 flush 方法,第二个方法内部就是执行所有的 Session 对象的 close 方法。
流程引擎内部有一个 Session 实现是比较特别的。也就是org.activiti.eng