https://www.huangdf.xyz/categories/study-notes
南风
南风
发布于 2025-06-17 / 1 阅读
0
0

Activiti7工作流介绍及使用

1、能做什么

Activiti7是一个基于BPMN的工作流引擎,提供了丰富的节点类型来满足业务流程建模需求。适用于各种需要工作流管理的场景,例如办公自动化,业务流程管理。

常见的各种工作流就是通过activiti构建的,例如入职、离职审批;用章审批;请假审批等等都可以通过Activiti实现。

它的优势在于灵活,可以修改流程定义,无需修改代码;可拓展,提供了丰富的api以及拓展点,可以根据需求自定义功能;标准化,基于BPMN标准,流程定义具有良好的可视化和可移植性。

综上,在一些流程审批类的业务中,activiti能有非常亮眼的操作,例如OA系统。

以下是基于7.1.0-M6版本的一些内容。

2、怎么做

2.1、安装插件

由于插件兼容性的问题,貌似是再ide19版本之后原来的的插件就不支持了,但是有新的插件可以用:activiti-bpmn-visualizer,这个插件也能实现将xml文件转换成图形化的界面。

2.2、创建一个activiti文件

如图所示,右键创建选择bpmn。这个在我的理解中是属于静态文件,所以我选择在resource文件夹下面再创建一个文件夹来存放这类文件。但也不一定非要放在这里,部署的时候可以任意选择路径的。

然后就得到了这样一个xml文件,本质上是一个xml文件,对自己有信心的也可以直接编辑xml文件,我还是不折磨自己了。

选中文件右键,选择View BPMN****,然后就可以进入图形化界面来编排流程了。

2.3、各种流程节点

大多数内容来源于官方文档。官方文档链接如下:

Activiti 用户指南 --- Activiti User Guide

2.3.1、开始节点

2.3.1.1、start event

最基本的开始节点,需要手动启动流程

2.3.1.2、start timer event

能够在指定时间启动的流程,支持一次性启动和周期性启动

这里可以选择启动方式。

time-date:什么时候启动,一次性启动。

time duration:等待多久启动。PT5M表示等待5分钟再启动。

time cycle:多久循环一次。格式为:重复次数/开始时间/重复间隔/结束时间。开始结束时间是可选配置,也可以通过参数来定义。R3/PT10H表示重复三次,每次间隔十小时,如果有enddate的话则表示,到了enddate的时间,不管有没有完成指定的次数,都将停止。此外还能通过corn表达式来指定执行时间。

需要注意的是,由于定时启动是开启一个新的线程来执行任务,所以ThreadLocal相关变量会获取不到。

2.3.1.3、message start event

这个节点适用于需要由外部触发的事件,接收到特定消息后启动流程。需要注意的是,消息启动事件的名称,在所部署的流程中必须唯一,否则会抛出异常,以及,只有顶层流程支持消息启动,子流程、嵌套流程不支持消息启动。

定义节点后需要指定message reference。并在当前xml文件process标签之外定义信号<message/>,此外还需要代码中在通过runtimeservice发出广播。

如果在发送消息时指定了executeonid,则只会向特定的流程发信息。

2.3.1.4、single start event

信号启动事件是在接收到特定信号的时候才启动,类似消息启动事件,区别在于信号是广播的,所有监听这个信号的收到信号后都会执行流程。

定义节点后需要指定single reference。并在当前xml文件process标签之外定义信号<signal/>,此外还需要代码中在通过runtimeservice发出广播。

如果在发送消息时指定了executeonid,则只会向特定的流程发信号。

2.3.1.5、error start event

错误启动事件。捕获到特定错误时启动的流程,只能用在子流程中,不能用在主流程中。而错误也常由错误结束事件或者错误边界事件抛出

2.3.2、Activities节点

2.3.2.1、UserTask

用户任务节点。用户任务必须由用户来完成。当流程执行到用户任务时,会创建一个新的任务并把这个任务加入到分配人或者群组的任务列表中。

参数解释:

基础标识属性:

id:唯一标识。

name:任务名称。

documentation:任务描述。

任务分配属性:

assignee:输入固定的字符串则是固定分配,通过uel表达式注入则是动态分配例如${userA}

candidate users:多个候选人。用于组任务分配。范例:["userA","userB"]

asynchronous:异步延续。若设置为true,任务提交后流程继续执行由异步作业处理,避免长事务阻塞。

due date:超时时间。超时之后不会将任务删除而是标记为已超时。

业务关联参数:

form properties:表单属性。不常用。

business key:业务关联键。启动流程时关联业务id。一般用时间戳来唯一标识一条数据。

is for compensation:用于标记补偿任务,当抛出补偿边界事件时触发该任务执行补偿任务,例如,订单支付失败时,触发取消订单。补偿仅在事务子流程或者显示补偿范围生效,不影响主流程其他部分。需要注意的是,补偿事务不能通过startprocesinstencebykey启动,必须依赖补偿事件触发。

execution listeners:监听器。监听任务事件并触发业务逻辑。能够在任务创建时、任务分配、任务完成、任务删除时触发。包括但不限于初始化表单、通知任务人会签、更新业务状态等操作。需要注意的是,这里的监听器需要写全限定名。监听器实现tasklistener这个接口即可。

priority:优先级0-100,只影响任务排序

3.2.2.2、ServiceTask

特点

该节点是实现自动化业务逻辑的核心组件,代表流程中无需人工干预的自动执行节点。其核心作用是:

自动化执行:代替人工执行任务,例如数据库交互、服务调用等。

系统集成:连接外部系统,消息队列、微服务、调用api等。

数据处理:在流程中计算、校验、转换数据等。

决策支持:执行条件判断并设置流程变量。

参数解释:

id:唯一标识。

name:任务名称。

documentation:补充描述,可以不填。

is for compensation:是否是补偿任务。同user task。

asynchronous:是否异步。同user task。

exclusive:是否排他执行,默认是勾选的。可以避免并发问题。

expression:表达式注入。通过uel表达式注入被spring管理的bean,实现调用接口。具体写法是${beanName.methodName(object args...)},参数自定义。

delegate expression:代理表达式注入。注入方式同expression,${expression}。

class:java类实现。填入全限定名即可。类实现JavaDelegate。

result variable name:

failed job retry cycle:任务失败的策略,按照ISO 8601规范填写,例如R3/PT10H(重试三次,每次间隔十小时)

fields:注入字段,可以搭配上面的代理、表达式、Java类注入特定的字段,例如定义一个field名为name,值为aaa,当类实现javadelegate后添加一个类型为express的私有属性且变量名为name,则可以通过name.getvalue()获取到配置的aaa。

execution listeners:监听器,实现方式同user task

2.3.2.3、ScriptTask

特点

脚本任务是工作流引擎中的一个特殊任务,执行到此处时,会执行其中的脚本。结合官方文档7.1.0-M6是不支持使用外部编写好的脚本的。脚本内容可以是普通的脚本,也可以是对流程变量的修改之类的操作。

参数解释:

id:唯一标识。

name:任务名称。

documentation:补充描述。

is for compensation:是否是补偿任务,同上。

asynchronous:是否支持异步。

script body:脚本内容,可以是脚本语言,也可以是引用的外部脚本。

script format:必填项,脚本类型,JavaScript、groovy、python、juel等。除了js、 juel都需要添加额外的依赖。

auto store variables:是否自动将脚本变量存储为流程变量,默认是false。

execution listeners:监听器,用法同上。

2.3.2.4、BusinessRuleTask

特点

可以针对一个任务流程定义多个规则,一个流程能通过的情况可能有多种,这里的很多种就可以抽象成业务规则。这个任务需要配合drl文件使用,主要是rules这个配置项,可以配置多个规则,而规则需要预先定义好并放在drl文件中。部署的时候将三个文件全部通过bar压缩包形式部署。

参数解释:

rule variable input:传递给规则引擎的输入变量,可以使用uel表达式传递,多个值用逗号隔开(${a,b,c,d}),在规则文件中通过$a\$b\$c\$d获取输入的值。

result variable:结果变量。规则引擎执行结果的流程变量名。类型是LIst<Object>,如果不指定的话会使用默认的变量名(org.activiti.engine.rules.OUTPUT)

rules:使用的规则。多个规则用逗号隔开(rule1,rule2...)

exclude:配合rules使用,若为true,则表示排除以上规则,反之则是包含以上规则。

要使用这个功能还需要配合另一个依赖

<dependency> 
  <groupId>org.drools</groupId> 
  <artifactId>drools-core</artifactId> 
  <version>7.73.0.Final</version> 
</dependency> 
<!-- 其他必要依赖:kie-api, drools-compiler --> 

2.3.2.5、ReceiveTask

特点

该任务为接收任务,是一个非主动的任务流程,当任务执行到这里时会进入同步等待状态,直到显示的调用了执行流程(RuntimeService.signal(executionId))任务才会继续执行。且该任务数据不存在ru-task表中,而是ru-execution表中。

参数解释

上面都有的。不再赘述。

2.3.2.6、ManualTask

手动任务。对于工作流引擎来说,这个任务是一个直通任务,当流程执行到此处时,会自动通过,这个任务的意义是在流程中留痕。

2.3.2.7、ServiceTask衍生

2.3.2.7.1、MailTask

特点

该任务是归属于servicetask下的一个分类,本质上还是一个servicetask,不过type被定义成了mail(activiti:type="mail")。

<serviceTask activiti:type="mail" id="sid-5c4f410f-be2d-40e9-8040-e5fb61a91112"/>
参数解释:

to:收件人。多个收件人用逗号隔开。

from:发送地址,如果不提供则会使用默认的地址。默认地址需要在项目配置文件中自行配置。后面的信息我乱写的。

activiti:
  mail-server-default-from: xxx@xxx.com ##默认邮箱,默认是activiti@activiti.org
  mail-server-host: mail ##右键服务器主机名,默认是localhost
  mail-server-password: pas ##邮箱密码
  mail-server-port: 123 ##端口,默认25,通过SMTP
  mail-server-use-ssl: true ##使用ssl,默认false
  mail-server-use-tls: true ##使用tls,默认false
  mail-server-user-name: aaa ##

subject:电子邮件的主题。

cc:抄送。多个抄送人用逗号分隔。

bcc:密件抄送。多个密件抄送用逗号隔开。

text:普通的邮件内容。可以和html结合,以防客户端不支持富文本格式的电子邮件。

html:html格式的电子邮件,也是右键内容。

2.3.2.7.2、CamelTask

特点

可以在不同系统中传递数据。这个涉及到camel这个规则引擎了,不赘述,因为我也不懂。camel引擎主要功能是在不同系统之间集成和处理数据,作用类似一个胶水将不同系统粘合或者说像一个管道,将不同系统联通起来。

Activiti CamelTask(骆驼任务) - Jesai - 博客园

更多的东西去这个帖子看吧。

2.3.2.7.3、MuleTask

特点

mule任务允许想mule发送消息,增强了activiti的集成功能。mule也是作为servicetask的衍生存在的,只需要在servicetask中定义type为mule即可。

参数解释:

endpoint url:想要调用的mule端点。

language:用于定义payloadexpression的语言类型。

payload expression:类型是一个表达式,是消息的负载。

result value:存储调用结果的变量。

2.3.2.8、decision task

这个任务在官方文档中没有记载,且在22版ide中通过图形化插件创建出来的任务在xml中显示是报错的红色。所以暂且不提这个。

2.3.3、Structural节点

2.3.3.1、sub process

特点

这个节点是子流程,不能独立存在,是大流程中的一部分。子流程中包含网关、事件等,是一个完整的流程。也被称为嵌入子流程。子流程允许分层建模,许多建模工具允许折叠子流程,隐藏子流程的细节,并显示业务流程的高级端到端概述。

子流程也能创建新的边界事件。在子流程执行期间引发的事件,能够被子流程边界上的边界时加所捕获,从而为这个事件创建一个仅限于子流程的

子流程必须有一个nonestart节点,不允许是其他事件,和至少一个结束事件。此外序列流不能跨越子流程。

参数解释:

is transactional subprocess:默认false。这个参数可以给子流程换个身份,是否是事务子流程。可以将多个活动进行分组,方便其中同时成功或者失败。一个事务子流程中可能包含多个独立的流程,而这些流程一旦执行就已经提交了,所以想要通过原始的事务来实现同时成功或者失败是不现实的,所以这里可以通过补偿边界事件来实现事务的回滚。定义一个边界事件,当其中某个流程出现了问题,边界事件捕获这个问题,从而执行特定事件的回滚。

3.2.3.2、event sub process

特点

事件子流程 在图形化界面是一个虚线框。没有额外的参数。时间子流程是由事件触发的子流程,当主流程触发了特定事件时,就会触发事件子流程。例如当主流程执行到某个步骤,执行到了error end event,程序异常结束,而事件子流程中开始节点捕获了这个error,那么事件子流程就会开始执行。事件子流程还可以嵌入到子流程当中,具体操作就是直接把这个虚线框拖到子流程的框中即可。

参数解释:

无额外参数。

3.2.3.3、CallActivity

特点

调用子流程和上面的事件子流程类似,都是执行到这个活动时执行一个新的流程。区别在于,事件子流程执行的是当前流程的活动,而调用子流程可以执行别的流程的活动。

参数解释:

called element:被引用的流程的id。

called element type:被引用流程类型。

inherit parent variables:继承自父类得变量。

在官方文档中对于参数的传递是通过in和out两个标签进行的。in表示流程开始时传入的变量,out表示流程结束后返回的变量。文档中没有提到关于type和inherit这两个参数,这两个参数具体什么作用也无从得知。

<callActivity id="callSubProcess" calledElement="checkCreditProcess" >
  <extensionElements>
	  <activiti:in source="someVariableInMainProcess" target="nameOfVariableInSubProcess" />
	  <activiti:out source="someVariableInSubProcess" target="nameOfVariableInMainProcess" />
  </extensionElements>
</callActivity>


2.3.4、gateways

网关的作用很大,和task结合能够迸发出意料之外的火花。网关有排他/独占网关、并行网关、包容性网关、事件性网关这四类

2.3.4.1、Exclusive Gateway

排他网关的形状是一个菱形中有一个×,X表示OR的意思。排他网关用于对过程中的数据决策进行建模,说通俗点就是在数据流上定义一个判断,true则输出,false则不输出,所有经过这个网关的东西都要走一遍这个判断。如果存在多个条件为true,则执行且只执行第一个为true的流程。若全都是false则会触发异常,异常可以通过边界事件处理,或者执行异常结束。

排他网关的使用则是在经过网关的输出序列流上定义条件表达式,通过条件表达式的真假来确定执行哪一个流程。

2.3.4.2、Parallel Gateway

并行网关的形状是零星中有一个+号,+表示AND的意思。它允许分叉到多个执行路径或加入多个传入的执行路径。并行网关的功能是基于传入和传出的序列流。

传出:为每个传出的序列流创建一个并发执行。

传入:到达网关的序列流都将在网关等待,直到每个传入的序列流都进入网关,然后这个流程才继续。

一个并行网关可以同时拥有多个传入和传出,此时需要等待所有传入均到达,再为每个传出创建并行路径。

值得注意的是并行网关不需要平衡,即输入的序列流和输出的序列流的数量不需要一致。

2.3.4.3、Inclusive Gateway

包容性网关。外形是菱形中有一个圆。它可以看作是并行网关和排他网关的结合。可以在输出流上创建表达式来判断真假,区别在于可以执行不止一条路径,也就是存在复数个为真的路径,这些路径都会执行。同样的,当作为接收方时,需要等待所有具有process token的输入流均到达才会继续执行。这是和并行网关差异的地方。

2.3.4.4、Event-based Gateway

基于事件的网关。形状是菱形中有一个五边形。它允许根据事件做出决策,他的每一个传出序列流都需要连接到一个中间捕获事件,当流程执行到它时,他的状态类似等待,暂停执行。每一个传出的序列流,都会有一个事件订阅。

需要注意的是:基于事件的网关必须要有大于1个输出序列流,且这个输出序列流只能连接到中间捕获事件(intermediateCatchEvent类型)。同样的连接基于事件的网关的中间捕获事件只能由一个输入流。

2.3.5、边界事件

边界事件是捕获附加到活动的事件(边界事件永远不会引发)。这意味着,当 Activity 正在运行时,该事件正在侦听某种类型的触发器。 捕获事件时,活动将中断,并遵循从事件中传出的序列流。

边界事件通用的属性:

id:唯一标识。

attach to ref :将边界事件附加到活动。

边界事件还有一个标签xxx event definition:这个标签用于定义标签的类型。

2.3.5.1、Timer Boundary Event

定时边界事件。当执行到边界事件附加的活动时,计时器将启动,达到指定时间之后边界事件将启动(如果主流程活动尚未结束),然后活动中断,边界事件被跟踪。

这种情况活动将被打断。

如果边界活动直接指向结束节点,那么会中断活动。如果边界事件和主流程中的活动通过一个并行网关连接,那这个活动不会被打断。

注意事项:边界事件不能有多个输出流,除非通过并行网关分发。

2.3.5.2、Error Boundary Event

错误边界事件。图标是一个同心圆中有一个类似N的图标,表示catch。它需要配合error end活动使用,ref是end的id。当触发error end的时候,错误将持续抛出,直到遇到错误边界事件,此时将会销毁抛出错误结束事件的这个活动,无论其子流程或者嵌套活动有多少,然后沿着错误边界事件的输出流继续活动。

错误边界事件在嵌套子流程或者调用活动中应用最合适。错误结束事件发生后错误持续向上抛出,最终遇到合适的错误边界事件。

2.3.5.3、Signal Boundary Event

信号边界事件。形状是一个同心圆中心有一个空心三角形。他的作用是捕获与引用的信号定义具有相同名称的信号,不论这个信号是在哪里发起的,本流程或者别的流程都可以,只要信号的名称和定义的引用相同就能捕获,属于是全局的信号捕获。

如果有两个信号边界事件同时捕获了信号,这两个边界事件都会触发,即信号捕获之后不会被消耗。

2.3.5.4、Message Boundary Event

消息边界事件。形状是一个同心圆中包含一个邮件的标志。它的作用是捕获与引用的消息定义具有相同名称的消息。

和信号边界事件不同,消息边界事件只针对单个消息。消息的针对放只是单个接收方。

2.3.5.5、Cancel Boundary Event

取消边界事件。形状是同心圆中加上一个×。当活动执行到取消结束事件时,会触发事务子进程边界上附加的边界取消事件。当取消边界事件触发时,会中断当前作用范围内所有活动的执行,并对范围内已经执行完成的活动执行其补偿活动,补偿完成之后经由取消边界事件的输出流继续执行。

需要注意的是:

事务子进程只允许一个取消边界事件。

如果是针对嵌套的子进程,那当取消边界事件触发时,只会补偿已经完成的子进程。即子进程A中包含子进程B,子进程B中包含子进程C、D。其中C执行完成,D触发取消结束事件。最后执行补偿的时候只有C会执行补偿事件。而其他的进程因为未执行完成,而不执行补偿事件。

如果将取消边界事件放置在具有多实例特征的事务子进程上,如果一个实例触发取消,则边界事件将取消所有实例。

2.3.5.6、Compensation Boundary Event

补偿边界事件。上面说补偿挺多了。补偿边界事件的形状是同心圆+视频快退按钮。补偿边界事件,必须使用有向关联引用单个补偿处理程序。

补偿边界事件的激活时间是所附加的活动成功完成时。嵌入式子进程不支持补偿边界事件。

当流程实例结束的时候,对应的补偿事件会取消。

当补偿边界事件被添加到循环中时,每次执行都会创建一个补偿。

2.3.6、中间捕获事件

Intermediate Catching Events。

2.3.6.1、Timer Intermediate Catching Event

计时器的中间捕获事件。当执行到计时器的中间捕获事件时,将等待特定时长,然后将沿着计时器中间捕获事件的输出序列流继续执行。

形状是同心圆+时钟,时间设置遵循ISO 8601规范。

2.3.6.2、Signal Intermediate Catching Event

信号中间捕获事件。形状是同心圆+空心三角形。捕获特定的信号但并不消耗这个信号,即多个事件可以同时捕获同一个信号,即使是在不同的流程实例中或者在同一流程不同位置。

2.3.6.3、Message Intermediate Catching Event

消息中间捕获事件。形状是同心圆+邮件。捕获特定的消息,并消费这个消息,如果有多个捕获事件同时监听一个消息,那只有一个事件能够捕获到这个消息。

2.3.7、Intermediate Throwing Event

中间引发事件。

2.3.7.1、Intermediate Throwing None Event

中间空引发事件。形状是一个同心圆。效果是直接通过这个节点,这个节点本身没什么功能,也没什么特殊参数可以添加,只能添加一个监听器,当节点通过的时候触发这个监听器,以实现记录节点的目的。

例如在两个活动之间添加这个事件 ,当第一个事件执行完成之后会执行这个空事件。给这个空事件绑定一个监听器就能够执行一些过程中的事情,例如记录中间值又或者记录操作步骤等信息。

2.3.7.2、Signal Intermediate Throwing Event

中间信号引发事件。形状是一个同心圆+实心三角形。

默认情况下,信号是同步传输的,也就是执行到这个节点的时候,会阻塞直到信号被传输到所有捕获进程实例。因此当存在一个信号接收出现异常时,所有实例都将失败。

可以通过设置async为true,将信号同步改为异步传输。

2.3.8、结束节点

2.3.8.1、None End Event

空结束事件。

没什么特别的,只是表示流程的结束。

形状是同心圆。

2.3.8.2、Error End Event

错误结束事件。形状是同心圆+闪电。

一方面表示流程结束,另一方面能够触发上面的错误边界事件。

参数中的errorRef能够引用别的错误类型。

2.3.8.3、Terminate End Event

终止结束事件。

形状是同心圆中再套一个圆。

当执行到这个结束事件时,如果这个节点位于子流程,那么这个子流程会结束,别的不受影响。

如果将参数terminateAll设置成true的话,不论这个流程是子流程还是嵌套子流程又或者是调用别的流程,都将结束全部。(7.1.0-M6xml文件里面没有这个配置项,同时图形化界面也没有这个参数。)

2.3.8.4、Cancel End Event

取消结束事件。

形状是同心圆+X。

这个节点只能在事务子流程中使用,当执行到取消结束事件时,将由取消边界事件捕获,然后触发补偿。

2.3.9、其他

2.3.9.1、序列流

序列流是从源元素指向目标元素的一个箭头。

在序列流上不做任何处理则是一个普通的序列流。

可以在序列流上添加条件,表达式条件类似${message=='你好啊'}

3、总结

工作流和编程差不多,开始结束节点,中间添加各个流程、子流程以及网关。

各个节点经由序列流连接。序列流上还能添加条件,以便控制流程走向。

添加各种边界事件来防止流程出错,类似代码中的异常捕获。

每一个节点还能添加监听器,通过监听器能够控制流程变量以及记录一些流转信息。


评论