Flowable 监听配置与示例
Flowable 提供了一套事件驱动机制:引擎在运行流程、任务、作业、变量等对象时,会向事件分发器(FlowableEventDispatcher)派发各类事件,注册了对应事件类型的监听器即可收到通知并执行自定义逻辑。这是在不修改引擎源码的前提下扩展引擎行为的核心手段之一。
监听器按作用对象可以分 成三类:
| 类型 | 接口 | 监听对象 | 典型用途 |
|---|---|---|---|
| 全局引擎事件监听器 | FlowableEventListener | 引擎派发的所有 FlowableEvent | 流程实例启动/完成、任务创建/完成、变量变化、作业失败等 |
| 执行监听器 | ExecutionListener | 流程执行节点 | 节点进入(start)、离开(end)、连线跳转(take) |
| 任务监听器 | TaskListener | 用户任务的创建/指派/完成/删除 | 任务动态处理、自动指派、消息通知 |
三者触发点不同:全局事件监听器面向整个引擎,执行监听器和任务监听器则绑定在 BPMN 模型元素上。实际项目中三者可以组合使用,也可以按需只选其一。
事件分发机制
引擎内部通过 FlowableEventDispatcher 完成事件的创建与派发。RuntimeService 等引擎服务对外暴露了事件分发的入口,监听器的注册与移除也经由它完成:
runtimeService.addEventListener(FlowableEventListener):注册全局监听器,监听其getTypes()返回的全部事件类型;runtimeService.addEventListener(FlowableEventListener, FlowableEventType...):注册监听器并指定关注的事件类型;runtimeService.removeEventListener(FlowableEventListener):移除监听器;runtimeService.dispatchEvent(FlowableEvent):主动派发一个事件(自定义事件触发的基础,见自定义监听事件与触发)。
当监听器通过 getTypes() 声明自己关注的事件类型后,分发器只把匹配的事件交给它,因此同一个监听器可以只关心少数几种事件。
全局引擎事件监听器
核心接口 FlowableEventListener
org.flowable.common.engine.api.delegate.event.FlowableEventListener 是所有全局事件监听器必须实现的接口:
| 方法 | 说明 |
|---|---|
void onEvent(FlowableEvent event) | 事件触发时被调用,接收的事件类型由 getTypes() 决定 |
Collection<? extends FlowableEventType> getTypes() | 返回该监听器关注的事件类型集合;不重写时默认关注全部事件 |
boolean isFailOnException() | 监听逻辑抛出异常时是否让当前操作失败;返回 true 则异常会向上传播并回滚 |
boolean isFireOnTransactionLifecycleEvent() | 是否在事务生命周期事件(提交/回滚前后)触发,而不是事件发生时立即触发 |
String getOnTransaction() | 非空时指定事件在事务生命周期中的触发点(如 before-commit、after-commit、after-rollback) |
其中 isFailOnException() 是监听器"出错了要不要拖垮主流程"的关键开关。业务强相关的监听(如消息通知、状态落库)通常希望异常时回滚,应返回 true;非关键的旁路逻辑(如日志、统计)则应返回 false,避免监听器异常导致流程失败。
注册方式一:引擎配置
在 Spring Boot 集成中,通过 EngineConfigurationConfigurer<SpringProcessEngineConfiguration> 在引擎创建前把监听器注册到配置对象上:
@Configuration
class FlowableListenerConfig {
@Bean
EngineConfigurationConfigurer<SpringProcessEngineConfiguration> processEngineConfigurer(
List<FlowableEventListener> listeners) {
return configuration -> configuration.setEventListeners(listeners);
}
}
这种方式在引擎初始化时就生效,适合固定不变的监听器集合。
注册方式二:运行时注册
通过 RuntimeService.addEventListener(...) 动态注册,可以在应用启动完成后、按条件有选择地注册:
runtimeService.addEventListener(listener);
runtimeService.addEventListener(listener, FlowableEngineEventType.PROCESS_STARTED);
与配置方式相比,运行时注册更灵活,可以在监听器需要依赖其它 Bean 或需要感知运行期状态时使用。
darkblue 项目示例:启动时批量注册
darkblue-cloud-flow-service 采用"启动时批量注册 + 抽象监听器基类"的方式组织全局事件监听器,整体结构如下:
@Slf4j
@Configuration
public class FlowableListenerConfiguration implements ApplicationListener<ApplicationStartedEvent> {
private final RuntimeService runtimeService;
private final List<AbstractFlowableEngineEventListener<? extends FlowableEvent>> flowableEngineEventListeners;
public FlowableListenerConfiguration(
RuntimeService runtimeService,
List<AbstractFlowableEngineEventListener<? extends FlowableEvent>> flowableEngineEventListeners) {
this.runtimeService = runtimeService;
this.flowableEngineEventListeners = flowableEngineEventListeners;
}
@Override
public void onApplicationEvent(ApplicationStartedEvent event) {
StringBuilder sb = new StringBuilder("[");
for (AbstractFlowableEngineEventListener<? extends FlowableEvent> listener : flowableEngineEventListeners) {
sb.append(listener.getClass().getName()).append(", ");
}
sb.deleteCharAt(sb.length() - 1);
sb.append("]");
log.info("Register flowable engine event listeners: {}", sb);
for (AbstractFlowableEngineEventListener<? extends FlowableEvent> listener : flowableEngineEventListeners) {
this.runtimeService.addEventListener(listener);
}
}
}
要点:
- 所有监听器实现类都是 Spring 容器中的 Bean,通过构造器注入
List<AbstractFlowableEngineEventListener<? extends FlowableEvent>>一次性收集; - 在
ApplicationStartedEvent(应用启动完成)时统一注册,保证监听器依赖的其它组件都已就绪; - 注册前打印监听器清单,方便排查哪些监听器已经生效。
抽象基类与事件分发
项目中的监听器统一继承 AbstractFlowableEngineEventListener<T extends FlowableEvent>,基类负责"类型判断 + 强转 + 事务包裹":
public abstract class AbstractFlowableEngineEventListener<T extends FlowableEvent> extends AbstractFlowableEventListener {
protected ProcessEngine processEngine;
@Override
@Transactional(rollbackFor = Exception.class)
public void onEvent(FlowableEvent flowableEvent) {
if (flowableEvent instanceof FlowableEngineEvent) {
FlowableEventType engineEventType = flowableEvent.getType();
if (this.getTypes().contains(engineEventType)) {
T engineEvent = (T) flowableEvent;
this.onEngineEvent(engineEvent);
}
}
}
/**
* 工作流引擎事件处理
*
* @param engineEvent 工作流引擎事件
* @see FlowableEngineEventType
*/
protected abstract void onEngineEvent(T engineEvent);
@Override
public boolean isFailOnException() {
return true;
}
}
- 基类先判断事件是否属于
FlowableEngineEvent,再校验事件类型是否在当前监听器声明的getTypes()中,最后强转为泛型参数交给子类实现的onEngineEvent处理; @Transactional(rollbackFor = Exception.class)让监听逻辑与主流程处于同一事务,监听器内抛出的异常会触发回滚;isFailOnException()返回true,监听失败即视为流程操作失败;- 子类只需实现两件事:声明关注的
getTypes()集合、实现onEngineEvent业务逻辑。
子类示例:流程启动监听器
@Slf4j
@Component
public class ProcessInstanceStartedListener extends AbstractProcessInstanceListener<FlowableProcessStartedEvent> {
private final MessageSender messageSender;
private final ApplicationEventPublisher applicationEventPublisher;
@Override
protected void onEngineEvent(FlowableProcessStartedEvent engineEvent) {
ExecutionEntity executionEntity = (ExecutionEntity) engineEvent.getEntity();
log.info("触发流程启动事件,流程ID:{}", executionEntity.getProcessInstanceId());
ProcessInstance processInstance = processEngine.getRuntimeService().createProcessInstanceQuery()
.includeProcessVariables()
.processInstanceId(executionEntity.getProcessInstanceId()).singleResult();
if (processInstance != null) {
// 组装消息并发送到业务服务
ProcessInstanceEventMessage msg = this.createProcessInstanceEventMessage(processInstance);
msg.setProcessInstanceState(ProcessInstanceState.PROCESSING);
Topic topic = new Topic(Topics.PROCESS_INSTANCE_EVENT_TOPIC, msg.getBusinessMark());
messageSender.send(topic, processInstance.getProcessInstanceId(), msg, this.getUserContext(processInstance));
// 发布待办创建事件
ProcessInstanceCreateTodoEvent event = new ProcessInstanceCreateTodoEvent(this, processInstance);
this.applicationEventPublisher.publishEvent(event);
} else {
log.error("未查到对应流程信息:{}", executionEntity.getProcessInstanceId());
}
}
@Override
public Collection<? extends FlowableEventType> getTypes() {
return Set.of(FlowableEngineEventType.PROCESS_STARTED);
}
}
同一个模式也用于其它引擎事件:ProcessInstanceCompletedListener 监听 PROCESS_COMPLETED,ProcessInstanceCancelledListener 监听 PROCESS_CANCELLED,任务相关监听器监听 TASK_CREATED、TASK_COMPLETED 等。这样一组监听器把"流程引擎发生了什么"翻译成"业务系统需要响应什么",实现审批流与业务解耦。
执行监听器
执行监听器 ExecutionListener 绑定在 BPMN 元素上,在流程执行的特定时刻被回调:
| 事件名 | 常量 | 触发时机 |
|---|---|---|
start | EVENTNAME_START | 节点开始执行前 |
end | EVENTNAME_END | 节点执行结束后 |
take | EVENTNAME_TAKE | 流转经过一条顺序流时 |
实现 org.flowable.engine.delegate.ExecutionListener 接口,重写 void notify(DelegateExecution execution):
@Component("orderStartListener")
public class OrderStartListener implements ExecutionListener {
@Override
public void notify(DelegateExecution execution) {
String processInstanceId = execution.getProcessInstanceId();
// 记录节点开始时间、初始化业务变量等
}
}
在 BPMN XML 中通过 flowable:executionListener 扩展属性绑定,有三种写法:
<serviceTask id="auditTask" name="审批" flowable:class="com.example.AuditTaskDelegate">
<extensionElements>
<!-- 方式一:指定类名,由引擎反射实例化 -->
<flowable:executionListener event="start" class="com.example.OrderStartListener"/>
<!-- 方式二:指定 Spring Bean 名称 -->
<flowable:executionListener event="end" delegateExpression="${orderEndListener}"/>
<!-- 方式三:表达式 -->
<flowable:executionListener event="take" expression="${myVariable == 'pass'}"/>
</extensionElements>
</serviceTask>
class:指定全限定类名,引擎通过无参构造器创建实例,不经过 Spring 容器;delegateExpression:指定 Spring Bean 名称,可注入依赖,实际项目更常用;expression:指定表达式,常用于条件判断,但监听逻辑应尽量收敛到 Java 类中,避免把复杂逻辑写进模型。
任务监听器
任务监听器 TaskListener 绑定在用户任务节点上,org.flowable.task.service.delegate.TaskListener 接口只声明一个方法 void notify(DelegateTask delegateTask),事件常量定义在父接口 BaseTaskListener 中:
| 事件名 | 常量 | 触发时机 |
|---|---|---|
create | EVENTNAME_CREATE | 任务创建并完成初始化后 |
assignment | EVENTNAME_ASSIGNMENT | 任务被指派给办理人后 |
complete | EVENTNAME_COMPLETE | 任务完成前 |
delete | EVENTNAME_DELETE | 任务被删除前 |
all | EVENTNAME_ALL_EVENTS | 上述所有事件(标记值) |
@Component("candidateUserTaskListener")
public class CandidateUserTaskListener implements TaskListener {
@Override
public void notify(DelegateTask delegateTask) {
// 根据流程变量为任务设置候选人
String candidateUser = (String) delegateTask.getVariable("candidateUser");
delegateTask.addCandidateUser(candidateUser);
}
}
BPMN XML 中绑定方式与执行监听器一致:
<userTask id="approval" name="审批" flowable:candidateGroups="managers">
<extensionElements>
<flowable:taskListener event="create" delegateExpression="${candidateUserTaskListener}"/>
<flowable:taskListener event="assignment" class="com.example.TaskAssigneeListener"/>
</extensionElements>
</userTask>
任务监听器与全局事件的 TASK_CREATED / TASK_ASSIGNED 并不冲突:任务监听器是针对具体任务节点的模型级监听,全局事件监听器则是引擎级的统一入口,两者可以同时存在、各司其职。
三种监听器对比
| 维度 | 全局事件监听器 | 执行监听器 | 任务监听器 |
|---|---|---|---|
| 接口 | FlowableEventListener | ExecutionListener | TaskListener |
| 回调方法 | onEvent(FlowableEvent) | notify(DelegateExecution) | notify(DelegateTask) |
| 注册位置 | 引擎配置 / RuntimeService | BPMN 元素 | BPMN 用户任务 |
| 关注粒度 | 事件类型 | 节点生命周期 | 任务生命周期 |
| 适用范围 | 流程、任务、作业、变量等全局事件 | 任意节点 | 用户任务 |
| 典型场景 | 流程状态落库、消息通知 | 节点进入/离开统计 | 候选人指派、自动处理 |
注意事项
- 注册时机:引擎配置方式在引擎创建前生效;
ApplicationStartedEvent注册方式在应用启动完成后生效,两者不要重复注册同一个监听器。 - 异常策略:
isFailOnException()决定监听器异常是否影响主流程。业务强相关监听返回true保证事务一致,旁路监听返回false避免拖垮流程。 - 事务边界:事件在引擎事务内派发,监听逻辑默认与主流程同事务。需要"事务提交后再通知"的场景,可以结合
isFireOnTransactionLifecycleEvent/getOnTransaction或事务同步回调实现。 - 事件类型过滤:
getTypes()返回的集合越小,监听器被调用的次数越少,排查问题也越容易。不要用"关注全部事件"代替精确声明。 - 重复执行:多实例、重试等场景下同一节点可能触发多次事件,监听逻辑需要具备幂等性。