事件篇
事件
(event)通常用于为流程生命周期中发生的事情建模。事件总是图形化为圆圈。在BPMN 2.0中,有两种主要的事件分类:*捕获(catching)与抛出(throwing)*事件。
- 捕获: 当流程执行到达这个事件时,会等待直到触发器动作。触发器的类型由其中的图标,或者说XML中的类型声明而定义。捕获事件与抛出事件显示上的区别,是其内部的图标没有填充(即是白色的)。
- 抛出: 当流程执行到达这个事件时,会触发一个触发器。触发器的类型,由其中的图标,或者说XML中的类型声明而定义。抛出事件与捕获事件显示上的区别,是其内部的图标填充为黑色。
1. 定时器事件
定时器事件是一种在特定时间触发的事件。在Activiti中,可以通过定时器事件来实现定时执行某个任务或者触发某个流程实例,具体包括定时器启动事件,定时器捕获中间件事件,定时器边界事件,在很多的业务场景中。
1.1 定时器开始事件
定时器启动事件(timer start event)在指定时间创建流程实例。在流程只需要启动一次,或者流程需要在特定的时间间隔重复启动时,都可以使用。在使用时我们需要注意如下几个点:
- 子流程不能有定时器启动事件。
- 定时器启动事件,在流程部署的同时就开始计时。不需要调用startProcessInstanceByXXX就会在时间启动。调用startProcessInstanceByXXX时会在定时启动之外额外启动一个流程。
- 当部署带有定时器启动事件的流程的更新版本时,上一版本的定时器作业会被移除。这是因为通常并不希望旧版本的流程仍然自动启动新的流程实例。
- asyncExecutorActivate:需要设置为
true
,否则定时器不会生效,因为这块需要开启异步任务。
定时器启动事件,用其中有一个钟表图标的圆圈
来表示。我们通过具体案例来介绍
部署流程后会在我们设置的时间开启一个流程实例,在没有到达定时时间的时候在act_ru_timer_job
可以看到我们的定时任务信息
时间到达后会触发定时开启
事件。
定时器开始事件除了上面的指定固定时间启动外我们还可以通过循环和持续时间来处理
timeDate
:指定一个具体的日期和时间,例如2022-01-01T00:00:00
。timeCycle
:指定一个重复周期,例如R/PT1H
表示每隔1小时触发一次。timeDuration
:指定一个持续时间,例如PT2H30M
表示持续2小时30分钟。
然后我们增加一个重复周期的案例。这块我们可以通过自动任务来演示案例
在自动任务这块绑定了一个JavaDelegate
来处理
public class MyJavaDelegate implements JavaDelegate {@Overridepublic void execute(DelegateExecution execution) {System.out.println("自动任务执行了..." + LocalDateTime.now());}
}
然后部署流程测试,在act_ru_timer_job
查看定义信息
可以看到执行了3次。都间隔了30秒
1.2 定时器中间事件
在开始事件和结束事件之间发生的事件称为中间事件
,定时器中间捕获事件指在流程中将一个定时器作为独立的节点来运行,是一个捕获事件。当流程流转到定时器中间捕获事件时,会启动一个定时器,并一直等待触发,只有到达指定时间定时器才被触发。
当我们审批通过申请出库
后,等待一分钟触发定时器。然后会进入到出库处理
。同时在触发前在act_ru_timer_job
中可以查询到对应的任务信息。
1.3 定时器边界事件
当某个用户任务或者子流程在规定的时间后还没有执行。那么我们就可以通过定时器边界事件来触发执行特定的处理流程。
注意在定时器边界事件配置了cancelActivity属性,用于说明该事件是否为中断事件。cancelActivity属性值默认为true,表示它是边界中断事件,当该边界事件触发时,它所依附的活动实例被终止,原有的执行流会被中断,流程将沿边界事件的外出顺序流继续流转。如果将其设置为false,表示它是边界非中断事件,当边界事件触发时,则原来的执行流仍然存在,所依附的活动实例继续执行,同时也执行边界事件的外出顺序流。
部署后启动流程。那么会进入到合同审批-总经理审判
的这个节点。同时在act_ru_timer_job
中可以看到这个边界事件的定义
等待了一分钟定时器边界事件触发。我们可以在控制台中看到JavaDelegate
任务的执行。
因为这块的边界事件我们定义的是非中断
。所以用户任务还在,只是在边界事件中触发了服务任务。来通知用户审批处理。
然后总经理
审批通过。后会进入到财务审批的节点
同时会开启我们的中间边界事件。act_ru_timer_job
中会生成对应的记录。
同时act_ru_task
中的审批是财务审核
。
等待一分钟后。因为边界事件设置的是中断
类型。所以触发后财务审核
终止。只剩下触发后的新的出口中的财务实习审批
2.消息事件
消息事件(message event),是指引用具名消息的事件。消息具有名字与载荷。与信号不同,消息事件只有一个接收者
2.1 开始事件
消息开始事件
,也就是我们通过接收到某些消息后来启动流程实例,比如接收到了一封邮件,一条短信等,具体通过案例来讲解.
做消息的定义
在消息开始事件中我们需要绑定上面定义的消息
然后就可以部署流程
/*** 流程部署操作*/@Testpublic void test1(){// 1.获取ProcessEngine对象ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();// 2.完成流程的部署操作 需要通过RepositoryService来完成RepositoryService repositoryService = processEngine.getRepositoryService();// 3.完成部署操作Deployment deploy = repositoryService.createDeployment().addClasspathResource("flow/event-message-start.bpmn20.xml").name("消息启动事件").deploy(); // 是一个流程部署的行为 可以部署多个流程定义的System.out.println(deploy.getId());System.out.println(deploy.getName());}
部署完流程后。消息启动事件会在act_ru_event_subscr
中记录我们的定义信息。
然后就可以发送相关的消息。来激活该流程实例,注意
:消息的名称我们不要使用驼峰命名法
来定义
当我们发送消息后
/*** 发送消息。触发流程*/
@Test
public void test3() throws InterruptedException {ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();RuntimeService runtimeService = engine.getRuntimeService();// 发送消息 发送的消息应该是具体的消息的名称而不应该是idruntimeService.startProcessInstanceByMessage("msg01");TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
}
可以看到消息开始事件触发了。
2.2 中间事件
消息中间事件就是在流程运作中需要消息来触发的场景,案例演示,自动流程1
处理完成后,需要接收特定的消息之后才能进入到自动流程2
然后在消息中间事件的图标中我们需要绑定刚刚定义的消息
部署启动和审批流程后进入到消息中间事件的节点
然后发送消息触发消息中间事件
/*** 触发消息中间事件*/
@Test
public void test5(){ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();RuntimeService runtimeService = engine.getRuntimeService();// 查询出当前的 执行实例的 编号Execution execution = runtimeService.createExecutionQuery().processInstanceId("110001").onlyChildExecutions().singleResult();runtimeService.messageEventReceived("msg02",execution.getId());
}
然后进入到了用户任务2
的审批。说明触发了
2.3 边界事件
消息边界事件同样的针对是用户节点在消息触发前如果还没有审批。就会触发消息事件的处理逻辑。同样我们通过具体的案例来介绍。
定义两个消息
部署流程、启动流程后进入到用户任务1
后。在act_ru_event_subscr
表中就可以看到对应的消息事件,这时我们就可以发送相关的消息。
public void test5(){ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();RuntimeService runtimeService = engine.getRuntimeService();runtimeService.messageEventReceived("msg03","170005");
}
然后在控制台就可以看到JavaDelegate
被执行调用了
这里我们需要注意当前的边界事件是非中断的。所以还是需要用户任务1
来审批推进,审批后会绑定msg04
当我们触发了第二个消息边界事件。那么任务会进入到用户任务3
。同时用户任务2
被中断了。然后msg04
的任务也结束了。
3.错误事件
错误事件
可以用做一个流程的开始事件或者作为一个任务或者子流程的边界事件,错误事件没有提供作用中间事件的功能,这一点和前面介绍的定时器事件和消息事件还有区别的。在错误事件中提供了错误结束事件
。我们在案例中会详细的讲解。
3.1 开始事件
错误开始事件(error start event)可以触发一个事件子流程
,且总是在另外一个流程异常结束时触发。BPMN 2.0规定了错误开始事件只能在事件子流程中被触发,不能在其他流程中被触发,包括顶级流程、嵌套子流程和调用活动。错误启动事件不能用于启动流程实例。
错误启动事件总是中断
。我们通过案例来介绍
在对应的自动任务1
中我们需要显示的抛出异常信息
/*** 自定义的委托类*/
public class MyFirstDelegate implements JavaDelegate {/*** 回调方法* @param execution*/@Overridepublic void execute(DelegateExecution execution) {System.out.println("服务任务执行了..." + LocalDateTime.now().toString());// 抛出错误 触发 子流程中的错误开始事件throw new BpmnError("error01");}
}
那么部署完流程后。然后发起一个新的流程就会走事件子流程
中的逻辑了。错误开始事件可以在如下的场景中使用:
-
输入验证失败:当用户提交工作流启动请求时,需要对输入的数据进行验证。如果数据不符合预期的格式或规则,可以使用错误开始事件来捕获并处理验证失败的情况。
-
权限验证失败:在某些情况下,只有特定的用户或用户组才能启动某个工作流。当非授权用户尝试启动工作流时,可以使用错误开始事件来捕获并处理权限验证失败的情况。
-
前置条件不满足:在工作流启动之前,可能需要满足一些前置条件,例如某个数据已经存在或某个服务可用。如果前置条件不满足,可以使用错误开始事件来捕获并处理这种情况。
-
数据源异常:在工作流启动过程中,可能需要从外部数据源获取数据。如果数据源出现异常导致无法获取数据,可以使用错误开始事件来捕获并处理数据源异常的情况。
总的来说,错误开始事件可以用于捕获工作流启动时可能出现的各种错误情况,并根据具体的业务需求进行相应的处理。
3.2 边界事件
当某个任务发生错误时,可以通过错误边界事件来捕获并处理该错误,以保证流程的正常执行。
错误边界事件
可以在流程中的任务节点上定义,并与该任务节点关联。当任务节点执行过程中发生错误时,错误边界事件会被触发,并执行相应的处理逻辑,如发送错误通知、重新分配任务、跳转到其他节点等。
错误边界事件可以捕获多种类型的错误,如异常、超时、网络故障等。通过使用错误边界事件,可以增加流程的容错性,并提供更好的错误处理机制,保证流程的稳定性和可靠性。
需要注意
的是,错误边界事件只能与任务节点关联,而不能与其他类型的节点(如网关、开始节点、结束节点)关联。此外,在设计流程时,需要准确定义错误边界事件的触发条件和处理逻辑,以确保错误能够被正确捕获和处理。具体我们通过案例来演示。
案例中我们把错误边界事件绑定在了普通的用户任何和一个子流程上。如果对应的节点抛出的相关的错误。对应的边界事件就可以被触发。
错误边界事件可能的应用场景:
-
任务执行失败:当某个任务执行失败时,可以使用错误边界事件来捕获该异常,并执行一些恢复操作,例如重新分配任务给其他用户或记录错误信息。
-
子流程异常:当子流程执行过程中发生异常时,可以使用错误边界事件捕获该异常,并执行一些补救措施,例如回退到上一个节点或重新启动子流程。
-
超时处理:当某个任务或子流程在规定的时间内没有完成时,可以使用错误边界事件来捕获超时异常,并执行相应的超时处理逻辑,例如发送提醒邮件或自动终止流程。
-
数据校验失败:在某些场景下,需要对流程中的数据进行校验,如果校验失败,则可以使用错误边界事件来捕获校验异常,并进行相应的处理,例如返回错误信息给用户或中止流程。
总之,错误边界事件可以帮助我们在流程执行过程中及时捕获并处理异常情况,提高流程的可靠性和稳定性。
3.3 结束事件
在Activiti中,错误结束事件
(Error End Event)是一个用于标记流程实例在特定错误条件下结束的节点。当流程实例执行到错误结束事件时,流程实例将立即终止执行,并且流程实例的状态将被标记为“错误结束”。
错误结束事件可以与错误边界事件(Error Boundary Event)结合使用,用于在流程中捕获和处理特定的错误。当错误边界事件触发时,流程会跳转到与错误边界事件关联的错误结束事件,从而使流程实例结束。
错误结束事件可以配置一个错误代码,用于标识特定的错误类型。在流程定义中,可以定义多个错误结束事件,每个事件可以有不同的错误代码。当流程实例执行到错误结束事件时,可以根据错误代码进行相应的处理,例如记录日志、发送通知等。
错误结束事件可以用于处理各种错误情况,例如系统异常、业务规则异常等。通过使用错误结束事件,可以使流程能够在错误发生时进行合理的处理,提高系统的可靠性和稳定性。
总之,错误结束事件是Activiti中的一个节点,用于标记流程实例在特定错误条件下结束。它可以与错误边界事件结合使用,用于捕获和处理特定的错误。通过使用错误结束事件,可以实现对流程中各种错误情况的处理和管理。
当子流程中的支付失败的情况下会触发错误结束事件。该事件会被错误边界事件捕获。错误边界事件捕获后会重新发起支付的流程。这就是我们介绍的案例流程。
4. 信号事件
信号事件是Activiti中的一种事件类型,用于在流程执行过程中通知其他流程实例或任务实例。
信号事件是一种全局事件
,可以在任何流程实例或任务实例中触发和捕获。当一个流程实例或任务实例触发了一个信号事件,其他等待捕获相同信号的流程实例或任务实例将被唤醒并继续执行。
信号事件可以用于以下场景:
-
并行流程实例之间的协作:当一个流程实例需要与其他并行流程实例进行协作时,可以触发一个信号事件来通知其他流程实例执行相应的任务。
-
动态流程控制:当流程的执行需要根据外部条件进行动态调整时,可以使用信号事件来触发相应的流程变化。
-
异常处理:当发生异常情况时,可以触发一个信号事件来通知其他流程实例或任务实例进行异常处理。
使用信号事件需要以下几个步骤:
-
定义信号事件:在流程定义中定义一个信号事件,指定信号的名称和其他属性。
-
触发信号事件:在流程实例或任务实例中触发一个信号事件。
-
捕获信号事件:在其他流程实例或任务实例中捕获相同名称的信号事件。
-
响应信号事件:在捕获的信号事件中定义相应的处理逻辑,例如执行任务或流程变化。
信号事件我们可以分为开始事件
、中间捕获事件
、中间抛出事件
、边界事件
,具体的介绍如下
4.1 开始事件
- 启动事件是一个特殊的信号事件,用于在流程启动时触发。
- 当流程启动时,如果存在一个启动事件,并且该事件匹配到了被触发的信号,流程将会被启动。
- 启动事件可以用于实现流程启动前的条件判断,例如当某个条件满足时,才允许启动流程。
具体的案例如下:
定义信号信息:
在定义信号的时候有一个Scope
属性可以设置为Global或processInstance
- Global:全局范围的信号定义,表示可以在任何流程实例中触发和捕获信号。当一个信号事件被触发时,所有等待捕获该信号的节点都会被唤醒。
- processInstance:流程实例范围的信号定义,表示只能在当前流程实例中触发和捕获信号。当一个信号事件被触发时,只有等待在当前流程实例中捕获该信号的节点会被唤醒。
而当前的启动事件是在流程实例启动时触发的事件,用于执行一些初始化操作。启动事件可以在流程定义的开始节点上定义,并在开始节点上设置事件类型为start。启动事件只有一个全局范围的信号定义,即scope属性只能设置为Global。当一个启动事件被触发时,所有等待捕获该信号的节点都会被唤醒。
然后在信号开始节点
中绑定刚刚定义的信号:
接下就可以部署流程。然后通过信号
来启动对应的流程实例了。
/*** 通过信号启动一个新的流程*/
@Test
public void test2() throws InterruptedException {ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();// 发起流程 需要通过 runtimeService来实现RuntimeService runtimeService = engine.getRuntimeService();// 通过发送信号。触发对应订阅了该信号的流程runtimeService.signalEventReceived("signal1");TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
}
执行上面的方法就可以看到act_ru_task
中对应的就有了一条用户任务
的待办信息
同时对应的信号事件存储在了act_ru_event_subscr
中。
当然触发该事件的方式并不仅仅只有这一种方案还有:
- 由流程中的信号中间抛出事件抛出信号,所有订阅了该信号的信号开始事件所在的流程定义都会被启动;
- 作为普通开始事件,启动流程。
事件抛出我们在后面的案例中讲解。而作为普通的开始事件。直接执行下面的启动代码即可
// 通过流程定义ID来启动流程 返回的是流程实例对象ProcessInstance processInstance = runtimeService.startProcessInstanceById("event-signal-start1:1:232503");
4.2 中间事件
信号中间事件
分为捕获事件
和抛出事件
.当流程流转到信号中间捕获事件时会中断并等待触发,直到接收到相应的信号后沿信号中间捕获事件的外出顺序流继续流转。信号事件默认是全局的,与其他事件(如错误事件)不同,其信号不会在捕获之后被消费。如果存在多个引用了相同信号的事件被激活,即使它们不在同一个流程实例中,当接收到该信号时,这些事件也会被一并触发。具体我们通过案例来讲解
消息定义我们用的scope是 processInstance。也就是只在当前流程实例生效。部署运行后可以看具体的效果
启动流程后在act_ru_event_subscr
中记录了信号事件的相关信息。同时记录了作用域信息
然后我们审批用户节点进入到抛出信号事件
的节点。
审批任务完成
/*** 任务审批*/
@Test
public void test7() throws Exception{ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();TaskService taskService = engine.getTaskService();taskService.complete("245007");Thread.sleep(100000000);
}
可以看到自动任务2
执行了
同时因为scope
是processInstance
在act_ru_event_subscr
中记录的信号事件也被消费了。如果是global
则该信号事件还是继续监听。
4.3 边界事件
信号边界事件
会捕获与其信号事件定义引用的信号具有相同信号名称的信号。当流程流转到信号边界事件依附的流程活动(如用户任务、子流程等)时,工作流引擎会创建一个捕获事件,在其依附的流程活动的生命周期内等待一个抛出信号。该信号可以由信号中间抛出事件
抛出或由API触发
。信号边界事件被触发后流程会沿其外出顺序流继续流转。如果该边界事件设置为中断
,则依附的流程活动将被终止。
部署流程后启动流程那么具有的相关的数据act_ru_event_subscr
表中记录的信号事件
然后流程会进入到用户任务1
节点。当然可以正常的审批。还有就是可以发布相关的信号事件。在当前的环境下我们可以通过runtimeService
的API来触发
/*** 通过信号启动事件* 发起一个流程* 1.通过runtimeService中提供的API来发送信号* 2.通过其他流程实例中的信号中间抛出事件来触发* 3.作为普通的流程实例来启动即可*/
@Test
public void test2() throws InterruptedException {ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();// 发起流程 需要通过 runtimeService来实现RuntimeService runtimeService = engine.getRuntimeService();// 通过runtimeService的API来发布信号runtimeService.signalEventReceived("signal02");TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
}
同时因为是非中断
的,所以用户任务1
还在。接下来我们就需要做审批操作。审批通过就会进入到用户任务2
。
进入到用户任务2
后。继续审批就会触发信号抛出事件
,然后被信号边界
事件捕获。
5. 其他事件
5.1 终止结束事件
终止结束事件
也称为中断结束事件
,主要是对流程进行终止的事件,可以在一个复杂的流程中,如果某方想要提前中断这个流程,可以采用这个事件来处理,可以在并行处理任务中。如果你是在流程实例层处理,整个流程都会被中断,如果是在子流程中使用,那么当前作用和作用域内的所有的内部流程都会被终止。具体还是通过两个案例来给大家介绍:
第一个案例:终止结束事件是在主流程中触发的场景
设置终止结束事件。里面有一个terminateAll
默认为false。含义是当终止结束事件
在多实例或者嵌套的子流程中。那么不会终止整个流程。如果设置为true那么不管是否嵌套都会终止整个的流程实例。
通过案例的演示。我们发下在用户任务1
和用户任何2
没有审批的情况下当用户任务3
审批通过后同时flag
设置为false
的情况下触发了终止结束事件
那么整个流程实例都被终止了。
另一个流程案例:在子流程中触发终止结束事件
在本案例中我们可以通过terminateAll
属性非常方便的控制终止的范围。
5.2 取消结束事件
取消结束事件(cancel end event)只能与BPMN事务子流程(BPMN transaction subprocess)一起使用。当到达取消结束事件时,会抛出取消事件,且必须由取消边界事件(cancel boundary event)捕获。取消边界事件将取消事务,并触发补偿(compensation)。
具体通过案例来讲解:
注意
:结束取消事件我们只能在事务子流程中使用.
在流程设计器中没有直接提供事务子流程
的图标,我们需要通过普通的子流程来设置事务的属性即可
然后就是补偿的任务我们需要勾选可补偿的选项
部署任务后我们再继续启动流程实例的时候。出现了如下的错误
检查xml文件中发现少了该属性。那么我们需要收到的加上
然后做正常的审批。触发取消结束事件
,结合上面的流程图我们可以看到如下的效果
补充任务触发。可以看到控制台的日志信息
用户任务4在act_ru_task
中可以看到对应的记录
5.3 补偿事件
在Activiti中,补偿事件(Compensation Event)是一种用于处理流程中发生异常或错误的特殊事件。当流程中的某个任务或活动发生错误或无法继续执行时,补偿事件可以被触发来回滚或修复之前已经完成的任务或活动。
补偿事件通常与错误边界事件(Error Boundary Event)结合使用。错误边界事件是在流程中的任务或活动周围设置的捕获异常的事件。当任务或活动发生异常时,错误边界事件将被触发,进而触发相应的补偿事件。
补偿事件可以执行一系列的补偿操作,包括撤销之前已经完成的任务、还原数据、发送通知等。补偿操作的具体步骤和逻辑可以在流程定义中定义,并且可以使用Java代码或脚本来实现。
补偿事件的触发和执行是自动完成的,无需人工干预。一旦补偿事件被触发,Activiti引擎会自动查找相应的补偿事件,并按照定义的补偿操作进行执行。
通过使用补偿事件,可以有效地处理流程中的异常情况,提高流程的稳定性和容错性。补偿事件可以帮助流程在发生错误时自动进行修复,确保流程能够正常完成。
<?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"><error id="payFail" errorCode="payFail" ></error><process id="myProcess" name="My process" isExecutable="true"><startEvent id="startevent1" name="开始事件"></startEvent><parallelGateway id="parallelgateway1" name="并行网关"></parallelGateway><sequenceFlow id="flow1" sourceRef="startevent1" targetRef="parallelgateway1"></sequenceFlow><serviceTask id="servicetask1" name="预订机票" activiti:class="com.bobo.delegate.MyTwoDelegate"></serviceTask><serviceTask id="servicetask2" name="微信支付" activiti:class="com.bobo.delegate.MyOneDelegate"></serviceTask><userTask id="usertask1" name="人工出票" activiti:assignee="zhangsan"></userTask><sequenceFlow id="flow2" sourceRef="servicetask1" targetRef="usertask1"></sequenceFlow><parallelGateway id="parallelgateway2" name="Parallel Gateway"></parallelGateway><sequenceFlow id="flow3" sourceRef="usertask1" targetRef="parallelgateway2"></sequenceFlow><sequenceFlow id="flow4" sourceRef="parallelgateway1" targetRef="servicetask1"></sequenceFlow><sequenceFlow id="flow5" sourceRef="parallelgateway1" targetRef="servicetask2"></sequenceFlow><sequenceFlow id="flow6" sourceRef="servicetask2" targetRef="parallelgateway2"></sequenceFlow><serviceTask id="servicetask3" name="取消预订" isForCompensation="true" activiti:class="com.bobo.delegate.MyThreeDelegate"></serviceTask><boundaryEvent id="boundarycompensation1" name="补偿边界事件" attachedToRef="servicetask1" cancelActivity="true"><compensateEventDefinition></compensateEventDefinition></boundaryEvent><boundaryEvent id="boundaryerror1" name="错误边界事件" attachedToRef="servicetask2"><errorEventDefinition errorRef="payFail"></errorEventDefinition></boundaryEvent><intermediateThrowEvent id="compensationintermediatethrowevent1" name="补偿抛出中间事件"><compensateEventDefinition></compensateEventDefinition></intermediateThrowEvent><sequenceFlow id="flow7" sourceRef="boundaryerror1" targetRef="compensationintermediatethrowevent1"></sequenceFlow><endEvent id="endevent1" name="End"></endEvent><sequenceFlow id="flow8" sourceRef="compensationintermediatethrowevent1" targetRef="endevent1"></sequenceFlow><endEvent id="endevent2" name="End"></endEvent><sequenceFlow id="flow9" sourceRef="parallelgateway2" targetRef="endevent2"></sequenceFlow><association id="association1" sourceRef="boundarycompensation1" targetRef="servicetask3" associationDirection="None"></association></process><bpmndi:BPMNDiagram id="BPMNDiagram_myProcess"><bpmndi:BPMNPlane bpmnElement="myProcess" id="BPMNPlane_myProcess"><bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1"><omgdc:Bounds height="35.0" width="35.0" x="160.0" y="360.0"></omgdc:Bounds></bpmndi:BPMNShape><bpmndi:BPMNShape bpmnElement="parallelgateway1" id="BPMNShape_parallelgateway1"><omgdc:Bounds height="40.0" width="40.0" x="380.0" y="357.0"></omgdc:Bounds></bpmndi:BPMNShape><bpmndi:BPMNShape bpmnElement="servicetask1" id="BPMNShape_servicetask1"><omgdc:Bounds height="55.0" width="105.0" x="580.0" y="220.0"></omgdc:Bounds></bpmndi:BPMNShape><bpmndi:BPMNShape bpmnElement="boundarycompensation1" id="BPMNShape_boundarycompensation1"><omgdc:Bounds height="30.0" width="30.0" x="650.0" y="270.0"></omgdc:Bounds></bpmndi:BPMNShape><bpmndi:BPMNShape bpmnElement="servicetask2" id="BPMNShape_servicetask2"><omgdc:Bounds height="55.0" width="105.0" x="580.0" y="450.0"></omgdc:Bounds></bpmndi:BPMNShape><bpmndi:BPMNShape bpmnElement="boundaryerror1" id="BPMNShape_boundaryerror1"><omgdc:Bounds height="30.0" width="30.0" x="650.0" y="490.0"></omgdc:Bounds></bpmndi:BPMNShape><bpmndi:BPMNShape bpmnElement="usertask1" id="BPMNShape_usertask1"><omgdc:Bounds height="55.0" width="105.0" x="820.0" y="220.0"></omgdc:Bounds></bpmndi:BPMNShape><bpmndi:BPMNShape bpmnElement="parallelgateway2" id="BPMNShape_parallelgateway2"><omgdc:Bounds height="40.0" width="40.0" x="1140.0" y="336.0"></omgdc:Bounds></bpmndi:BPMNShape><bpmndi:BPMNShape bpmnElement="servicetask3" id="BPMNShape_servicetask3"><omgdc:Bounds height="55.0" width="105.0" x="830.0" y="336.0"></omgdc:Bounds></bpmndi:BPMNShape><bpmndi:BPMNShape bpmnElement="compensationintermediatethrowevent1" id="BPMNShape_compensationintermediatethrowevent1"><omgdc:Bounds height="35.0" width="35.0" x="740.0" y="590.0"></omgdc:Bounds></bpmndi:BPMNShape><bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1"><omgdc:Bounds height="35.0" width="35.0" x="820.0" y="590.0"></omgdc:Bounds></bpmndi:BPMNShape><bpmndi:BPMNShape bpmnElement="endevent2" id="BPMNShape_endevent2"><omgdc:Bounds height="35.0" width="35.0" x="1225.0" y="339.0"></omgdc:Bounds></bpmndi:BPMNShape><bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1"><omgdi:waypoint x="195.0" y="377.0"></omgdi:waypoint><omgdi:waypoint x="380.0" y="377.0"></omgdi:waypoint></bpmndi:BPMNEdge><bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2"><omgdi:waypoint x="685.0" y="247.0"></omgdi:waypoint><omgdi:waypoint x="820.0" y="247.0"></omgdi:waypoint></bpmndi:BPMNEdge><bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3"><omgdi:waypoint x="925.0" y="247.0"></omgdi:waypoint><omgdi:waypoint x="1160.0" y="247.0"></omgdi:waypoint><omgdi:waypoint x="1160.0" y="336.0"></omgdi:waypoint></bpmndi:BPMNEdge><bpmndi:BPMNEdge bpmnElement="flow4" id="BPMNEdge_flow4"><omgdi:waypoint x="400.0" y="357.0"></omgdi:waypoint><omgdi:waypoint x="400.0" y="247.0"></omgdi:waypoint><omgdi:waypoint x="580.0" y="247.0"></omgdi:waypoint></bpmndi:BPMNEdge><bpmndi:BPMNEdge bpmnElement="flow5" id="BPMNEdge_flow5"><omgdi:waypoint x="400.0" y="397.0"></omgdi:waypoint><omgdi:waypoint x="400.0" y="477.0"></omgdi:waypoint><omgdi:waypoint x="580.0" y="477.0"></omgdi:waypoint></bpmndi:BPMNEdge><bpmndi:BPMNEdge bpmnElement="flow6" id="BPMNEdge_flow6"><omgdi:waypoint x="685.0" y="477.0"></omgdi:waypoint><omgdi:waypoint x="1160.0" y="477.0"></omgdi:waypoint><omgdi:waypoint x="1160.0" y="376.0"></omgdi:waypoint></bpmndi:BPMNEdge><bpmndi:BPMNEdge bpmnElement="flow7" id="BPMNEdge_flow7"><omgdi:waypoint x="665.0" y="520.0"></omgdi:waypoint><omgdi:waypoint x="664.0" y="607.0"></omgdi:waypoint><omgdi:waypoint x="740.0" y="607.0"></omgdi:waypoint></bpmndi:BPMNEdge><bpmndi:BPMNEdge bpmnElement="flow8" id="BPMNEdge_flow8"><omgdi:waypoint x="775.0" y="607.0"></omgdi:waypoint><omgdi:waypoint x="820.0" y="607.0"></omgdi:waypoint></bpmndi:BPMNEdge><bpmndi:BPMNEdge bpmnElement="flow9" id="BPMNEdge_flow9"><omgdi:waypoint x="1180.0" y="356.0"></omgdi:waypoint><omgdi:waypoint x="1225.0" y="356.0"></omgdi:waypoint></bpmndi:BPMNEdge><bpmndi:BPMNEdge bpmnElement="association1" id="BPMNEdge_association1"><omgdi:waypoint x="665.0" y="300.0"></omgdi:waypoint><omgdi:waypoint x="664.0" y="363.0"></omgdi:waypoint><omgdi:waypoint x="830.0" y="363.0"></omgdi:waypoint></bpmndi:BPMNEdge></bpmndi:BPMNPlane></bpmndi:BPMNDiagram>
</definitions>
然后部署流程和启动流程实例。通过控制台的输出可以看到微信支付失败后触发了补偿中间事件。然后补偿边界事件触发。触发了补偿自动任务