Mastering Parallel Gateways in Activiti 7 with Spring Boot: A Step‑by‑Step Guide
This tutorial explains how to use parallel gateways in Activiti 7 with Spring Boot, covering their branching and merging functions, a complete reimbursement‑approval example, BPMN XML definition, and Java code for starting, querying, and approving workflow tasks.
Environment: Springboot 2.3.12.RELEASE + Activiti 7.1.0.M6
This article introduces the use of parallel gateways.
Overview
A parallel gateway allows a process to split into multiple concurrent branches and later merge them back into a single flow.
It has two functions: branching and merging .
Branching creates a concurrent branch for each outgoing sequence flow.
Merging combines multiple incoming branches into one outgoing flow.
The main difference from an exclusive gateway is that a parallel gateway ignores any conditions on the outgoing flows.
Example
The following reimbursement approval example demonstrates the branching and merging capabilities of a parallel gateway.
Four user tasks are defined, each with a dynamic assignee.
The "Employee reimbursement" task creates two parallel branches for approval by a deputy manager and a manager. After both approvals, the gateway merges and the flow proceeds to the "General manager".
Process definition (BPMN XML)
<code><?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test">
<process id="ps" name="ps" isExecutable="true">
<startEvent id="startEvent" name="开始"/>
<parallelGateway id="parallelgateway" name="并行执行"/>
<userTask id="usertask1" name="副经理" activiti:assignee="${fjl}"/>
<userTask id="usertask2" name="经理" activiti:assignee="${jl}"/>
<parallelGateway id="parallelgateway1" name="Parallel Gateway"/>
<sequenceFlow id="flow4" sourceRef="parallelgateway" targetRef="usertask1"/>
<sequenceFlow id="flow5" sourceRef="parallelgateway" targetRef="usertask2"/>
<sequenceFlow id="flow6" sourceRef="usertask1" targetRef="parallelgateway1"/>
<sequenceFlow id="flow7" sourceRef="usertask2" targetRef="parallelgateway1"/>
<userTask id="usertask3" name="总经理" activiti:assignee="${zjl}"/>
<sequenceFlow id="flow8" sourceRef="parallelgateway1" targetRef="usertask3"/>
<endEvent id="endevent1" name="End"/>
<sequenceFlow id="flow9" sourceRef="usertask3" targetRef="endevent1"/>
<userTask id="usertask4" name="员工报销单" activiti:assignee="${userId}"/>
<sequenceFlow id="flow10" sourceRef="usertask4" targetRef="parallelgateway"/>
<sequenceFlow id="flow11" sourceRef="startEvent" targetRef="usertask4"/>
</process>
<!-- ... -->
</definitions></code>Start Process
<code>@GetMapping("/start")
public R startProcess(String processDefinitionId, String userId) {
Map<String, Object> variables = new HashMap<>();
variables.put("userId", userId);
// start process instance
ProcessInstance instance = ps.startProcessInstanceAssignVariables(processDefinitionId, variables);
// set next assignees
variables.put("fjl", "20000");
variables.put("jl", "20001");
ps.executionTask(variables, instance.getId());
return R.success();
}</code>Query Tasks
<code>@GetMapping("/tasks")
public R myTasks(String userId) {
List<Task> list = ps.queryTasks(userId);
List<Map<String, Object>> result = list.stream().map(task -> {
Map<String, Object> res = new HashMap<>();
res.put("id", task.getId());
res.put("assignee", task.getAssignee());
res.put("createTime", task.getCreateTime());
res.put("bussinessKey", task.getBusinessKey());
res.put("category", task.getCategory());
res.put("dueDate", task.getDueDate());
res.put("desc", task.getDescription());
res.put("name", task.getName());
res.put("owner", task.getOwner());
res.put("instanceId", task.getProcessInstanceId());
res.put("variables", task.getProcessVariables());
res.put("state", task.isSuspended());
return res;
}).collect(Collectors.toList());
return R.success(result);
}</code>Approve Task
<code>@GetMapping("/approve")
public R approve(@RequestParam Map<String, Object> variables) {
String instanceId = (String) variables.remove("instanceId");
if (StringUtils.isEmpty(instanceId)) {
return R.failure("未知任务");
}
ps.executionTask(variables, instanceId);
return R.success();
}</code>Runtime tables (act_ru_task, act_ru_execution) show the state of tasks after each approval step, illustrating how the parallel gateway merges branches only after both deputy manager and manager have approved.
Finally, the process ends when the general manager approves, demonstrating the simple branching and merging behavior of parallel gateways.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.