Flowable进阶-网关、事件和服务
网关
并行网关
并行网关允许将流程拆分为多个分支,也可以将多个分支汇集到一起。并行网关的功能是基于流入流出的顺序流。fork分支:用于任务的开始。并行后所有外出的顺序流,为每个顺序流都创建一个并发分支。
join汇聚:用于任务的汇聚。所有道道并行网关,在此处等待的进入分支,直到所有进入顺序流的分支都达到后,流程会通过汇集网关。
注意:如果同一个并行网关有多个进入和外出顺序流,他就同时具有分支和汇聚的功能。此时网关会线汇聚所有进入的顺序流,然后再切分为多个并行分支。
区别:并行网关并不会解析条件。即使顺序流中定义了条件,也会被忽略。
举例说明:
在这个流程中,等到技术经理审批后,流程并不会继续往下走,而是需要项目经理一起审批后才能继续往下走。
包容网关
包容网关可以看作是排他网关与并行网关的结合。并行网关也会像排他网关一样,解析定义条件。但区别是包含网关可以选择多条顺序流,这与并行网关是一样的。分支:所有外出顺序流的条件都会被解析,结果为true的顺序流会以并行的方式继续执行,会为每个顺序流创建一个分支。
汇聚:所有并行分支达到包含网关,会进入等待状态,直到每个包含流程token的进入顺序流的分支都到达。即包含网关只等待被选中执行了的进入顺序流。这也是与并行网关的区别。
举例说明:
事件网关
通常网关根据连线条件来决定后继路径,但事件网关不同,它提供了根据事件做选择的方式。
事件网关的每个外出顺序流都需要连接至一个捕获中间事件。当流程执行到达事件网关时,网关类似处于等待的状态,暂停执行,并为每个外出顺序流创建相对的事件订阅。
事件网关只有分支行为,流程的走向完全是由于中间事件的选择,它允许从多个候选分支中选择事件最先触发的分支(如时间事件、消息事件),并取消其他分支。
事件网关的外出顺序流和普通顺序流不同,这些顺序流从不实际被执行。相反,它们允许流程引擎决定,当执行到达一个事件网关时,需要订阅什么事件。事件网关的使用需要注意以下几个约束条件:1、一个事件网关,必须有两条或以上外出顺序流。2、事件网关后只能连接 intermediateCatchEvent(中间捕获事件)类型的元素。在 Flowable 中,
事件网关后还不支持连接接收任务(Receive Task)。3、连接到事件网关的中间捕获事件,必须只有一个入口顺序流。
事件
定时器事件
定时器事件分为三个事件,分别是:“定时器启动事件”、“中间计时器捕获事件”、“边界计时器事件”
他们分别运用在这些地方:
用法如下:
- 定时器启动事件:
在指定时间创建流程实例。在流程只需要启动一次,或者流程需要在特定的时间间隔重复启动时,都可以使用。 - 中间计时器捕获事件:
当第一个人工处理完成后,第二个人工处理的任务需要在 2023-09-07T15:20:30 之后执行。就可以使用中间计时器捕获事件来定义和使用。 - 边界计时器事件:
注意:边界计时器事件也分为中断和非中断,默认是中断事件。设置了属性 cancelActivity=“false” 的时候为非中断事件。
中断事件:是中断当前的活动沿着事件触发。
人工处理1如果在定义的 dueDate 这个时间之前还没有处理,那么就会触发定时边界事件进入人工处理3,而人工处理1和2就不执行了。
非中断事件:是不影响当前活动,并沿着事件触发。
人工处理1如果在定义的 dueDate 这个时间之前还没有处理,那么就会触发定时边界事件进入人工处理3,但不影响原本人工处理1和2的执行流程,相当于可以走两条分支。
消息事件
消息事件同样分为三个事件,分别是:“消息启动事件”、“中间消息捕获事件”、“边界消息事件”,大致用法和定时器事件相似,定时器事件用时间来控制,消息事件用程序发送消息来控制流转。
他们分别运用在这些地方:
用法如下:
在消息事件中存在以下配置,首先在流程中配置消息定义
然后将消息定义绑定在消息事件中:
随后,在流程引擎项目中,通过发送消息的代码,就可以触发消息,启动相应流程:
@Test
void runProcess(){runtimeService.startProcessInstanceByMessage("启动流程消息");
}
注意:发送消息发送的应该是消息的名称而不是消息的ID。如果名称错误,流程将无法启动并会出现以下报错:
信号事件
信号事件和消息事件类似,同样是通过一个消息来触发执行,其中的差别是:
触发方式:信号事件具有群发特性,可被多个监听者触发;消息事件则是一对一的触发方式。
传播范围:信号事件传播范围广泛,可跨流程实例和位置;消息事件则相对局限,通常只针对特定的接收者。
应用场景:信号事件适用于需要广播通知的场景;消息事件适用于需要精确控制消息接收者的场景。
运用方式:
首先在流程中新建一个信号定义:
在流程引擎中使用的时候,发送相应的信号,即可开启信号事件
@Test
void runProcess(){runtimeService.signalEventReceived("信号1");
}
其中值得注意的是:中间信号抛出事件
信号中间抛出事件也就是在流程执行中的某个节点抛出了对应的信号,然后对应的信号中间捕获事件就会触发。
在本流程中,信号抛出事件会抛出和中间信号捕捉事件同一个配置的信号,起到了流程内部的信号通知作用。
服务任务
在使用流程引擎的过程中,某阶段任务我们需要自定义一些程序来执行任务,这个时候就涉及到了流程引擎的服务任务:
在使用服务任务的时候,首先我们需要定义一个实现了JavaDelegate接口的任务类,举例说明:
public class MyJavaDelegate implements JavaDelegate {@Overridepublic void execute(DelegateExecution execution) {//这里演示一个模拟程序System.out.println("---------开始处理任务-----------");try {Thread.sleep(3000L);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("=========任务处理结束==========");}
}
在这里我们定义了一个处理任务的类。在流程定以的时候,我们就可以将节点选为服务任务:
这样就变成了,一个服务任务节点:
选中该节点,在节点属性的“类”中,将我们刚才新建的“MyJavaDelegate”类的引用路径复制进去即可。
随后保存并将该流程导入流程引擎中,使用代码将该流程注册并启用:
@Testpublic void deployServer(){ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();RepositoryService repositoryService = processEngine.getRepositoryService();Deployment deploy = repositoryService.createDeployment().addClasspathResource("processes/serverFlowable.bpmn20.xml").name("服务调用流程").category("服务") // 分类.tenantId("dpb") // 租户id.deploy();System.out.println("deploy.getId() = " + deploy.getId());System.out.println("deploy.getName() = " + deploy.getName());System.out.println("deploy.getCategory() = " + deploy.getCategory());}@Testvoid TestServer() {// 发起服务流程Map<String, Object> map = new HashMap<>();ProcessInstance studentLeave = runtimeService.startProcessInstanceByKey("serverFlowable", map);// 查看历史List<HistoricActivityInstance> activities = historyService.createHistoricActivityInstanceQuery().processInstanceId(studentLeave.getId()).finished().orderByHistoricActivityInstanceEndTime().asc().list();for (HistoricActivityInstance activity : activities) {System.out.println(activity.getActivityName());}}
即可得到一个可调用Java程序的流程。运行结果如下图所示:
注意:在服务任务中,定义的Java类是不能使用@Autowired注入其他服务类使用的,例如,我们创建了一个“TestService”服务类,准备用@Autowired注入的方式使用它:
运行结果如下:
那么在这种情况下,想要使用TestService服务类,就需要去实现 “ApplicationContextAware” 接口的方式来调用服务类:
@Component
public class MyJavaDelegate implements JavaDelegate, ApplicationContextAware {private static ApplicationContext applicationContext;@Overridepublic void setApplicationContext(ApplicationContext applicationContext1) throws BeansException {applicationContext = applicationContext1;}@Overridepublic void execute(DelegateExecution execution) {//这里演示一个模拟程序System.out.println("---------开始处理任务-----------");try {Thread.sleep(3000L);} catch (InterruptedException e) {throw new RuntimeException(e);}TestService service = applicationContext.getBean(TestService.class);System.out.println(service.getSomeName());System.out.println("=========任务处理结束==========");}
}
这样,程序就可以正常调用了: