目录
Activiti流程控制
Activiti提供了流程运行时对流程进行控制的API,可以使用RuntimeService的方法对流程进行控制。RuntimeService也是Activiti提供的业务组件之一。TaskService主要用于任务管理,RuntimeService则主要用于管理流程在运行时产生的数据,以及提供对流程进行操作的API。其中流程运行时产生的数据包括流程参数、事件、流程实例及执行流等。流程的操作包括启动流程、流程前进等。
1、流程实例与执行流
以普通的请假流程为例,假设现在有这样一个流程:员工发起请假流程,如果请假天数小于5天,先由部门经理审批,然后由人力专员审批;如果请假天数大于5天,就需要人力资源总监与公司的副总经理同时审批。对于请假天数不足5天的情况,可以设计一个只有两个用户任务节点的流程:对于请假天数大于5天的情况(需要同时审批),则会产生流程分支。请假天数小于5天的流程如图9-1所示,请假天数大于5天的流程如图9-2所示。
1.1、流程实例与执行流概念
在Activiti中,启动了一个流程,会创建一个流程实例(ProcessInstance),每个流程实例至少会有一个执行流(Execution)。当流程实例没有流程分支时,一般情况下只会存在一个执行流:假设流程出现两个分支(如图9-2所示),此时Activiti将会有三个执行流,第一个为原来流程的主执行流,而其余两个为子执行流。实际上,如果不考虑Activiti的实现,从业务的角度看,流程实例的执行流分为两个执行流,此时整个流程中只两个执行流存在(图9-2中的分支),子执行流只是对Activiti设计的一个描述。
1.2、流程实例和执行流对象(ProcessInstance与Execution)
ProcessInstance是一个接口,一个ProcessInstance实例表示一个流程实例,ProcessInstance实际上是执行流(Execution)的子接口,流程实例也是一个执行流。ProcessInstance中有Execution没有的属性,例如流程定义和业务主键。当得到的是一个ProcessInstance实例时,就将其看作一个流程实例;当得到一个Execution实例时,它就是一个执行流。
流程实例与执行流的数据保存在执行表(ACT_RU_EXECUTION)中,对应的映射实体为ExecutionEntityImpl。ExecutionEntityImpl主要包括以下映射属性。
- id:主键ID,对应ACT RU EXECUTION表的D。
- revision:该数据的修订版本号,对应REV字段。
- businessKey:流程实例的业务主键,只有ProcessInstance实例才有该值。
- parentld:父执行流的D,在没有流程分支的情况下,流程实例与执行流的父ID为同一条数据,如果出现新的执行流,那么将会设置该值,对应PARENT_ID字段。
- processDefinitionId:流程定义ID,不管是流程实例数据还是执行流数据,该字段均会被设置为相应的流程定义ID,对应PROC_DEF_ID字段。
- superExecutionId::父执行流的D,如果该执行流是子流程的一部分,则该值不为null,对应SUPER_EXEC字段。
- activityld:当前执行流的动作,一般为流程节点的名称,对应ACT_ID字段。
- isActive:该执行流状态是否活跃,如果当前的执行流分为两个子执行流,则当前的执行流被标识为非活跃状态,而两个子执行流则为活跃状态,对应IS ACTIVITE字段。
- isConcurrent:执行流是否并行,对应IS CONCURRENT字段。
- isScope:是否在执行流范围内。
- isEventScope::是否在事件范围内。
- suspensionState:流程中断状态,1为活跃,2为中断。
2、启动流程
RuntimeService中提供多个启动流程的方法,这些方法命名均为startProcessInstanceByXXX,其中“XXX”可以为流程定义(ProcessDefinition)D、流程定义的key(流程描述文件中process的id属性)和流程定义的message。根据流程定义ID启动流程的方法为startProcessInstanceByld,该方法存在多个同名的重载方法,可以按照实际情况来使用这些方法。类似地,根据流程定义的key和根据流程定义的message启动流程的方法,同样存在多个重载方法。下面将介绍如何使用这些方法启动流程。
2.1、startProcessInstanceById方法
根据流程定义ID启动流程,RuntimeService提供了四个重载的方法,使用这些方法,可以设置流程的业务主键、流程的启动参数等。调用这些方法,均会返回一个流程实例
(ProcessInstance对象),这些方法描述如下。
- startProcessInstanceById(String processDefinitionId):根据流程定义ID启动流程,在进
行流程部署时,可以得到一个Deployment的实例,根据Deployment实例的ID可以查询到ProcessDefinition的实例。 - startProcessInstanceById(String processDefinitionId,Map<String,Object>variables):
一个startProcessInstanceByld方法一样,使用该方法可以为流程设置参数,第二个参数为Map。 - startProcessInstanceById(String processDefinitionId,String businessKey):使用流程定义ID和业务主键启动流程,业务主键会被保存到执行表的BUSINESS KEY字段。
- startProcessInstanceById(String processDefinitionId,String businessKey,Map<String,
Object>variables):与前一个startProcessInstanceByld方法类似,该方法既可以设置业务主键,也允许传入流程参数。
<process id="test" isClosed="false" isExecutable="true" name="start event" processType="None">
<startEvent id="start"/>
<sequenceFlow sourceRef="start" targetRef="writeVacation"/>
<userTask id="writeVacation"/>
<sequenceFlow sourceRef="writeVacation" targetRef="audit"/>
<userTask id="audit"/>
<sequenceFlow sourceRef="audit" targetRef="end"/>
<endEvent id="end"/>
</process>
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = processEngine.getRepositoryService();
RuntimeService runtimeService = processEngine.getRuntimeService();
Deployment deploy = repositoryService.createDeployment().addClasspathResource("demo9/startById.bpmn20.xml").deploy();
//查找流程定义
ProcessDefinition pd = repositoryService.createProcessDefinitionQuery().deploymentId(deploy.getId()).singleResult();
//设置流程参数
Map<String, Object> vars = new HashMap<String, Object>();
vars.put("days", 5);
//启动流程
runtimeService.startProcessInstanceById(pd.getId());
runtimeService.startProcessInstanceById(pd.getId(), vars);
runtimeService.startProcessInstanceById(pd.getId(), "varcationRequest1");
runtimeService.startProcessInstanceById(pd.getId(), "vacationRequest2", vars);
//查询流程实例,结果为4
long count = runtimeService.createProcessInstanceQuery().count();
System.out.println("流程实例数量:" + count);
在代码中,定义了一个简单的请假流程,一个编写请假申请的UserTask(write Vacation)和一个进行请假审核的User Task(audit)。调用startProcessInstanceByld方法启动流程后,Activiti会在各个数据表中写入相应的流程数据,包括流程任务(Task)、流程参数和流程实例等数据。根据前面章节的讲述可以知道,流程任务数据保存在任务表中,流程参数数据保存在参数表中,因此在调用startProcessInstanceByld方法时传入的流程参数,同样会
保存到参数表中。调用了startProcessInstanceById方法后,打开执行表,可以看到产生的流程实例数据如图所示。
由图可以看到,执行表中有4条数据,即此时有4个流程实例。除了4条主执行流数据外,还会产生4条子执行流数据,这4条数据的parentld为对应的流程实例id,即主执行流id。
2.2、startProcessInstanceByKey方法
除了可以使用流程定义的D来启动流程外,还可以根据流程描述文件中定义的process节点的id属性来启动流程,注意方法名的key不要与流程的BUSINESS KEY字段混淆,流
程实例表的BUSINESS KEY字段值由启动方法传入。使用startProcessInstanceByKey方法,只需要知道process节点的id属性,而不需要到数据库中重新查找流程定义数据。与
startProcessInstanceById方法类似,在RuntimeService中,startProcessInstanceByKey方法同样存在多个同名的重载方法。
- startProcessInstanceByKey(String processDefinitionKey):根据流程文件定义的process
节点的id启动流程。 - startProcessInstanceByKey(String processDefinitionKey,Map<String,Object variables):
与前一个启动流程方法一样,在该方法中可以传入流程参数。 - startProcessInstanceByKey(String processDefinitionKey,String businessKey):与第一个启
动流程的方法一样,该方法可以传入流程业务主键。 - startProcessInstanceByKey(String processDefinitionKey,String businessKey,Map<String,
Object>variables):与前一个方法类似,该方法除了可以传入流程业务主键外,还可以传入流程参数。
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = engine.getRepositoryService();
RuntimeService runtimeService = engine.getRuntimeService();
// 部署流程描述文件
repositoryService.createDeployment().addClasspathResource("demo9/startByKey.bpmn20.xml").deploy();
//初始化流程参数
Map<String, Object> vars = new HashMap<String, Object>();
vars.put("days", 4);
//启动流程
runtimeService.startProcessInstanceByKey("vacationRequest");
runtimeService.startProcessInstanceByKey("vacationRequest", vars);
runtimeService.startProcessInstanceByKey("vacationRequest", "testKey");
runtimeService.startProcessInstanceByKey("vacationRequest", "testKey2", vars);
// 查询流程实例,结果为4
long count = runtimeService.createProcessInstanceQuery().count();
System.out.println("流程实例数量:" + count);
调用了4个startProcessInstanceByKey方法启动流程,最后在进行流程实例查询时,结果同样为4。由于只需要根据流程d来启动,因此不需要查询流程定义。需要注意的是,与使用startProcessInstanceByld方法时一样,每个流程定义中只允许存在一个流程业务主键。
2.3、startProcessInstanceByMessage方法
在实际应用中,可能会出现以下场景:一个订单流程,当接收到相应订单信息后,自动启动该流程。解决这种需求,可以使用Activiti的消息事件,在流程定义的开始事件中,添加消息事件定义,当流程接收到某个消息时,就会启动流程。
<!-- 定义消息 -->
<message id="startMsg" name="startMsg"></message>
<process id="saleOrder" name="saleOrder">
<startEvent id="start" >
<!-- 定义消息事件 -->
<messageEventDefinition messageRef="startMsg"></messageEventDefinition >
</startEvent>
<sequenceFlow sourceRef="start" targetRef="confirmOrder" />
<userTask id="confirmOrder"></userTask>
<sequenceFlow sourceRef="confirmOrder" targetRef="sendGoods" />
<userTask id="sendGoods"></userTask>
<sequenceFlow sourceRef="sendGoods" targetRef="end" />
<endEvent id="end" />
</process>
代码定义了一个订单流程,注意其中的粗体字代码,其在流程描述文件中定义了一个message节点,id为startMsg,再为开始事件添加消息事件定义,并且该事件定义引用了
代码中定义的message。Activiti在加载流程描述文件时,如果发现开始事件中存在消息事件,则会向事件表(ACT RU_EVENT_SUBSCR)中写入事件描述数据。
定义了流程后,此时可以使用
startProcessInstanceByMessage方法来启动流程。RuntimeService中定义了几个同名的
startProcessInstanceByMessage重载方法,这些方法描述如下。
- startProcessInstanceByMessage(String messageName):根据消息名称启动流程。
- startProcessInstanceByMessage(String messageName,Map<String,Object process Variables):与前一个方法类似,使用该方法可以传入流程参数。
- startProcessInstanceByMessage(String messageName,.String businessKey):根据消息名称
启动流程,可以传入业务主键。 - startProcessInstanceByMessage(String messageName,String businessKey,Map<String,
Object>process Variables):与前一个方法类似,除可以传入业务主键外,还可以传入流程参数。
在此需要注意的是,流程名称并不是message节点的id属性,而是name属性,而消息事件定义(messageEventDefinition节点)的messageRef属性引用的是message节点的id属性。
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = engine.getRepositoryService();
RuntimeService runtimeService = engine.getRuntimeService();
// 部署流程描述文件
repositoryService.createDeployment().addClasspathResource("demo9/startByMessage.bpmn20.xml").deploy();
//初始化流程参数
Map<String, Object> vars = new HashMap<String, Object>();
vars.put("days", 4);
//启动流程
runtimeService.startProcessInstanceByMessage("startMsg");
runtimeService.startProcessInstanceByMessage("startMsg", vars);
runtimeService.startProcessInstanceByMessage("startMsg", "testKey");
runtimeService.startProcessInstanceByMessage("startMsg", "testKey2", vars);
// 查询流程实例,结果为4
long count = runtimeService.createProcessInstanceQuery().count();
System.out.println("流程实例数量:" + count);
3、流程参数
Activiti中专门使用ACT_RU_VARIABLE表来保存流程中的参数,这些参数均有自己的生命周期和作用域,RuntimeService也提供了设置流程参数的方法:setVariables和set VariablesLocal。
3.1、设置与查询流程参数
RuntimeService的setVariable和setVariableLocal方法,允许传入基本类型参数,也允许传入序列化对象,它们的基本使用方法与TaskService的设置参数的方法类似,不同的是,对于RuntimeService中的设置参数的方法,需要传入执行流的ID。同样,查询流程参数也需要提供执行流的ID与参数名称。
<process id="vacationRequest" name="vacationRequest">
<startEvent id="start" />
<sequenceFlow sourceRef="start" targetRef="writeVacation" />
<userTask id="writeVacation"></userTask>
<sequenceFlow sourceRef="writeVacation" targetRef="audit" />
<userTask id="audit"></userTask>
<sequenceFlow sourceRef="audit" targetRef="end" />
<endEvent id="end" />
</process>
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = engine.getRepositoryService();
RuntimeService runtimeService = engine.getRuntimeService();
// 部署流程描述文件
repositoryService.createDeployment().addClasspathResource("demo9/variables.bpmn20.xml").deploy();
//启动流程
ProcessInstance pi = runtimeService.startProcessInstanceByKey("vacationRequest");
//查询流程实例的执行流
Execution exe = runtimeService.createExecutionQuery().processInstanceId(pi.getId()).singleResult();
//设置流程参数
runtimeService.setVariable(exe.getId(), "days", 5);
//查找参数
System.out.println("获取流程参数:" + runtimeService.getVariable(exe.getId(), "days"));
3.2、流程参数的作用域
当使用TaskService的set VariableLocal方法时,流程参数只作用于当前的任务节点,RuntimeService中的setVariableLocal方法与之类似,使用
setVariableLocal方法时,流程参数只作用于设置的执行流,一旦该执行流结束,该参数将失效。
使用setVariable方法设置的参数,使用get VariableLocal方法不能获取,而使用set VariableLocal设置的参数,则可以使用getVariable方法获取。
如图所示,当流程启动时,将会执行流程分支,分别执行“Manager Audit”和“HR Audit”任务。此时对Activiti来说,将会产生两个子执行流,当两个任务都完成后,流程将会执行“End Task”任务。本例将会在执行“Manager Audit”和“HR Audit”任务节点时,分别为两个执行流设置不同的参数,完成任务后(流程向前进行),再查找设置的参数。
<process id="vacationRequest" name="vacationRequest">
<startEvent id="startevent1" name="Start"></startEvent>
<parallelGateway id="parallelgateway1" name="Parallel Gateway"></parallelGateway>
<userTask id="usertask1" name="Manager Audit"></userTask>
<userTask id="usertask2" name="HR Audit"></userTask>
<parallelGateway id="parallelgateway2" name="Parallel Gateway"></parallelGateway>
<sequenceFlow id="flow1" name="" sourceRef="startevent1" targetRef="parallelgateway1"></sequenceFlow>
<sequenceFlow id="flow2" name="" sourceRef="parallelgateway1" targetRef="usertask1"></sequenceFlow>
<sequenceFlow id="flow3" name="" sourceRef="parallelgateway1" targetRef="usertask2"></sequenceFlow>
<sequenceFlow id="flow4" name="" sourceRef="usertask2" targetRef="parallelgateway2"></sequenceFlow>
<sequenceFlow id="flow5" name="" sourceRef="usertask1" targetRef="parallelgateway2"></sequenceFlow>
<userTask id="usertask3" name="End Task"></userTask>
<endEvent id="endevent1" name="End"></endEvent>
<sequenceFlow id="flow6" name="" sourceRef="parallelgateway2" targetRef="usertask3"></sequenceFlow>
<sequenceFlow id="flow7" name="" sourceRef="usertask3" targetRef="endevent1"></sequenceFlow>
</process>
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = engine.getRepositoryService();
RuntimeService runtimeService = engine.getRuntimeService();
TaskService taskService = engine.getTaskService();
// 部署流程描述文件
repositoryService.createDeployment().addClasspathResource("demo9/localVariable.bpmn20.xml").deploy();
//启动流程
ProcessInstance pi = runtimeService.startProcessInstanceByKey("vacationRequest");
//查询全部的任务,得到相应的执行流,设置不同的参数
List<Task> tasks = taskService.createTaskQuery().processInstanceId(pi.getId()).list();
for (Task task : tasks) {
Execution exe = runtimeService.createExecutionQuery().executionId(task.getExecutionId()).singleResult();
if ("Manager Audit".equals(task.getName())) {
//经理审核节点,设置Local参数
runtimeService.setVariableLocal(exe.getId(), "managerVar", "manager var");
} else {
//HR审核节点,设置全局参数
runtimeService.setVariable(exe.getId(), "hrVar", "hr var");
}
}
//两个执行流时输出参数
for (Task task : tasks) {
Execution exe = runtimeService.createExecutionQuery().executionId(task.getExecutionId()).singleResult();
if ("Manager Audit".equals(task.getName())) {
System.out.println("使用getVariableLocal方法获取经理参数:" +
runtimeService.getVariableLocal(exe.getId(), "managerVar"));
System.out.println("使用getVariable方法获取经理参数:" +
runtimeService.getVariableLocal(exe.getId(), "managerVar"));
} else {
System.out.println("使用getVariableLocal方法获取HR参数:" +
runtimeService.getVariableLocal(exe.getId(), "hrVar"));
System.out.println("使用getVariable方法获取HR参数:" +
runtimeService.getVariable(exe.getId(), "hrVar"));
}
}
//完成任务
for (Task task : tasks) {
taskService.complete(task.getId());
}
System.out.println("======== 完成流程分支后 ========");
//重新查找流程任务
tasks = taskService.createTaskQuery().processInstanceId(pi.getId()).list();
for (Task task : tasks) {
System.out.println("当前流程节点:" + task.getName());
Execution exe = runtimeService.createExecutionQuery()
.executionId(task.getExecutionId()).singleResult();
System.out.println("经理参数:" + runtimeService.getVariable(exe.getId(), "managerVar"));
System.out.println("HR参数:" + runtimeService.getVariable(exe.getId(), "hrVar"));
}
使用getVariableLocal方法获取经理参数:manager var
使用getVariable方法获取经理参数:manager var
使用getVariableLocal方法获取HR参数:null
使用getVariable方法获取HR参数:hr var
======== 完成流程分支后 ========
当前流程节点:End Task
经理参数:null
HR参数:hr var
根据输出的结果可以看出,对于使用setVariableLocal方法设置的参数,只要该执行流存在,那么不管使用getVariable方法还是使用get VariableLocal方法均可以获得参数。而对于使用setVariable方法设置的参数,只能通过getVariable方法取得。一个执行流结束后,将不能获取使用setVariableLocal方法设置的参数,而通过setVariable方法设置的参数,不管当前执行流是否结束,只要流程尚未结束,仍然可以通过getVariable方法获取该参数。因此,
setVariableLocal方法设置的参数的作用域仅仅局限于当前设置的执行流中,一旦该执行流结束,该参数会失效,而使用setVariable方法设置的参数,可以作用于整个流程。
3.3、其他设置参数的方法
与TaskService类似,RuntimeService也提供了可以设置多个参数的方法:setVariables和setVariablesLocal方法,调用这两个方法时,只需要将参数封装到一个Map对象中即可。这两个方法的使用在此不再赘述。需要注意的是,如果设置的参数类型不是基本数据类型,就会将其进行序列化并转换为byte数组,保存到资源表(ACT_GE_BYTEARRAY)中。
4、流程操作
4.1、流程触发
在工作流程中,对于用户任务,可以使用TaskService的complete方法将当前流程节点的任务完成,完成后流程就会向前进行,而对于receiveTask等流程节点,则可以使用trigger方法将其触发,使流程往前进行。RuntimeService的trigger方法描述如下。
- trigger(String executionId):触发流程中的等待节点,让流程往下执行。
- trigger((String executionld,Map<String,Object>processVariables):与前面的trigger方法类似,可以设置流程参数。
- trigger(String executionId,Map<String,Object> processVariables,Map<String.Object> transientVariables):与前面的trigger方法类似,可以设置临时参数。
这里的第二个trigger方法允许传入多个流程参数,
需要注意的是,此处传入的参数将作用于整个流程,而不仅仅局限于某个执行流,第三个tigger方法允许传入临时参数。
<process id="vacationRequest" name="vacationRequest" isExecutable="true">
<startEvent id="startevent1" name="Start"></startEvent>
<endEvent id="endevent1" name="End"></endEvent>
<receiveTask id="receivetask1" name="Receive Task"></receiveTask>
<userTask id="usertask1" name="Complete Task"></userTask>
<sequenceFlow id="flow1" sourceRef="startevent1" targetRef="receivetask1"></sequenceFlow>
<sequenceFlow id="flow2" sourceRef="receivetask1" targetRef="usertask1"></sequenceFlow>
<sequenceFlow id="flow3" sourceRef="usertask1" targetRef="endevent1"></sequenceFlow>
</process>
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = engine.getRepositoryService();
RuntimeService runtimeService = engine.getRuntimeService();
// 部署流程描述文件
repositoryService.createDeployment().addClasspathResource("demo9/trigger.bpmn").deploy();
// 开始流程
ProcessInstance pi = runtimeService.startProcessInstanceByKey("vacationRequest");
// 查找执行流(当前只有一个执行流)
Execution exe = runtimeService.createExecutionQuery().activityId("receivetask1").singleResult();
if (exe != null) {
System.out.println("当前流程节点为: receivetask1");
}
// 触发等待节点
runtimeService.trigger(exe.getId());
// 查询当前的流程节点
exe = runtimeService.createExecutionQuery().activityId("usertask1").singleResult();
if (exe != null) {
System.out.println("当前流程节点为: usertask1");
}
当前流程节点为: receivetask1
当前流程节点为: usertask1
4.2、触发信号事件
事件节点是在流程中记录事件发生的流程元素,BPMN2.0规范中主要有两种类型的事件:捕获(Catching)事件和抛出(Throwing)事件。如果在一个流程中定义了Catching事件节点,则流程执行到该节点时,会一直等待触发的信号,直到接收到相应的信号后,流程才继续向前执行,此为Catching事件。如果在一个流程中定义了Throwing事件,则当流程执行到该节点时,该事件节点会自动往下执行,并不需要任何的信号指示,此为Throwing事件。在Catching事件中,如果希望流程继续往下执行,可以使用RuntimeService的signalEventReceived方法抛出信号,那么Catching事件节点就会捕获到相应的信号,让流程继续往下执行。当流程到达信号Catching事件时,事件表(ACT RU_EVENT_SUBSCR)中会产生相应的事件描述数据,
对应的EVENT_TYPE字段值为signal。
如图所示,当流程启动后,会到达“First Task”节点,流程继续向下执行,则会到达定义的Catching事件:
<signal id="testSignal" name="testSignal"></signal>
<process id="testProcess" name="testProcess">
<startEvent id="startevent1" name="Start"></startEvent>
<intermediateCatchEvent id="signalCatchEvent"
name="SignalCatchEvent">
<signalEventDefinition signalRef="testSignal"></signalEventDefinition>
</intermediateCatchEvent>
<endEvent id="endevent1" name="End"></endEvent>
<receiveTask id="usertask1" name="First Task"></receiveTask>
<sequenceFlow id="flow1" name="" sourceRef="startevent1"
targetRef="usertask1"></sequenceFlow>
<sequenceFlow id="flow2" name="" sourceRef="usertask1"
targetRef="signalCatchEvent"></sequenceFlow>
<sequenceFlow id="flow3" name="" sourceRef="signalCatchEvent"
targetRef="endevent1"></sequenceFlow>
</process>
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = engine.getRepositoryService();
RuntimeService runtimeService = engine.getRuntimeService();
// 部署流程描述文件
repositoryService.createDeployment().addClasspathResource("demo9/signalEventReceived.bpmn").deploy();
// 开始流程
ProcessInstance pi = runtimeService.startProcessInstanceByKey("testProcess");
// 查询执行流
Execution exe = runtimeService.createExecutionQuery().activityId("usertask1").singleResult();
System.out.println("当前节点:" + exe.getActivityId());
// 触发receiveTask
runtimeService.trigger(exe.getId());
// 查询当前节点
exe = runtimeService.createExecutionQuery().activityId("signalCatchEvent").singleResult();
System.out.println("调用trigger方法后当前节点:" + exe.getActivityId());
// 发送信号给事件,流程结束
runtimeService.signalEventReceived("testSignal");
List exes = runtimeService.createExecutionQuery()
.processInstanceId(pi.getId()).list();
System.out.println("触发Catch事件后,执行流数量:" + exes.size());
当前节点:usertask1
调用trigger方法后当前节点:signalCatchEvent
触发Catch事件后,执行流数量:0
4.3、触发消息事件
Activiti中除了提供了信号事件节点外,还提供了消息事件节点。可以使用messageEventDefinition元素定义一个消息事件,该元素可以被定义在startEvent和intermediateCatchEvent元素中。如果在startEvent中定义了消息事件,就可以使用RuntimeService的startProcess-
InstanceByMessage方法来启动流程;如果在intermediateCatchEvent元素下定义了消息事件,那么就意味着该消息事件是一个Catching事件。换言之,当执行流执行到该节点时,会一直等待触发的消息,直到收到触发的消息后,执行流才会继续往下进行。流程到达消息Catching事件时,会在事件表中产生事件描述数据,对应的EVENT_TYPE字段值为message.
当执行流遇到消息事件时,可以使用RuntimService的messageEventReceived方法向其发送信号,通知流程引擎该事件被接收。
<message id="testMsg" name="testMsg"></message>
<process id="testProcess" name="testProcess">
<startEvent id="startevent1" name="Start"></startEvent>
<userTask id="usertask1" name="End Task"></userTask>
<intermediateCatchEvent id="messageintermediatecatchevent1"
name="MessageCatchEvent">
<messageEventDefinition messageRef="testMsg"></messageEventDefinition>
</intermediateCatchEvent>
<endEvent id="endevent1" name="End"></endEvent>
<sequenceFlow id="flow1" name="" sourceRef="startevent1"
targetRef="messageintermediatecatchevent1"></sequenceFlow>
<sequenceFlow id="flow2" name=""
sourceRef="messageintermediatecatchevent1" targetRef="usertask1"></sequenceFlow>
<sequenceFlow id="flow3" name="" sourceRef="usertask1"
targetRef="endevent1"></sequenceFlow>
</process>
// 创建流程引擎
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 得到流程存储服务实例
RepositoryService repositoryService = engine.getRepositoryService();
// 得到运行时服务组件
RuntimeService runtimeService = engine.getRuntimeService();
// 部署流程描述文件
repositoryService.createDeployment()
.addClasspathResource("bpmn/MessageEvent.bpmn").deploy();
// 开始流流程
runtimeService.startProcessInstanceByKey("testProcess");
// 查询当前节点
Execution exe = runtimeService.createExecutionQuery()
.activityId("messageintermediatecatchevent1").singleResult();
System.out.println("当前流程节点:" + exe.getActivityId());
// 触发消息事件
runtimeService.messageEventReceived("testMsg", exe.getId());
// 查询当前事件
exe = runtimeService.createExecutionQuery().activityId("usertask1")
.singleResult();
System.out.println("当前流程节点:" + exe.getActivityId());
当前流程节点:messageintermediatecatchevent1
当前流程节点:usertask1
4.4、中断与激活流程
由前面的讲述可知,流程表使用SUSPENSION_STATE字段来保存流程的中断状态,若该字段值为1,则表示该流程为活跃状态;如果值为2,则表示该流程为中断状态。
RuntimeService中提供了中断流程(suspendProcessInstanceByld)和激活流程(activateProcessInstanceById)的方法,在调用这两个方法时,需要提供流程实例的ID。
<process id="testProcess" name="testProcess">
<startEvent id="startevent1" name="Start"></startEvent>
<userTask id="usertask1" name="First Task"></userTask>
<userTask id="usertask2" name="End Task"></userTask>
<endEvent id="endevent1" name="End"></endEvent>
<sequenceFlow id="flow1" name="" sourceRef="startevent1" targetRef="usertask1"></sequenceFlow>
<sequenceFlow id="flow2" name="" sourceRef="usertask1" targetRef="usertask2"></sequenceFlow>
<sequenceFlow id="flow3" name="" sourceRef="usertask2" targetRef="endevent1"></sequenceFlow>
</process>
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = engine.getRepositoryService();
RuntimeService runtimeService = engine.getRuntimeService();
// 部署流程描述文件
repositoryService.createDeployment().addClasspathResource("demo9/SuspendProcess.bpmn").deploy();
// 开始流流程
ProcessInstance pi = runtimeService.startProcessInstanceByKey("testProcess");
// 中断流程
runtimeService.suspendProcessInstanceById(pi.getId());
Execution exe = runtimeService.createExecutionQuery()
.activityId("usertask1").singleResult();
System.out.println("中断后执行流状态:" + exe.isSuspended());
// 激活流程
runtimeService.activateProcessInstanceById(pi.getId());
exe = runtimeService.createExecutionQuery().activityId("usertask1")
.singleResult();
System.out.println("激活后执行流状态:" + exe.isSuspended());
4.5、删除流程
删除运行时的流程,可以使用RuntimeService的deleteProcessInstance(String processInstanceld,
String deleteReason)方法,调用该方法时只需要提供流程实例的ID和删除原因字符串即可。流程实例被删除后,相关的数据将会从原来运行时的表中被删除,将这部分数据存放到历史表中,其中包括流程实例、流程任务和流程节点等数据。
<process id="testProcess" name="testProcess">
<startEvent id="startevent1" name="Start"></startEvent>
<userTask id="usertask1" name="First Task"></userTask>
<endEvent id="endevent1" name="End"></endEvent>
<userTask id="usertask3" name="Second Task"></userTask>
<parallelGateway id="parallelgateway1" name="Parallel Gateway"></parallelGateway>
<sequenceFlow id="flow1" name="" sourceRef="startevent1" targetRef="parallelgateway1"></sequenceFlow>
<sequenceFlow id="flow2" name="" sourceRef="parallelgateway1" targetRef="usertask1"></sequenceFlow>
<sequenceFlow id="flow3" name="" sourceRef="parallelgateway1" targetRef="usertask3"></sequenceFlow>
<parallelGateway id="parallelgateway2" name="Parallel Gateway"></parallelGateway>
<sequenceFlow id="flow4" name="" sourceRef="usertask1" targetRef="parallelgateway2"></sequenceFlow>
<sequenceFlow id="flow5" name="" sourceRef="usertask3" targetRef="parallelgateway2"></sequenceFlow>
<sequenceFlow id="flow6" name="" sourceRef="parallelgateway2" targetRef="endevent1"></sequenceFlow>
</process>
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = engine.getRepositoryService();
RuntimeService runtimeService = engine.getRuntimeService();
// 部署流程描述文件
repositoryService.createDeployment().addClasspathResource("demo9/DeleteProcess.bpmn").deploy();
// 开始流流程
ProcessInstance pi = runtimeService.startProcessInstanceByKey("testProcess");
long count = runtimeService.createProcessInstanceQuery().count();
System.out.println("启动时流程实例数量:" + count);
// 删除流程
runtimeService.deleteProcessInstance(pi.getId(), "abc");
count = runtimeService.createProcessInstanceQuery().count();
System.out.println("删除后流程实例数量:" + count);
启动时流程实例数量:3
删除后流程实例数量:2
5、流程数据查询
经过操作后,流程中也会产生相关的数据,Activiti为流程实例和执行流提供了相应的Query查询象.RuntimeService提供了创建这些查询对象的方法,得到相应的Query对象后,可以使用其提供的设置查询条件的方法,最后使用list、singleResult等方法返回查询结果,也可以使用orderByXXX方法对查询结果进行排序。
5.1、执行流查询
使用RuntimeService的createExecutionQuery方法可以得到一个ExecutionQuery对象,使用该对象可以根据执行流的相关数据查询执行流,这些数据包括执行流的参数、执行流的活动状态、执行流信号事件和执行流消息事件等。
在图中定义了一个含有信号事件、消息事件的流程。下面将使用该流程作为例子,调用ExecutionQuery的几个主要查询方法:
<signal id="testSignal" name="signalName"></signal>
<message id="testMsg" name="messageName"></message>
<process id="testProcess" name="testProcess2">
<startEvent id="startevent1" name="Start"></startEvent>
<intermediateCatchEvent id="messageintermediatecatchevent1" name="MessageCatchEvent">
<messageEventDefinition messageRef="testMsg"></messageEventDefinition>
</intermediateCatchEvent>
<endEvent id="endevent1" name="End"></endEvent>
<intermediateCatchEvent id="signalintermediatecatchevent1" name="SignalCatchEvent">
<signalEventDefinition signalRef="testSignal"></signalEventDefinition>
</intermediateCatchEvent>
<sequenceFlow id="flow1" name="" sourceRef="startevent1" targetRef="messageintermediatecatchevent1"></sequenceFlow>
<sequenceFlow id="flow4" name="" sourceRef="signalintermediatecatchevent1" targetRef="endevent1"></sequenceFlow>
<sequenceFlow id="flow5" name="" sourceRef="messageintermediatecatchevent1" targetRef="signalintermediatecatchevent1"></sequenceFlow>
</process>
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = engine.getRepositoryService();
RuntimeService runtimeService = engine.getRuntimeService();
repositoryService.createDeployment().addClasspathResource("demo9/ExecutionQuery.bpmn").deploy();
//设置参数
Map<String, Object> vars1 = new HashMap<String, Object>();
vars1.put("days", 5);
Map<String, Object> vars2 = new HashMap<String, Object>();
vars2.put("days", 6);
Map<String, Object> vars3 = new HashMap<String, Object>();
vars3.put("days", 7);
vars3.put("name", "crazyit");
// 开始流流程
ProcessInstance pi1 = runtimeService.startProcessInstanceByKey("testProcess",
"businessKey1", vars1);
ProcessInstance pi2 = runtimeService.startProcessInstanceByKey("testProcess",
"businessKey2", vars2);
ProcessInstance pi3 = runtimeService.startProcessInstanceByKey("testProcess",
"businessKey3", vars3);
// 使用执行流查询方法
List<Execution> exes = runtimeService.createExecutionQuery()
.processDefinitionKey("testProcess").list();
System.out.println("使用processDefinitionKey方法查询执行流:" + exes.size());
exes = runtimeService.createExecutionQuery()
.processInstanceBusinessKey("businessKey1").list();
System.out.println("使用processInstanceBusinessKey方法查询执行流:" + exes.size());
exes = runtimeService.createExecutionQuery()
.messageEventSubscriptionName("messageName").list();
System.out.println("使用messageEventSubscriptionName方法查询执行流:" + exes.size());
// 根据节点id属性查询当前的执行流
Execution exe = runtimeService.createExecutionQuery()
.activityId("messageintermediatecatchevent1")
.processInstanceId(pi1.getId()).singleResult();
System.out.println("使用activityId和processInstanceId方法查询执行流,得到执行ID:" + exe.getId());
//让流程往下执行
runtimeService.messageEventReceived("messageName", exe.getId());
exes = runtimeService.createExecutionQuery().signalEventSubscriptionName("signalName").list();
System.out.println("使用signalEventSubscriptionName方法查询执行流:" + exes.size());
// 根据参数查询执行流
exes = runtimeService.createExecutionQuery().variableValueEquals("name", "crazyit").list();
System.out.println("使用variableValueEquals方法查询执行流:" + exes.size());
exes = runtimeService.createExecutionQuery().variableValueGreaterThan("days", 5).list();
System.out.println("使用variableValueGreaterThan方法查询执行流:" + exes.size());
exes = runtimeService.createExecutionQuery().variableValueGreaterThanOrEqual("days", 5).list();
System.out.println("使用variableValueGreaterThanOrEqual方法查询执行流:" + exes.size());
exes = runtimeService.createExecutionQuery().variableValueLessThan("days", 6).list();
System.out.println("使用variableValueLessThan方法查询执行流:" + exes.size());
exes = runtimeService.createExecutionQuery().variableValueLessThanOrEqual("days", 6).list();
System.out.println("使用variableValueLessThanOrEqual方法查询执行流:" + exes.size());
exes = runtimeService.createExecutionQuery().variableValueLike("name", "%crazy%").list();
System.out.println("使用variableValueLike方法查询执行流:" + exes.size());
exes = runtimeService.createExecutionQuery().variableValueNotEquals("days", 8).list();
System.out.println("使用variableValueNotEquals方法查询执行流:" + exes.size());
使用processDefinitionKey方法查询执行流:6
使用processInstanceBusinessKey方法查询执行流:1
使用messageEventSubscriptionName方法查询执行流:3
使用activityId和processInstanceId方法查询执行流,得到执行ID:10007
使用signalEventSubscriptionName方法查询执行流:1
使用variableValueEquals方法查询执行流:1
使用variableValueGreaterThan方法查询执行流:2
使用variableValueGreaterThanOrEqual方法查询执行流:3
使用variableValueLessThan方法查询执行流:1
使用variableValueLessThanOrEqual方法查询执行流:2
使用variableValueLike方法查询执行流:1
使用variableValueNotEquals方法查询执行流:3
5.2、流程实例查询
与执行流类似,可以使用RuntimeService中的createProcessInstanceQuery方法获取
ProcessInstanceQuery实例,ProcessInstanceQuery提供的设置查询条件的方法与ExecutionQuery提供的类似。
<signal id="testSignal" name="signalName"></signal>
<message id="testMsg" name="messageName"></message>
<process id="testProcess" name="testProcess2">
<startEvent id="startevent1" name="Start"></startEvent>
<intermediateCatchEvent id="messageintermediatecatchevent1" name="MessageCatchEvent">
<messageEventDefinition messageRef="testMsg"></messageEventDefinition>
</intermediateCatchEvent>
<endEvent id="endevent1" name="End"></endEvent>
<intermediateCatchEvent id="signalintermediatecatchevent1" name="SignalCatchEvent">
<signalEventDefinition signalRef="testSignal"></signalEventDefinition>
</intermediateCatchEvent>
<sequenceFlow id="flow1" name="" sourceRef="startevent1" targetRef="messageintermediatecatchevent1"></sequenceFlow>
<sequenceFlow id="flow4" name="" sourceRef="signalintermediatecatchevent1" targetRef="endevent1"></sequenceFlow>
<sequenceFlow id="flow5" name="" sourceRef="messageintermediatecatchevent1" targetRef="signalintermediatecatchevent1"></sequenceFlow>
</process>
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = engine.getRepositoryService();
RuntimeService runtimeService = engine.getRuntimeService();
// 部署流程描述文件
repositoryService.createDeployment().addClasspathResource("demo9/ProcessInstanceQuery.bpmn").deploy();
ProcessInstance pi1 = runtimeService.startProcessInstanceByKey(
"testProcess", "key1");
ProcessInstance pi2 = runtimeService.startProcessInstanceByKey(
"testProcess", "key2");
ProcessInstance pi3 = runtimeService.startProcessInstanceByKey(
"testProcess", "key3");
// 将流程置为中断状态
runtimeService.suspendProcessInstanceById(pi1.getId());
// 查询流程实例
List<ProcessInstance> pis = runtimeService.createProcessInstanceQuery()
.processDefinitionKey("testProcess").list();
System.out.println("使用processDefinitionKey方法查询流程实例:" + pis.size());
pis = runtimeService.createProcessInstanceQuery().active().list();
System.out.println("使用active方法查询流程实例:" + pis.size());
pis = runtimeService.createProcessInstanceQuery()
.processInstanceBusinessKey("key2").list();
System.out.println("使用processInstanceBusinessKey方法查询流程实例:" + pis.size());
// 根据多个流程实例ID查询
Set<String> ids = new HashSet<String>();
ids.add(pi1.getId());
ids.add(pi2.getId());
pis = runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list();
System.out.println("使用processInstanceIds方法查询流程实例:" + pis.size());
使用processDefinitionKey方法查询流程实例:3
使用active方法查询流程实例:2
使用processInstanceBusinessKey方法查询流程实例:1
使用processInstanceIds方法查询流程实例:2