在业务流程中,消息事件就像是流程之间或流程与外部系统之间互相发送和接收的“信件”或“通知”。它是一种实现解耦和异步通信的关键机制。
每个消息事件都必须关联一个“消息名称”(在BPMN 2.0规范中称为 Message Reference)。这个名称是唯一的标识符,Flowable 引擎通过它来匹配“发送方”(抛出事件)和“接收方”(捕获事件)。你可以把它理解为邮件的主题或信件的地址,只有地址匹配,信件才能被正确接收。
常见的消息事件类型
消息事件主要分为两大类:捕获事件 (Catching Event) 和 抛出事件 (Throwing Event)。
捕获事件:流程执行到这里会“暂停”,等待一个特定名称的消息到来,收到后流程才会继续。它的图标通常是空心的信封。
抛出事件:流程执行到这里会“主动”发送一个特定名称的消息,然后立即继续执行。它的图标通常是实心的信封。
下面是几种最常用的消息事件:
- 消息启动事件 (Message Start Event)
图标: 单层细线圆圈内一个空心信封。
作用: 不像普通启动事件那样自动或手动启动,这种流程实例必须通过一个匹配消息名称的 API 调用来启动。
场景: 当外部系统(如订单系统)需要触发一个新的流程(如发货流程)时使用。 - 消息中间捕获事件 (Intermediate Catching Message Event)
图标: 双层细线圆圈内一个空心信封。
作用: 当流程执行到这个节点时,会进入等待状态,直到外部发送了一个匹配该事件消息名称的消息,流程才会从这个节点继续向后执行。
场景: 订单流程在“等待支付”节点暂停,直到支付系统发送“支付成功”的消息后才继续。 - 消息中间抛出事件 (Intermediate Throwing Message Event)(flowableui显示不出来!!)
图标: 双层细线圆圈内一个实心信封。
作用: 当流程执行到这个节点时,它会触发一个动作(通常是执行一段 Java 代码,如 JavaDelegate),用来向外部系统或其他流程发送消息。执行完后,流程立即往下走。
场景: 用户注册流程完成后,主动发送一个“新用户注册”的消息给营销系统。 - 消息结束事件 (Message End Event)(flowableui显示不出来!!)
图标: 单层粗线圆圈内一个实心信封。
作用: 在流程实例结束的同时,抛出一个消息。
场景: 报销流程审批通过并结束后,发送一个“报销已完成”的消息通知财务系统打款。 - 消息边界事件 (Message Boundary Event)
图标: 附着在某个任务(如用户任务)边框上的双层细线圆圈,内含空心信封。
作用: 当它所附着的任务处于活动状态时,如果接收到了一个匹配的消息,它会触发一条新的执行路径。它可以是中断的(中断当前任务)或非中断的(不中断当前任务,并行执行)。
场景: 在“审批订单”任务中,如果3天内未审批,外部系统可以发送一个“催办”消息(非中断),或者客户发送了“取消订单”消息(中断),直接结束审批任务。
如何在 Flowable-UI 中绘制消息事件
下面我们以一个简单的例子“流程A发送消息,流程B接收消息并启动”来演示如何在 Flowable-UI (Flowable Design) 中进行绘制。
步骤一:定义消息
在绘制事件之前,最好先定义好你要使用的“消息”。
在 Flowable-UI 的流程编辑器中,点击画布的空白区域。
在右侧的属性面板中,找到并展开 Messages(消息)部分。
点击 “Add message…”。
在弹出的窗口中,填写 Message ID (例如 message_order_paid) 和 Name (例如 订单已支付消息)。这个 ID 就是我们前面说的 Message Name,是匹配的关键。
步骤二:绘制接收消息的流程 (流程B)
拖拽消息启动事件: 从左侧组件面板的 “Start events” 中,拖拽 “Message start event” 到画布上。
关联消息: 选中这个消息启动事件,在右侧的属性面板中,找到 Message reference(消息引用)属性。
点击下拉框,选择我们刚刚创建的 订单已支付消息 (message_order_paid)。
然后可以继续绘制流程B的后续步骤,比如“准备发货”。
步骤三:绘制发送消息的流程 (流程A)
在一个新的或已有的流程(流程A)中,假设在“支付成功”这个 Service Task 之后,我们需要发送消息。
拖拽消息中间抛出事件: 从左侧组件面板的 “Intermediate throwing events” 中,拖拽 “Message throw event” 到画布上。
关联消息: 同样地,选中这个事件,在右侧属性面板的 Message reference 中,选择同一个 订单已支付消息 (message_order_paid)。
实现发送逻辑: 非常重要的一点,抛出事件本身不会自动发送任何东西。它只是一个触发点。你必须告诉 Flowable 执行到这里时该做什么。
在属性面板中,找到 Class 属性(在 “Execution listeners” 或直接在主属性区)。
填入一个实现了 org.flowable.engine.delegate.JavaDelegate 接口的类的完全限定名(例如 com.example.SendMessageDelegate)。在这个类里,你将编写真正的消息发送逻辑(比如调用另一个系统的API,或向消息队列发送消息)。
绘制其他事件也是类似的逻辑:
消息中间捕获事件:拖拽组件,然后关联 Message reference 即可。流程会自动在此等待。
消息边界事件:先拖拽一个任务(如 User Task),然后再从组件面板的 “Boundary events” 中拖拽 “Message boundary event” 到那个任务的边框上,它就会自动附着。之后再设置它的 Message reference 和中断/非中断属性(Cancel activity)。
核心 API 与代码示例
在你的 Java 代码中,你需要使用 Flowable 的 RuntimeService 来与消息事件进行交互。
示例1:通过消息启动一个流程实例
对应上面的流程B,当外部系统完成支付后,可以调用以下代码来启动发货流程:
import org.flowable.engine.RuntimeService;
import org.springframework.beans.factory.annotation.Autowired;
// ...
@Autowired
private RuntimeService runtimeService;
public void startShippingProcess(String orderId) {
// "message_order_paid" 就是我们在UI中定义的 Message Name
// 也可以传递流程变量
runtimeService.startProcessInstanceByMessage("message_order_paid",
Map.of("orderId", orderId));
}
示例2:触发一个消息中间捕获事件
如果一个流程实例正停在“等待消息”的节点上,你可以用下面的代码来触发它,让它继续执行。
import org.flowable.engine.RuntimeService;
import org.flowable.engine.runtime.Execution;
import org.springframework.beans.factory.annotation.Autowired;
// ...
@Autowired
private RuntimeService runtimeService;
public void triggerPaymentReceivedMessage(String processInstanceId) {
// 找到正在等待这个消息的执行(Execution)
// "message_payment_received" 是消息中间事件的 Message Name
Execution execution = runtimeService.createExecutionQuery()
.processInstanceId(processInstanceId)
.messageEventSubscriptionName("message_payment_received")
.singleResult();
if (execution != null) {
// 触发消息,传递数据
runtimeService.messageEventReceived("message_payment_received", execution.getId(),
Map.of("paymentStatus", "SUCCESS"));
}
}
示例3:实现消息抛出事件的 Java Delegate
对应上面的流程A,这是 SendMessageDelegate 的一个简单实现。
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.JavaDelegate;
public class SendMessageDelegate implements JavaDelegate {
@Override
public void execute(DelegateExecution execution) {
// 在这里实现真正的消息发送逻辑
System.out.println("流程实例 " + execution.getProcessInstanceId() + " 正在发送消息...");
// 从流程变量中获取数据
String orderId = (String) execution.getVariable("orderId");
// 1. 可以是调用 startProcessInstanceByMessage 来启动另一个流程
// execution.getEngineServices().getRuntimeService().startProcessInstanceByMessage(...)
// 2. 也可以是调用外部系统的 REST API
// restTemplate.postForObject("http://inventory-service/notify", new Notification(orderId), String.class);
// 3. 还可以是向消息队列(如 RabbitMQ, Kafka)发送消息
// rabbitTemplate.convertAndSend("order_exchange", "order.paid.routing_key", new OrderPaidEvent(orderId));
System.out.println("消息已发送!关联订单ID: " + orderId);
}
}
下面我将为你构建一个经典的企业级场景:
订单处理与仓储发货分离流程
流程A:订单管理流程 (Order Management Process)
负责接收订单、等待财务确认付款、通知仓库发货、等待发货完成、最后关闭订单。
它将抛出消息来启动发货流程,并捕获消息来确认发货完成。
它还会使用一个消息边界事件来处理用户在付款确认前取消订单的场景。
流程B:仓储发货流程 (Warehouse Shipping Process)
这是一个独立的流程,由订单流程的消息来启动。
负责打包商品、安排物流,并在流程结束时抛出一个消息,通知订单流程它已完成。
文件1:订单管理流程 (order-management-process.bpmn20.xml)
这个流程演示了:
消息中间抛出事件 (Intermediate Throwing Event): 用于通知仓库开始发货。
消息中间捕获事件 (Intermediate Catching Event): 用于等待仓库发货完成的通知。
消息边界事件 (Message Boundary Event): 用于在“确认付款”任务激活时,接收外部的“取消订单”消息。
<?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:flowable="http://flowable.org/bpmn"
targetNamespace="http://flowable.org/examples">
<!-- 定义整个流程中会用到的所有消息 -->
<message id="msg_start_shipping" name="通知开始发货消息"/>
<message id="msg_shipment_complete" name="发货完成通知消息"/>
<message id="msg_cancel_order" name="取消订单消息"/>
<process id="orderManagementProcess" name="订单管理流程" isExecutable="true">
<startEvent id="start" name="订单创建"/>
<sequenceFlow sourceRef="start" targetRef="taskConfirmPayment"/>
<!-- 用户任务:财务人员确认付款 -->
<userTask id="taskConfirmPayment" name="确认付款" flowable:assignee="financeUser"/>
<!--
消息边界事件: 附着在“确认付款”任务上。
如果该任务正在执行,此时若接收到名为 "msg_cancel_order" 的消息,
会中断该任务,并沿着此路径走到“订单已取消”的结束状态。
-->
<boundaryEvent id="boundaryCancelEvent" attachedToRef="taskConfirmPayment" cancelActivity="true">
<messageEventDefinition messageRef="msg_cancel_order"/>
</boundaryEvent>
<sequenceFlow sourceRef="boundaryCancelEvent" targetRef="endOrderCanceled"/>
<endEvent id="endOrderCanceled" name="订单已取消"/>
<sequenceFlow sourceRef="taskConfirmPayment" targetRef="throwStartShipping"/>
<!--
消息中间抛出事件: 流程执行到这里时,会发送一个名为 "msg_start_shipping" 的消息。
这个消息会去启动“仓储发货流程”。
注意: 抛出事件通常需要一个JavaDelegate来实现真正的发送逻辑,这里为了BPMN文件的纯粹性省略了class定义,
但在实际项目中,你需要为其配置一个实现类。
-->
<intermediateThrowEvent id="throwStartShipping" name="通知仓库发货">
<messageEventDefinition messageRef="msg_start_shipping"/>
</intermediateThrowEvent>
<sequenceFlow sourceRef="throwStartShipping" targetRef="catchShipmentComplete"/>
<!--
消息中间捕获事件: 流程执行到这里会暂停,进入等待状态。
直到它接收到一个名为 "msg_shipment_complete" 的消息,流程才会继续。
-->
<intermediateCatchEvent id="catchShipmentComplete" name="等待发货完成">
<messageEventDefinition messageRef="msg_shipment_complete"/>
</intermediateCatchEvent>
<sequenceFlow sourceRef="catchShipmentComplete" targetRef="endOrderComplete"/>
<endEvent id="endOrderComplete" name="订单完成"/>
</process>
</definitions>
文件2:仓储发货流程 (warehouse-shipping-process.bpmn20.xml)
这个流程演示了:
消息启动事件 (Message Start Event): 整个流程的启动依赖于接收到特定消息。
消息结束事件 (Message End Event): 在流程结束的瞬间,抛出一个消息来通知其他系统或流程。
<?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:flowable="http://flowable.org/bpmn"
targetNamespace="http://flowable.org/examples">
<!-- 定义本流程中用到的消息 -->
<message id="msg_start_shipping" name="通知开始发货消息"/>
<message id="msg_shipment_complete" name="发货完成通知消息"/>
<process id="warehouseShippingProcess" name="仓储发货流程" isExecutable="true">
<!--
消息启动事件: 这个流程不会手动或自动启动。
它必须通过API调用并指定消息名称 "msg_start_shipping" 来创建实例。
这完美地接收了来自“订单管理流程”的信号。
-->
<startEvent id="startByMessage" name="收到发货通知">
<messageEventDefinition messageRef="msg_start_shipping"/>
</startEvent>
<sequenceFlow sourceRef="startByMessage" targetRef="taskPackItems"/>
<userTask id="taskPackItems" name="打包商品" flowable:assignee="warehouseWorker"/>
<sequenceFlow sourceRef="taskPackItems" targetRef="taskArrangeCourier"/>
<!-- 模拟一个自动化的任务,比如调用物流公司API -->
<serviceTask id="taskArrangeCourier" name="安排物流" flowable:class="com.example.ArrangeCourierDelegate"/>
<sequenceFlow sourceRef="taskArrangeCourier" targetRef="endAndNotify"/>
<!--
消息结束事件: 当流程执行到这个结束节点时,它会做两件事:
1. 结束当前流程实例。
2. 同时抛出一个名为 "msg_shipment_complete" 的消息。
这个消息会被“订单管理流程”中等待的捕获事件接收。
-->
<endEvent id="endAndNotify" name="发货完成并通知">
<messageEventDefinition messageRef="msg_shipment_complete"/>
</endEvent>
</process>
</definitions>
核心知识点串联与执行流程
启动: 启动一个“订单管理流程”的实例。流程会停在**[确认付款]这个用户任务。
正常路径:
财务人员完成[确认付款]任务。
流程流转到[通知仓库发货](intermediateThrowEvent)。此时,Flowable引擎(通过你实现的JavaDelegate或者直接调用API)会使用消息msg_start_shipping来启动一个新的流程实例。
[仓储发货流程]的[收到发货通知](messageStartEvent)被触发,一个新的发货流程实例被创建。
与此同时,“订单管理流程”继续执行,并停在了[等待发货完成](intermediateCatchEvent),进入等待状态。
发货流程执行: “仓储发货流程”按步骤执行(打包商品、安排物流)。
完成通知: 当“仓储发货流程”执行到[发货完成并通知](messageEndEvent)时,它会结束并将消息msg_shipment_complete抛出。
订单流程继续: “订单管理流程”中正在等待的[等待发货完成]节点捕获到该消息,流程被唤醒,继续执行直到[订单完成]。
异常/分支路径 (取消订单):
如果在财务人员处理[确认付款]任务期间,客户要求取消订单。
你的后端系统可以调用Flowable API,发送一个msg_cancel_order消息,并关联到这个流程实例。
附着在任务上的[消息边界事件]会捕获此消息,中断“确认付款”任务,然后将流程引导至[订单已取消]**的结束状态。