Flowable17错误事件---------------持续更新中
错误事件就是业务流程中的 try-catch 语句。
try 块:就是那个可能会出问题的活动(通常是一个服务任务 Service Task)。
throw new Exception():就是从服务任务的代码中抛出一个特定的业务错误。
catch (SpecificException e) 块:就是附加在服务任务边界上的错误边界事件 (Error Boundary Event),它专门捕获这个特定的业务错误,并把流程引向一个预定义的“异常处理”路径。
它的主要目的不是处理技术故障(比如数据库连接失败,那应该由重试机制处理),而是处理可预见的业务异常(比如“余额不足”、“库存告急”、“用户身份未认证”等)。
关键特性
中断性 (Interrupting):默认情况下,错误事件会立即中断它所附加的活动的正常执行。一旦错误被抛出并捕获,原有的正常流程路径就不会再继续了。
定向性与作用域 (Directed & Scoped):错误不是全局广播。它由一个活动内部抛出,并且只会被其直接的边界事件或其**父级流程(或子流程)**的边界事件捕获。
冒泡机制 (Bubbling/Propagation):如果一个错误在当前作用域(例如,一个子流程)内没有被捕获,它会像编程语言中的异常一样,**向上“冒泡”**到父流程,直到被某个父级的错误边界事件捕获,或者最终导致整个流程实例失败。
基于错误码匹配 (Error Code Matching):抛出的错误和捕获事件之间的匹配是通过一个错误码 (Error Code) 字符串来完成的。这非常关键。
错误事件的类型
错误事件主要有三种形式,其中边界事件最为常用。
1. 错误边界事件 (Error Boundary Event)
图标:一个附加在活动边框上的圆圈,中间有一个闪电符号。
作用:这是最经典的try-catch用法。它附加在一个活动上,监听该活动在执行期间抛出的特定错误。
典型场景(支付失败):
一个“处理支付”的服务任务。
任务的Java逻辑调用支付API。
如果API返回“余额不足”的业务错误码,Java代码就抛出一个 new BpmnError(“INSUFFICIENT_FUNDS”)。
附加在任务上的错误边界事件,其 errorCode 被配置为 INSUFFICIENT_FUNDS。
该事件捕获到错误,中断支付任务,然后将流程引导至“通知用户充值”的人工任务。
2. 错误结束事件 (Error End Event)
图标:一个加粗的圆圈,中间有一个实心的闪电符号。
作用:它用于在当前执行路径(尤其是在子流程中)结束时,主动向外抛出一个错误。
典型场景(子流程校验失败):
你有一个“用户背景调查”的子流程。
子流程内部经过一系列检查后,发现用户的信用评分不达标。
子流程的执行路径会走向一个“错误结束事件”,该事件定义了一个 errorCode 为 CREDIT_CHECK_FAILED。
这个错误的抛出会立即终止该子流程,并将错误“冒泡”给父流程。
父流程可以通过一个附加在该子流程任务上的“错误边界事件”来捕获这个 CREDIT_CHECK_FAILED 错误,并决定整个申请是直接拒绝还是转入人工复核。
3. 错误启动事件 (Error Start Event)
图标:一个圆圈,中间有一个闪电符号。
作用:这种事件比较特殊,它只能用于启动一个“事件子流程” (Event Sub-Process)。事件子流程是一种特殊的子流程,它没有常规的进入连线,而是由其内部的启动事件触发。
典型场景(全局异常处理器):
在一个复杂的订单处理主流程中,任何一个服务任务在任何时候都可能因为“库存系统API”的临时业务问题而抛出一个 INVENTORY_API_ERROR。
你不想在每个服务任务上都附加一个相同的错误边界事件,这样太繁琐了。
你可以在主流程内部画一个“事件子流程”,并使用一个“错误启动事件”(配置了 errorCode=“INVENTORY_API_ERROR”)作为其起点。
这样,无论主流程的哪个部分抛出了这个错误,都会被这个事件子流程捕获,然后执行统一的错误处理逻辑(如“记录日志并通知IT部门”)。
一个简单的支付失败流程
<process id="paymentProcess" name="Payment Process" isExecutable="true"><startEvent id="start"/><sequenceFlow sourceRef="start" targetRef="processPaymentTask"/><!-- "try" 块: 这个服务任务可能会失败 --><serviceTask id="processPaymentTask" name="处理支付"flowable:class="com.example.flowable.delegate.ProcessPaymentDelegate"/><sequenceFlow id="flowToSuccess" sourceRef="processPaymentTask" targetRef="paymentSuccessEnd"/><!-- "catch" 块: 附加在任务上的错误边界事件 --><boundaryEvent id="catchPaymentError" attachedToRef="processPaymentTask"><!-- 关键: 它只捕获 errorCode 为 "INSUFFICIENT_FUNDS" 的错误 --><errorEventDefinition errorRef="INSUFFICIENT_FUNDS"/></boundaryEvent><sequenceFlow id="flowToFailure" sourceRef="catchPaymentError" targetRef="notifyUserTask"/><userTask id="notifyUserTask" name="通知用户余额不足"/><sequenceFlow sourceRef="notifyUserTask" targetRef="paymentFailedEnd"/><endEvent id="paymentSuccessEnd" name="支付成功"/><endEvent id="paymentFailedEnd" name="支付失败"/></process>
具体Java代码
import org.flowable.engine.delegate.BpmnError;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.JavaDelegate;public class ProcessPaymentDelegate implements JavaDelegate {@Overridepublic void execute(DelegateExecution execution) {// 从流程变量中获取订单金额和用户IDDouble amount = (Double) execution.getVariable("amount");String userId = (String) execution.getVariable("userId");System.out.println("正在为用户 " + userId + " 处理金额为 " + amount + " 的支付...");try {// 模拟调用外部支付APIboolean paymentSuccess = callPaymentApi(userId, amount);if (!paymentSuccess) {// 业务逻辑失败!// 抛出一个 BpmnError。第一个参数是 "errorCode",必须与BPMN中的 errorRef 匹配。System.out.println("支付API返回失败,抛出 BpmnError...");throw new BpmnError("INSUFFICIENT_FUNDS", "用户账户余额不足");}System.out.println("支付成功!");// 如果没有抛出异常,流程会沿着正常路径继续} catch (Exception e) {// 这里可以处理真正的技术异常,比如网络超时// 但为了演示,我们只处理业务异常if (e instanceof BpmnError) {throw e; // 重新抛出 BpmnError 让引擎捕获}// 处理其他技术异常...}}private boolean callPaymentApi(String userId, Double amount) {// 模拟逻辑:如果金额大于1000,就认为余额不足return amount <= 1000;}
}