news 2026/5/8 14:11:46

用Activiti/Flowable跑一遍就懂了:BPMN2.0四种网关的Java代码与执行日志全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
用Activiti/Flowable跑一遍就懂了:BPMN2.0四种网关的Java代码与执行日志全解析

深入解析BPMN2.0四大网关:Activiti/Flowable实战与日志分析

在业务流程管理领域,BPMN2.0标准中的网关(Gateways)是控制流程走向的核心元素。本文将基于Spring Boot环境,通过Activiti/Flowable引擎的实际代码演示,结合执行日志和数据库表变化,深度剖析排他网关、并行网关、包容网关和基于事件网关的工作原理。不同于单纯的概念讲解,我们将从工程实践角度,用可复现的测试案例展示每种网关的分支合并行为。

1. 环境准备与基础配置

在开始网关实验前,我们需要准备一个标准的Spring Boot项目集成Activiti/Flowable。以下是Maven依赖配置示例:

<dependencies> <dependency> <groupId>org.activiti</groupId> <artifactId>activiti-spring-boot-starter</artifactId> <version>7.1.0.M6</version> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> </dependencies>

为方便观察网关行为,建议在application.properties中开启调试日志:

logging.level.org.activiti=DEBUG

创建基础测试类模板,后续所有网关测试都将继承此类:

@SpringBootTest public abstract class GatewayTestBase { @Autowired private RuntimeService runtimeService; @Autowired private TaskService taskService; @Autowired private RepositoryService repositoryService; protected ProcessInstance startProcess(String processDefinitionKey, Map<String, Object> variables) { return runtimeService.startProcessInstanceByKey(processDefinitionKey, variables); } protected void deployProcess(String bpmnFile) { repositoryService.createDeployment() .addClasspathResource(bpmnFile) .deploy(); } }

2. 排他网关(Exclusive Gateway)实战解析

排他网关是最常用的决策节点,其特点是仅选择一条符合条件的路径。我们构建一个订单审核流程来演示其特性:

<process id="exclusiveGatewayDemo"> <startEvent id="start"/> <sequenceFlow id="flow1" sourceRef="start" targetRef="exclusiveGw"/> <exclusiveGateway id="exclusiveGw" name="Approval Gateway"/> <sequenceFlow id="flow2" sourceRef="exclusiveGw" targetRef="approveTask"> <conditionExpression xsi:type="tFormalExpression"> ${order.amount < 1000} </conditionExpression> </sequenceFlow> <sequenceFlow id="flow3" sourceRef="exclusiveGw" targetRef="managerApproveTask"> <conditionExpression xsi:type="tFormalExpression"> ${order.amount >= 1000 && order.amount < 5000} </conditionExpression> </sequenceFlow> <sequenceFlow id="flow4" sourceRef="exclusiveGw" targetRef="directorApproveTask"> <conditionExpression xsi:type="tFormalExpression"> ${order.amount >= 5000} </conditionExpression> </sequenceFlow> <!-- 各审批任务定义省略 --> </process>

关键测试案例展示不同金额订单的路由逻辑:

public class ExclusiveGatewayTest extends GatewayTestBase { @Test public void testLowAmountApproval() { Map<String, Object> vars = new HashMap<>(); vars.put("order", new Order(800)); // 测试金额800元 ProcessInstance pi = startProcess("exclusiveGatewayDemo", vars); Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult(); assertEquals("Approve Task", task.getName()); // 验证进入普通审批 } @Test public void testHighAmountApproval() { Map<String, Object> vars = new HashMap<>(); vars.put("order", new Order(6000)); // 测试金额6000元 ProcessInstance pi = startProcess("exclusiveGatewayDemo", vars); Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult(); assertEquals("Director Approve Task", task.getName()); // 验证进入总监审批 } }

观察引擎日志可以发现关键行为:

DEBUG o.a.e.i.bpmn.behavior.ExclusiveGatewayActivityBehavior - Leaving exclusive gateway 'exclusiveGw' with sequence flow 'flow4'

数据库表ACT_RU_EXECUTION的变化显示,无论选择哪条路径,都只会有一条执行记录继续流动。

提示:排他网关的条件表达式应互斥,否则引擎会选择XML中第一个符合条件的路径

3. 并行网关(Parallel Gateway)并发机制揭秘

并行网关用于创建同步的并发路径,典型应用场景如订单处理中的支付与发货并行:

<process id="parallelGatewayDemo"> <startEvent id="start"/> <sequenceFlow id="flow1" sourceRef="start" targetRef="fork"/> <parallelGateway id="fork"/> <sequenceFlow id="flow2" sourceRef="fork" targetRef="paymentTask"/> <sequenceFlow id="flow3" sourceRef="fork" targetRef="shippingTask"/> <userTask id="paymentTask" name="Receive Payment"/> <userTask id="shippingTask" name="Ship Order"/> <sequenceFlow id="flow4" sourceRef="paymentTask" targetRef="join"/> <sequenceFlow id="flow5" sourceRef="shippingTask" targetRef="join"/> <parallelGateway id="join"/> <sequenceFlow id="flow6" sourceRef="join" targetRef="archiveTask"/> <userTask id="archiveTask" name="Archive Order"/> </process>

测试代码验证并行执行特性:

public class ParallelGatewayTest extends GatewayTestBase { @Test public void testForkJoinBehavior() { ProcessInstance pi = startProcess("parallelGatewayDemo", new HashMap<>()); // 验证同时创建两个任务 List<Task> tasks = taskService.createTaskQuery() .processInstanceId(pi.getId()) .orderByTaskName() .asc() .list(); assertEquals(2, tasks.size()); assertEquals("Receive Payment", tasks.get(0).getName()); assertEquals("Ship Order", tasks.get(1).getName()); // 模拟完成支付任务 taskService.complete(tasks.get(0).getId()); // 此时归档任务仍未创建 assertNull(taskService.createTaskQuery() .processInstanceId(pi.getId()) .taskName("Archive Order") .singleResult()); // 完成发货任务后,归档任务出现 taskService.complete(tasks.get(1).getId()); assertNotNull(taskService.createTaskQuery() .processInstanceId(pi.getId()) .taskName("Archive Order") .singleResult()); } }

数据库观察发现,当流程到达fork网关时,ACT_RU_EXECUTION表会新增两条记录:

IDPROC_INST_ID_ACT_ID_IS_ACTIVE_
11001fork0
21001paymentTask1
31001shippingTask1

日志中的关键条目:

DEBUG o.a.e.i.bpmn.behavior.ParallelGatewayActivityBehavior - Forking execution for parallel gateway 'fork' into 2 branches

4. 包容网关(Inclusive Gateway)动态路由剖析

包容网关结合了排他与并行网关的特性,允许动态决定并行路径数量。典型应用如多条件审批流程:

<process id="inclusiveGatewayDemo"> <startEvent id="start"/> <sequenceFlow id="flow1" sourceRef="start" targetRef="inclusiveGw"/> <inclusiveGateway id="inclusiveGw" name="Check Requirements"/> <sequenceFlow id="flow2" sourceRef="inclusiveGw" targetRef="legalReview"> <conditionExpression xsi:type="tFormalExpression"> ${contract.needLegalReview} </conditionExpression> </sequenceFlow> <sequenceFlow id="flow3" sourceRef="inclusiveGw" targetRef="financeReview"> <conditionExpression xsi:type="tFormalExpression"> ${contract.value > 10000} </conditionExpression> </sequenceFlow> <sequenceFlow id="flow4" sourceRef="inclusiveGw" targetRef="techReview"> <conditionExpression xsi:type="tFormalExpression"> ${contract.hasTechnicalClause} </conditionExpression> </sequenceFlow> <!-- 各审核任务定义省略 --> <inclusiveGateway id="joinGw"/> <sequenceFlow id="flow5" sourceRef="legalReview" targetRef="joinGw"/> <sequenceFlow id="flow6" sourceRef="financeReview" targetRef="joinGw"/> <sequenceFlow id="flow7" sourceRef="techReview" targetRef="joinGw"/> <sequenceFlow id="flow8" sourceRef="joinGw" targetRef="finalizeTask"/> </process>

测试案例展示不同场景下的路由组合:

public class InclusiveGatewayTest extends GatewayTestBase { @Test public void testSinglePath() { Contract contract = new Contract(); contract.setNeedLegalReview(true); Map<String, Object> vars = new HashMap<>(); vars.put("contract", contract); ProcessInstance pi = startProcess("inclusiveGatewayDemo", vars); List<Task> tasks = taskService.createTaskQuery() .processInstanceId(pi.getId()) .list(); assertEquals(1, tasks.size()); assertEquals("Legal Review", tasks.get(0).getName()); } @Test public void testMultiplePaths() { Contract contract = new Contract(); contract.setValue(15000); contract.setHasTechnicalClause(true); Map<String, Object> vars = new HashMap<>(); vars.put("contract", contract); ProcessInstance pi = startProcess("inclusiveGatewayDemo", vars); List<Task> tasks = taskService.createTaskQuery() .processInstanceId(pi.getId()) .orderByTaskName() .asc() .list(); assertEquals(2, tasks.size()); assertEquals("Finance Review", tasks.get(0).getName()); assertEquals("Technical Review", tasks.get(1).getName()); } }

日志中可以看到条件计算过程:

DEBUG o.a.e.i.bpmn.behavior.InclusiveGatewayActivityBehavior - Evaluating condition on sequence flow 'flow3': ${contract.value > 10000} -> true

5. 基于事件的网关(Event-based Gateway)异步处理机制

基于事件的网关适用于需要等待外部事件触发的场景,如订单取消或超时处理:

<process id="eventGatewayDemo"> <startEvent id="start"/> <sequenceFlow id="flow1" sourceRef="start" targetRef="eventGw"/> <eventBasedGateway id="eventGw"/> <sequenceFlow id="flow2" sourceRef="eventGw" targetRef="cancelEvent"/> <sequenceFlow id="flow3" sourceRef="eventGw" targetRef="timeoutEvent"/> <intermediateCatchEvent id="cancelEvent"> <messageEventDefinition messageRef="orderCancelMsg"/> </intermediateCatchEvent> <intermediateCatchEvent id="timeoutEvent"> <timerEventDefinition> <timeDuration>PT2H</timeDuration> </timerEventDefinition> </intermediateCatchEvent> <sequenceFlow id="flow4" sourceRef="cancelEvent" targetRef="handleCancel"/> <sequenceFlow id="flow5" sourceRef="timeoutEvent" targetRef="handleTimeout"/> <!-- 各处理任务定义省略 --> </process>

测试案例展示事件触发机制:

public class EventGatewayTest extends GatewayTestBase { @Test public void testMessageEventTrigger() throws InterruptedException { ProcessInstance pi = startProcess("eventGatewayDemo", new HashMap<>()); // 初始状态无任务 assertNull(taskService.createTaskQuery() .processInstanceId(pi.getId()) .singleResult()); // 模拟发送取消消息 runtimeService.messageEventReceived("orderCancelMsg", pi.getId()); // 验证处理取消的任务被创建 Task task = taskService.createTaskQuery() .processInstanceId(pi.getId()) .singleResult(); assertEquals("Handle Cancellation", task.getName()); } @Test public void testTimerEventTrigger() { // 需要配置时间偏移测试 } }

引擎日志显示事件订阅创建:

DEBUG o.a.e.i.bpmn.behavior.EventBasedGatewayActivityBehavior - Created event subscription for MessageEventDefinition with message 'orderCancelMsg'

数据库表ACT_RU_EVENT_SUBSCR会记录当前等待的事件订阅。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/15 13:40:38

KCN-GenshinServer:5步快速搭建原神私服的终极GUI解决方案

KCN-GenshinServer&#xff1a;5步快速搭建原神私服的终极GUI解决方案 【免费下载链接】KCN-GenshinServer 基于GC制作的原神一键GUI多功能服务端。 项目地址: https://gitcode.com/gh_mirrors/kc/KCN-GenshinServer 你是否曾想过在自己的电脑上搭建一个原神私服&#x…

作者头像 李华
网站建设 2026/4/15 13:39:44

终极指南:如何用DeepEval构建企业级LLM质量评估系统

终极指南&#xff1a;如何用DeepEval构建企业级LLM质量评估系统 【免费下载链接】deepeval The LLM Evaluation Framework 项目地址: https://gitcode.com/GitHub_Trending/de/deepeval 大语言模型&#xff08;LLM&#xff09;应用开发中&#xff0c;最令人头疼的问题是…

作者头像 李华
网站建设 2026/4/15 13:39:43

Ubuntu 20.04下,如何像原生应用一样优雅地启动QGroundControl(AppImage版)

Ubuntu 20.04下实现QGroundControl原生应用体验的完整指南 当你每天都要启动QGroundControl进行无人机测试时&#xff0c;反复在终端输入命令或从文件管理器双击AppImage文件显然不够高效。作为Linux开发者&#xff0c;我们渴望那种点击Dock图标就能启动的专业感——就像使用VS…

作者头像 李华
网站建设 2026/5/1 11:16:43

鸿蒙音乐播放器开发实战:从零构建完整播放功能

1. 鸿蒙音乐播放器开发入门指南 最近在折腾鸿蒙应用开发&#xff0c;发现用ArkTS开发音乐播放器特别有意思。作为一个从Android转战鸿蒙的开发者&#xff0c;我花了三天时间完整实现了一个音乐播放器&#xff0c;把踩过的坑和关键实现点都记录下来&#xff0c;希望能帮到刚入门…

作者头像 李华
网站建设 2026/4/17 10:29:40

Qwen2.5-72B-GPTQ-Int4快速上手:3步完成镜像免配置与Web前端调用

Qwen2.5-72B-GPTQ-Int4快速上手&#xff1a;3步完成镜像免配置与Web前端调用 1. 模型简介 Qwen2.5-72B-Instruct-GPTQ-Int4是通义千问大模型系列的最新版本&#xff0c;作为一款72.7亿参数的大型语言模型&#xff0c;它在多个方面实现了显著提升&#xff1a; 知识量与能力增…

作者头像 李华