流程实例
流程实例概述
流程实例(ProcessInstance)代表流程定义的执行实例。一个流程实例包括了所有的运行节点。我们可以利用这个对象来了解当前流程实例的进度等信息。例如:用户或程序按照流程定义内容发起一个流程,这就是一个流程实例。
流程定义和流程实例的图解:
启动流程实例
启动流程实例并添加Businesskey(业务标识)
流程定义部署在Activiti后,就可以在系统中通过Activiti去管理该流程的执行,执行流程表示流程的一次执行。
比如部署系统出差流程后,如果某用户要申请出差这时就需要执行这个流程,如果另外一个用户也要申请出差则也需要执行该流程,每个执行互不影响,每个执行是单独的流程实例。
启动流程实例时,指定的businesskey,就会在act_ru_execution流程实例的执行表中存储businesskey。
Businesskey:业务标识,通常为业务表的主键,业务标识和流程实例一一对应。业务标识来源于业务系统。存储业务标识就是根据业务标识来关联查询业务系统的数据。
比如:出差流程启动一个流程实例,就可以将出差单的id作为业务标识存储到Activiti中,将来查询Activiti的流程实例信息就可以获取出差单的id,从而关联查询业务系统数据库得到出差单信息。
/**
* 启动流程实例,添加businessKey到Activiti的表
*/
@Test
public void addBusinessKey() {
// 1、获取流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2、获取RuntimeService
RuntimeService runtimeService = processEngine.getRuntimeService();
// 3、启动流程的过程中,添加businesskey,act_ru_execution表中
// 第一个参数:流程定义的key
// 第二个参数:businessKey,存出差申请单的id,就是1001
ProcessInstance instance = runtimeService.startProcessInstanceByKey("myEvection", "1001");
// 4、输出
System.out.println("businessKey=" + instance.getBusinessKey());
}
Activiti的act_ru_execution中存储业务标识:
操作数据库表
启动流程实例,操作如下数据库表:
select * from act_ru_execution # 流程实例执行表,记录当前流程实例的执行情况
说明:流程实例执行,如果当前只有一个分支时,一个流程实例只有一条记录且执行表的主键id和流程实例id相同,如果当前有多个分支正在运行则该执行表中有多条记录,存在执行表的主键和流程实例id不相同的记录。不论当前有几个分支总会有一条记录的执行表的主键和流程实例id相同。
一个流程实例运行完成,此表中与流程实例相关的记录删除。
select * from act_ru_task # 任务执行表,记录当前执行的任务
说明:启动流程实例,流程当前执行到第一个任务结点,此表会插入一条记录表示当前任务的执行情况,如果任务完成则记录删除。
select * from act_ru_identitylink # 任务参与者,记录当前参与任务的用户或组
select * from act_hi_procinst # 流程实例历史表
流程实例启动,会在此表插入一条记录,流程实例运行完成记录也不会删除。
select * from act_hi_taskinst # 任务历史表,记录所有任务
开始一个任务,不仅在act_ru_task表插入记录,也会在历史任务表插入一条记录,任务历史表的主键就是任务id,任务完成此表记录不删除。
select * from act_hi_actinst #活动历史表,记录所有活动
活动包括任务,所以此表中不仅记录了任务,还记录了流程执行过程的其它活动,比如开始事件、结束事件。
查询流程实例
流程在运行过程中可以查询流程实例的状态,当前运行结点等信息。
@Test
public void queryProcessInstance() {
// 流程定义key
String processDefinitionKey = "evection";
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 获取RunTimeService
RuntimeService runtimeService = processEngine.getRuntimeService();
List<ProcessInstance> list = runtimeService
.createProcessInstanceQuery()
.processDefinitionKey(processDefinitionKey)
.list();
for (ProcessInstance processInstance : list) {
System.out.println("流程实例id:" + processInstance.getProcessInstanceId());
System.out.println("所属流程定义id:" + processInstance.getProcessDefinitionId());
System.out.println("是否执行完成:" + processInstance.isEnded());
System.out.println("是否暂停:" + processInstance.isSuspended());
System.out.println("当前活动标识:" + processInstance.getActivityId());
}
}
关联BusinessKey
在Activiti实际应用时,查询流程实例列表时可能要显示出业务系统的一些相关信息,比如:查询当前运行的出差流程列表需要将出差单名称、出差天数等信息显示出来,出差天数等信息在业务系统中存在,而并没有在Activiti数据库中存在,所以是无法通过Activiti的API查询到出差天数等信息。
实现:在查询流程实例时,通过businessKey(业务标识 )关联查询业务系统的出差单表,查询出出差天数等信息。
通过下面的代码就可以获取Activiti中所对应实例保存的业务Key。而这个业务Key一般都会保存相关联的业务操作表的主键,再通过主键ID去查询业务信息,比如通过出差单的ID,去查询更多的请假信息(出差人,出差时间,出差天数,出差目的地等)
String businessKey = processInstance.getBusinessKey();
在Activiti的act_ru_execution表,字段BUSINESS_KEY就是存放业务KEY的。
挂起、激活流程实例
某些情况可能由于流程变更需要将当前运行的流程暂停而不是直接删除,流程暂停后将不会继续执行。
全部流程实例挂起
操作流程定义为挂起状态,该流程定义下边所有的流程实例全部暂停。
流程定义为挂起状态该流程定义将不允许启动新的流程实例,同时该流程定义下所有的流程实例将全部挂起暂停执行。RepositoryService使用流程定义的id来执行挂起或激活操作。
/**
* 全部流程实例的挂起和激活,suspend 暂停
*/
@Test
public void suspendAllProcessInstance() {
// 1、获取流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2、获取Repositoryservice
RepositoryService repositoryService = processEngine.getRepositoryService();
// 3、查询流程定义,获取流程定义的查询对象
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
.processDefinitionKey("myEvection")
.singleResult();
// 4、获取当前流程定义的实例是否都是挂起状态
boolean suspended = processDefinition.isSuspended();
// 5、获取流程定义的id
String definitionId = processDefinition.getId();
// 6、如果是挂起状态,改为激活状态
// act_re_procdef、act_ru_task、act_ru_execution、表中字段suspension_state_为1表示激活,2表示挂起
if (suspended) {
// 如果是挂起,可以执行激活的操作,参数1:流程定义id 参数2:是否激活,参数3:激活时间
repositoryService.activateProcessDefinitionById(definitionId, true, null);
System.out.println("流程定义id:" + definitionId + ",已激活");
} else {
// 7、如果是激活状态,改为挂起状态,参数1:流程定义id 参数2:是否暂停 参数3:暂停的时间
repositoryService.suspendProcessDefinitionById(definitionId, true, null);
System.out.println("流程定义id:" + definitionId + ",已挂起");
}
}
单个流程实例挂起
操作流程实例对象,针对单个流程执行挂起操作,某个流程实例挂起则此流程不再继续执行,完成该流程实例的当前任务将报异常。使用RuntimeService操作流程实例。
/**
* 挂起、激活单个流程实例
*/
@Test
public void suspendSingleProcessInstance() {
// 1、获取流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2、RuntimeService
RuntimeService runtimeService = processEngine.getRuntimeService();
// 3、通过RuntimeService获取流程实例对象
ProcessInstance instance = runtimeService.createProcessInstanceQuery()
.processInstanceId("12501")
.singleResult();
// 4、得到当前流程实例的暂停状态,true-已暂停 false-激活
boolean suspended = instance.isSuspended();
// 5、获取流程实例id
String instanceId = instance.getId();
// 6、判断是否已经暂停,如果已经暂停,就执行激活操作
if (suspended) {
// 如果已经暂停,就执行激活
runtimeService.activateProcessInstanceById(instanceId);
System.out.println("流程实例id:" + instanceId + "已经激活");
} else {
// 7、如果是激活状态,就执行暂停操作
runtimeService.suspendProcessInstanceById(instanceId);
System.out.println("流程实例id:" + instanceId + "已经暂停");
}
}
个人任务:分配任务负责人
固定分配
在进行业务流程建模时指定固定的任务负责人, 如图:在properties视图中,填写Assignee项为任务负责人。
表达式分配
UEL表达式
Activiti使用UEL表达式,UEL 是JavaEE6规范的一部分,UEL(Unified Expression Language)即统一表达式语言,Activiti支持两个UEL表达式:UEL-value 和 UEL-method。
1、UEL-value
assignee这个变量是Activiti的一个流程变量。或者使用这种方式定义:${user.assignee}
,user 也是 activiti 的一个流程变量, user.assignee
表示通过调用 user 的 getter 方法获取值。
2、UEL-method
例如:${UserBean.getUserId()}
,UserBean 是 Spring 容器中的一个 bean,表示调用该 bean 的 getUserId()方法。
3、UEL-method 与 UEL-value 结合
再比如:${ldapService.findManagerForEmployee(emp)}ldapService
是 Spring 容器的一个 bean,findManagerForEmployee 是该 bean 的一个方法,emp 是 Activiti流程变量, emp 作为参数传到 ldapService.findManagerForEmployee 方法中。
4、其它
表达式支持解析基础类型、 bean、 list、 array 和 map,也可作为条件判断。如下:${order.price > 100 && order.price < 250}
编写代码配置负责人
1、定义任务分配流程变量,${assignee0}
2、设置流程变量,在启动流程实例时设置流程变量,如下:
@Test
public void startAssigneeUel() {
// 获取流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 获取RunTimeService
RuntimeService runtimeService = processEngine.getRuntimeService();
// 设定assignee的值,用来替换uel表达式
Map<String, Object> assigneeMap = new HashMap<>();
assigneeMap.put("assignee0", "张三");
assigneeMap.put("assignee1", "李经理");
assigneeMap.put("assignee2", "王总经理");
assigneeMap.put("assignee3", "赵财务");
// 启动流程实例
runtimeService.startProcessInstanceByKey("myEvection1", assigneeMap);
}
执行成功后,可以在act_ru_variable表中看到刚才map中的数据。
注意:由于使用了表达式分配,必须保证在任务执行过程表达式执行成功,比如:某个任务使用了表达式${order.price > 100 && order.price < 250}
,当执行该任务时必须保证 order 在流程变量中存在,否则 Activiti 异常。
监听器分配
可以使用监听器来完成很多Activiti流程的业务。使用监听器的方式来指定负责人,那么在流程设计时就不需要指定assignee。
任务监听器是发生对应的任务相关事件时执行自定义Java逻辑或表达式。任务相当事件包括:
Event的选项包含:
Create:任务创建后触发
Assignment:任务分配后触发
Delete:任务完成后触发
All:所有事件发生都触发
定义任务监听类,且类必须实现 org.activiti.engine.delegate.TaskListener
接口。
public class MyTaskListener implements TaskListener {
/**
* 指定负责人
* @param delegateTask
*/
@Override
public void notify(DelegateTask delegateTask) {
// 判断当前的任务是创建申请并且是create事件
if("创建出差申请".equals(delegateTask.getName()) && "create".equals(delegateTask.getEventName())){
//这里指定任务负责人
delegateTask.setAssignee("张三");
}
}
}
使用监听器分配方式,按照监听事件去执行监听类的notify方法,方法如果不能正常执行也会影响任务的执行。
DelegateTask对象的内容如下:
流程变量
概述
流程变量在Activiti中是一个非常重要的角色,流程运转有时需要靠流程变量,业务系统和 Activiti结合时少不了流程变量,流程变量就是Activiti在管理工作流时根据管理需要而设置的变量。
比如:在出差申请流程流转时如果出差天数大于3天则由总经理审核,否则由人事直接审核, 出差天数就可以设置为流程变量,在流程流转时使用。
注意:虽然流程变量中可以存储业务数据可以通过Activiti的API查询流程变量从而实现查询业务数据,但是不建议这样使用,因为业务数据查询由业务系统负责,Activiti设置流程变量是为了流程执行需要而创建。
类型
如果将pojo存储到流程变量中,必须实现序列化接口Serializable,为了防止由于新增字段无法反序列化,需要生成serialVersionUID。
作用域
流程变量的作用域可以是一个流程实例(processInstance),或一个任务(task),或一个执行实例(execution)。
Global变量:流程变量的默认作用域是流程实例。当一个流程变量的作用域为流程实例时,可以称为Global变量。Global变量中变量名不允许重复,设置相同名称的变量,后设置的值会覆盖前设置的变量值。
Local变量:任务和执行实例仅仅是针对一个任务和一个执行实例范围,范围没有流程实例大,称为Local变量。Local变量由于在不同的任务或不同的执行实例中,作用域互不影响,变量名可以相同没有影响。Local 变量名也可以和Global变量名相同,没有影响。
使用方法
1、在属性上使用UEL表达式
可以在assignee处设置UEL表达式,表达式的值为任务的负责人,比如: ${assignee}
,assignee就是一个流程变量名称。Activiti获取UEL表达式的值,即流程变量assignee的值 ,将assignee的值作为任务的负责人进行任务分配。
2、在连线上使用UEL表达式
可以在连线上设置UEL表达式,决定流程走向。比如:${price<10000}
。price就是一个流程变量名称,UEL表达式结果类型为布尔类型。如果UEL表达式是true,要决定流程执行走向。
使用Global变量控制流程
需求
员工创建出差申请单,由部门经理审核,部门经理审核通过后出差3天及以下由人财务直接审批,3天以上先由总经理审核,总经理审核通过再由财务审批。
流程定义
1、出差天数大于等于3连线条件
也可以使用对象参数命名,如evection.num。
2、出差天数小于3连线条件
也可以使用对象参数命名,如:
设置Global流程变量
在部门经理审核前设置流程变量,变量值为出差单信息(包括出差天数),部门经理审核后可以根据流程变量的值决定流程走向。在设置流程变量时,可以在启动流程时设置,也可以在任务办理时设置。
(1)创建POJO对象
// 出差申请 pojo(省略getter、setter方法)
public class Evection implements Serializable {
private Long id;
//主键id
private String evectionName;
// 出差申请单名称
private Double num;
// 出差天数
private Date beginDate;
// 预计开始时间
private Date endDate;
// 预计结束时间
private String destination;
// 目的地
private String reson; // 出差事由
}
(2)启动流程时设置变量
在启动流程时设置流程变量,变量的作用域是整个流程实例。通过Map<key,value>
设置流程变量,map中可以设置多个变量,这个key就是流程变量的名字。
/**
* 启动流程实例,设置流程变量的值
*/
@Test
public void startProcess() {
// 获取流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 获取RunTimeService
RuntimeService runtimeService = processEngine.getRuntimeService();
// 流程定义key
String key = "myEvection2";
// 创建变量集合
Map<String, Object> map = new HashMap<>();
// 创建出差pojo对象
Evection evection = new Evection();
// 设置出差天数
evection.setNum(2d);
// 定义流程变量,把出差pojo对象放入map
map.put("evection", evection);
// 设置assignee的取值,用户可以在界面上设置流程的执行
map.put("assignee0", "张三");
map.put("assignee1", "李经理");
map.put("assignee2", "王总经理");
map.put("assignee3", "赵财务");
// 启动流程实例,并设置流程变量的值(把map传入)
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(key, map);
// 输出
System.out.println("流程实例名称=" + processInstance.getName());
System.out.println("流程定义id=" + processInstance.getProcessDefinitionId());
}
/**
* 完成任务,判断当前用户是否有权限
*/
@Test
public void completTask() {
// 任务id
String key = "myEvection2";
// 任务负责人
String assingee = "张三";
//获取processEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 创建TaskService
TaskService taskService = processEngine.getTaskService();
// 完成任务前,需要校验该负责人可以完成当前任务
// 校验方法:根据任务id和任务负责人查询当前任务,如果查到该用户有权限,就完成
Task task = taskService.createTaskQuery()
.processDefinitionKey(key)
.taskAssignee(assingee)
.singleResult();
if (task != null) {
taskService.complete(task.getId());
System.out.println("任务执行完成");
}
}
说明:startProcessInstanceByKey(processDefinitionKey, variables)
流程变量作用域是一个流程实例,流程变量使用Map存储,同一个流程实例设置变量map中key相同,后者覆盖前者。
任务办理时设置变量
在完成任务时设置流程变量,该流程变量只有在该任务完成后其它结点才可使用该变量,它的作用域是整个流程实例,如果设置的流程变量的key在流程实例中已存在相同的名字则后设置的变量替换前边设置的变量。
这里需要在创建出差单任务完成时设置流程变量。
/**
* 完成任务,判断当前用户是否有权限
*/
@Test
public void completTask() {
// 任务id
String key = "myEvection2";
// 任务负责人
String assingee = "张三";
// 获取processEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 创建TaskService
TaskService taskService = processEngine.getTaskService();
// 创建变量集合
Map<String, Object> map = new HashMap<>();
Evection evection = new Evection();
// 设置出差天数
evection.setNum(2d);
// 定义流程变量
map.put("evection",evection);
// 完成任务前,需要校验该负责人可以完成当前任务
// 校验方法:根据任务id和任务负责人查询当前任务,如果查到该用户有权限,就完成
Task task = taskService.createTaskQuery()
.processDefinitionKey(key)
.taskAssignee(assingee)
.singleResult();
if(task != null){
//完成任务是,设置流程变量的值
taskService.complete(task.getId(),map);
System.out.println("任务执行完成");
}
}
说明:通过当前任务设置流程变量,需要指定当前任务id,如果当前执行的任务id不存在则抛出异常。任务办理时也是通过Map<key,value>
设置流程变量,一次可以设置多个变量。
通过当前流程实例设置
通过流程实例id设置全局变量,该流程实例必须未执行完成。
@Test
public void setGlobalVariableByExecutionId() {
// 当前流程实例执行 id,通常设置为当前执行的流程实例
String executionId = "2601";
// 获取processEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 获取RuntimeService
RuntimeService runtimeService = processEngine.getRuntimeService();
Evection evection = new Evection();
evection.setNum(3d);
// 通过流程实例 id设置流程变量
runtimeService.setVariable(executionId, "evection", evection);
// 一次设置多个值
// runtimeService.setVariables(executionId, variables)
}
注意:executionId必须当前未结束 流程实例的执行id,通常此id设置流程实例 的id。也可以通过runtimeService.getVariable()
获取流程变量。
通过当前任务设置
@Test
public void setGlobalVariableByTaskId() {
// 当前待办任务id
String taskId = "1404";
// 获取processEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
TaskService taskService = processEngine.getTaskService();
Evection evection = new Evection();
evection.setNum(3);
// 通过任务设置流程变量
taskService.setVariable(taskId, "evection", evection);
// 一次设置多个值
// taskService.setVariables(taskId, variables)
}
注意:任务id必须是当前待办任务id,act_ru_task
中存在。如果该任务已结束,会报错,也可以通过taskService.getVariable()
获取流程变量。
设置local流程变量
任务办理时设置local流程变量,当前运行的流程实例只能在该任务结束前使用,任务结束该变量无法在当前流程实例使用,可以通过查询历史任务查询。
// 1、任务办理时设置local流程变量
// 设置local变量,作用域为该任务
taskService.setVariablesLocal(taskId, variables);
// 2、通过任务设置流程变量
taskService.setVariableLocal(taskId, "evection", evection);
// 一次设置多个值
// taskService.setVariablesLocal(taskId, variables)
组任务
在流程定义中在任务结点的assignee固定设置任务负责人,在流程定义时将参与者固定设置在.bpmn
文件中,如果临时任务负责人变更则需要修改流程定义,系统可扩展性差。
针对这种情况可以给任务设置多个候选人,可以从候选人中选择参与者来完成任务。
设置任务候选人:在流程图中任务节点的配置中设置 candidate-users(候选人),多个候选人之间用逗号分开。
组任务办理流程
1、查询组任务:指定候选人,查询该候选人当前的待办任务。候选人不能立即办理任务。
@Test
public void findGroupTaskList() {
// 流程定义key
String processDefinitionKey = "evection3";
// 任务候选人
String candidateUser = "lisi";
// 获取processEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 创建TaskService
TaskService taskService = processEngine.getTaskService();
// 查询组任务
List<Task> list = taskService.createTaskQuery()
.processDefinitionKey(processDefinitionKey)
.taskCandidateUser(candidateUser)//根据候选人查询
.list();
for (Task task : list) {
System.out.println("----------------------------");
System.out.println("流程实例id:" + task.getProcessInstanceId());
System.out.println("任务id:" + task.getId());
System.out.println("任务负责人:" + task.getAssignee());
System.out.println("任务名称:" + task.getName());
}
}
2、拾取(claim)任务:该组任务的所有候选人都能拾取。将候选人的组任务,变成个人任务。原来候选人就变成了该任务的负责人。
@Test
public void claimTask() {
// 获取processEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
TaskService taskService = processEngine.getTaskService();
// 要拾取的任务id
String taskId = "6302";
// 任务候选人id
String userId = "lisi";
// 拾取任务,即使该用户不是候选人也能拾取(建议拾取时校验是否有资格)
// 校验该用户有没有拾取任务的资格
Task task = taskService.createTaskQuery()
.taskId(taskId)
.taskCandidateUser(userId)// 根据候选人查询
.singleResult();
if (task != null) {
// 拾取任务
taskService.claim(taskId, userId);
System.out.println("任务拾取成功");
}
}
说明:即使该用户不是候选人也能拾取,建议拾取时校验是否有资格。组任务拾取后,该任务已有负责人,通过候选人将查询不到该任务。
3、查询个人任务。查询方式同个人任务部分,根据assignee查询用户负责的个人任务。
4、办理个人任务。同个人任务办理。
5、归还组任务。如果拾取后不想办理该任务?需要将已经拾取的个人任务归还到组里边,将个人任务变成了组任务。
// 归还组任务,由个人任务变为组任务,还可以进行任务交接
@Test
public void setAssigneeToGroupTask() {
// 获取processEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 查询任务使用TaskService
TaskService taskService = processEngine.getTaskService();
// 当前待办任务
String taskId = "6004";
// 任务负责人
String userId = "zhangsan2";
// 校验userId是否是taskId的负责人,如果是负责人才可以归还组任务
Task task = taskService
.createTaskQuery()
.taskId(taskId)
.taskAssignee(userId)
.singleResult();
if (task != null) {
// 如果设置为null,归还组任务,该 任务没有负责人
taskService.setAssignee(taskId, null);
}
}
6、任务交接。任务负责人将任务交给其它候选人办理该任务。
@Test
public void setAssigneeToCandidateUser() {
// 获取processEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 查询任务使用TaskService
TaskService taskService = processEngine.getTaskService();
// 当前待办任务
String taskId = "6004";
// 任务负责人
String userId = "zhangsan2";
// 将此任务交给其它候选人办理该 任务
String candidateuser = "zhangsan";
// 校验userId是否是taskId的负责人,如果是负责人才可以归还组任务
Task task = taskService
.createTaskQuery()
.taskId(taskId)
.taskAssignee(userId)
.singleResult();
if (task != null) {
taskService.setAssignee(taskId, candidateuser);
}
}
网关
网关用来控制流程的流向。
排他网关
排他网关(ExclusiveGateway),用来在流程中实现决策。 当流程执行到这个网关,所有分支都会判断条件是否为true,如果为true则执行该分支。
注意:排他网关只会选择一个为true的分支执行。如果有两个分支条件都为true,排他网关会选择id值较小的一条分支去执行。
为什么要用排他网关?不用排他网关也可以实现分支,如:在连线的condition条件上设置分支条件。在连线设置condition条件的缺点:如果条件都不满足,流程就结束了(是异常结束)。
如果使用排他网关决定分支的走向,如下:如果从网关出去的线所有条件都不满足则系统抛出异常。
并行网关
并行网关(ParallelGateway)允许将流程分成多条分支,也可以把多条分支汇聚到一起,并行网关的功能是基于进入和外出顺序流的:
-
fork分支:并行后的所有外出顺序流,为每个顺序流都创建一个并发分支。
-
join汇聚:所有到达并行网关,在此等待的进入分支, 直到所有进入顺序流的分支都到达以后, 流程就会通过汇聚网关。
注意,如果同一个并行网关有多个进入和多个外出顺序流, 它就同时具有分支和汇聚功能。 这时,网关会先汇聚所有进入的顺序流,然后再切分成多个并行分支。
与其他网关的主要区别是,并行网关不会解析条件。 即使顺序流中定义了条件,也会被忽略。
并行网关在业务应用中常用于会签任务,会签任务即多个参与者共同办理的任务。
包含网关
包含网关(InclusiveGateway)可以看做是排他网关和并行网关的结合体。 和排他网关一样,你可以在外出顺序流上定义条件,包含网关会解析它们。 但是主要的区别是包含网关可以选择多于一条顺序流,这和并行网关一样。
包含网关的功能是基于进入和外出顺序流的:
-
分支:所有外出顺序流的条件都会被解析,结果为true的顺序流会以并行方式继续执行, 会为每个顺序流创建一个分支。
-
汇聚:所有并行分支到达包含网关,会进入等待状态, 直到每个包含流程token的进入顺序流的分支都到达。 这是与并行网关的最大不同。换句话说,包含网关只会等待被选中执行了的进入顺序流。 在汇聚之后,流程会穿过包含网关继续执行。
事件网关
事件网关(EventGateway)允许根据事件判断流向。网关的每个外出顺序流都要连接到一个中间捕获事件。 当流程到达一个基于事件网关,网关会进入等待状态:会暂停执行。与此同时,会为每个外出顺序流创建相对的事件订阅。
事件网关的外出顺序流和普通顺序流不同,这些顺序流不会真的"执行", 相反它们让流程引擎去决定执行到事件网关的流程需要订阅哪些事件。 要考虑以下条件:
-
事件网关必须有两条或以上外出顺序流;
-
事件网关后,只能使用intermediateCatchEvent类型(Activiti不支持基于事件网关后连接ReceiveTask)
-
连接到事件网关的中间捕获事件必须只有一个入口顺序流。
intermediateCatchEvent支持的事件类型:Message Event消息事件、Singal Event信号事件、Timer Event定时事件。
使用事件网关定义流程:
评论区