Flowable引擎节点流转工作原理

1. 背景介绍


        当我们部署一个流程并启动后,Flowable会按照既定流程定义及进行节点处理以及自动流转,从一个节点执行到下一个节点,直至结束。在此过程中,系统如何处理BPMN XML文件?节点如何进行流转?本文对flowable内部源码进行解析,挖掘其引擎原理。

        文章末尾,会通过一个简单流程执行实例进行讲解,还原其执行步骤。

       注意:本文源码基于Flowable 6.4.0版本,gitthub地址:https://github.com/flowable/flowable-engine/tree/flowable-release-6.4.0

2. 系统原理


2.1. 总览图

总览图

如上图所示为流程启动关键节点图,主要由以下几部分组成:

1. 启动命令封装(橙色部分)

   主要功能:通过将bpmnXML格式文件解析为程序可执行的内存对象:Process。

   a. 获取流程定义 ProcessDefinition(通过入参<流程key or 流程id>找到对应的流程定义)

   b. 加载解析bpmn.xml文件(工作流原始文件)

   c. 生成bpmnModel(工作流描述)

   d. 生成Process(工作流执行)

2. 命令执行器(蓝色部分)

主要功能:流程执行过程中会添加各种拦截器其中最为核心的是CommandInvokerInterceptor

  a. LogInterceptor:当日志级别为debug时,会输出启动日志

  b. SpringTransactionInterceptor

      Spring事务管理,用户事务的doTransaction、commit、rollback

  c. CommandContextInterceptor:执行SqlSession close方法,将数据进行持久化

  d. TransactionContextInterceptor

  e. BpmnOverrideContextInterceptor

  f. CommandInvokerInterceptor:真正执行启动流程命令的拦截器

3. 节点流转(绿色部分)
   主要功能:完成对流程节点的执行,及找到下一个可执行的节点,直至流程运行完成。其最核心的包括DefaulFlowableEngineAgenda,该类持有operations,通过对节点操作封装为不同的Operation,进行节点的流转。

2.2. 命令执行器(CommandExecutor)

      Flowable是通过命令模式 + 责任链模式完成对操作的执行,flowable启动代码中,构造ProcessEngine时会调用 init()方法进行引擎初始化,如下图所示:

        由下图可得,在init()方法中,会调用 initCommandExecutors()对命令执行器进行初始化,其中包括各种拦截器,包括:commandInvoker、SpringTransactionInterceptor及其他拦截器

     每个拦截器都是 CommandInterceptor接口类的实现,最终是通过execute()方法,对包装好的Command命令进行执行。再这里注意下拦截器的加载顺序,就是拦截器的执行顺序,这里 CommandInvoker拦截器是放到了最后,该拦截器也是真正推动流程节点进行流转的。

2.3. 启动命令封装

      启动命令通过入参的流程key or  流程定义id加载对应的流程定义数据后,初始化了启动operation操作,其核心源码如下:

1) 调用new StartProcessInstanceCmd 生成启动命令

2)StartProcessInstanceCmd.execute() 方法定义如下:其首先获取流程定义ProcessDefinition,之后通过调用startProcessInstance启动流程。

      流程定义数据来源于数据表:ACT_RE_PROCDEF,其是在流程部署时写入的,注意其字段deploymentId,在作为数据表ACT_GE_BYTEARRAY的外键,关联了 流程XML原始文件。




3)在得到流程定义后,通过调用 ProcessInstanceHelpercreateProcessInstance方法进行流程启动,注意在该方法中,通过调用 ProcessDefinitionUtil.getProcess 得到流程 Process

     注意在之后,通过 process.getInitialFlowElement 方法得到流程初始节点。在之后构造 Execution 实体时,设置该初始节点。



4)在方法 ProcessInstanceHelper.startProcessInstance 中,通过调用CommandContextUtil.getAgenda(commandContext).planContinueProcessOperation(execution) 将当前实例添加到操作堆栈operations中。

           至此:完成了启动命令的初始化过程


2.4流程节点流转(重点)

2.4.1 节点流转核心节点图

重点:上图为节点流转图,节点流转的核心是AbstractAgenda,其默认的实现是 DefaultFlowableEngineAgenda,该类持有变量operations,operations是个一个堆栈,在流转过程中,通过DefaultFlowableEngineAgenda一系列操作可以

       将不同的操作压入栈中,然后运转过程中,将每一个操作弹出栈进行命令的执行。这里的operation指默认实现了AbstractOperation的操作类,AbstractOperation的实现类如下所示:

        比如要继续流转,则压入ContinueProcessOperation,需要结束流程则需要压入 EndExecutionOperation。


2.4.2. ContinueProcessOperation操作

        ContinueProcessOperation操作对节点和顺序流分别作了处理,如下图所示:

当前元素为节点时,执行如下主要操作:

1)标记节点开始

2)执行节点监听器

3)获取节点对应行为器

4)执行行为器

当无对应的行为器时,则创建 takeOutgoingSequenceFlowsOperation 操作


当前元素为顺序流时,执行如下主要操作:

1)找到当前元素后置元素,设置为当前执行元素

2)如果后置元素是节点,则直接执行

3)如果后置元素是顺序流,则构造ContinueProcessOperation操作


2.4.2.1. 节点行为器ActivityBehavior

      如下图所示为行为器的实现类,从图中可以看到我们熟悉的StartEvent、NoneEvent及ServiceTask的实现类。

在行为器执行完成对应操作后,由行为器决定是否继续向后流转节点FlowNodeActivityBehavior默认提供了leave()方法,其内部主要是构造 TakeOutgoingSequenceFlowsOperation 继续流转节点

2.4.3. TakeOutgoingSequenceFlowsOperation操作

       TakeOutgoingSequenceFlowsOperation的实现分别对节点和顺序流进行了操作,如下图所示:

当前元素为节点时:

1)首先标记当前节点执行完成

2)遍历后置顺序流元素,并计算顺序流表达式属性

3)当无有效的后置顺序流元素时,则构建EndExecutionOperation,压入栈中

4)存在有效的后置顺序流元素时,则遍历顺序流并且构造 ContinueProcessOperation操作,压入栈中

当前元素为顺序流时:

1)标记当前元素结束,构造ContinueProcessOperation操作,压入栈中

2.5 Example

     下面以该流程为例,详细列出操作流转步骤:

     节点流转图如下:

     详细步骤如下:

Step 1:在流程启动初期,operations堆栈中压入ContinueProcessOperation操作,传入的execution的当前节点为开始节点

Step 2:取出栈顶操作元素开始节点并执,首先进行节点开始持久化操作,之后调用节点行为类执行,开始节点的行为类是 NoneStartEventActivityBehavior,如下图所示为空操作,

其并没有实现execute()方法,还是复用了父类FlowNodeActivityBehavior的execute()方法,执行leave方法,压入TakeOutgoingSequenceOperation操作,此时execution的当前节点为开始节点

Step 3:弹出栈顶TakeOutgoingSequenceOperation(开始节点),首先进行节点结束持久化操作,之后获取开始节点的后置可用分支,压入ContinueProcessOperation堆栈,

此时execution的当前节点为 开始节点-创建变量节点顺序流

Step 4:取出栈顶ContinueProcessOperation(开始节点-创建变量节点顺序流) 并执行,其目标节点为 创建变量节点,开始执行,首先执行节点开始持久化操作,接着执行其行为类JavaDelegate,

执行完成后,压入TakeOutgoingSequenceOperation操作,此时execution的当前节点为创建变量节点

计算节点后置节点以及其他逻辑后,压入操作类,此时execution的当前节点为开始节点-->创建变量的连线。

Step 5:弹出栈顶TakeOutgoingSequenceOperation(创建变量节点),首先进行节点结束持久化操作,之后获取创建变量节点的可用分支,压入ContinueProcessOperation堆栈,

此时此时execution的当前节点为创建变量节点-结束事件顺序流

Step 6:弹出栈顶ContinueProcessOperation(创建变量节点-结束事件),其目标节点为结束事件,开始执行,首先进行节点开始持久化操作,接着执行其行为类 NoneEndEventActivityBehavior,如下图可得,

结束事件行为器构造了TakeOutgoingSequenceOperation操作,此时execution的当前节点为

结束事件节点

Step 7:弹出栈顶TakeOutgoingSequenceOperation(结束节点),首先进行节点结束持久化操作,之后获取创建变量节点的可用分支,无可用分支,则构造 EndExecutionOperation,此时execution的当前节点为结束节点

Step 8:弹出栈顶EndExecutionOperation(结束节点),执行相关结束操作

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,378评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,356评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,702评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,259评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,263评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,036评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,349评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,979评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,469评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,938评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,059评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,703评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,257评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,262评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,501评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,792评论 2 345