1、什么是流程变量

       流程变量在activiti中是一个非常重要的角色,流程运转有时需要靠流程变量,业务系统和activiti结合时少不了流程变量,流程变量就是activiti在管理工作流时根据管理需要而设置的变量。
       比如在请假流程流转时如果请假天数大于3天则由总经理审核,否则由人事直接审核,请假天数就可以设置为流程变量,在流程流转时使用。

注意:虽然流程变量中可以存储业务数据,可以通过activiti的api查询流程变量,从而实现查询业务数据,但是不建议这样使用,因为业务数据查询由业务系统负责,activiti设置流程变量是为了流程执行需要而创建。

2、流程变量类型

注意:如果将pojo存储到流程变量中,必须实现序列化接口serializable,为了防止由于新增字段无法反序列化,需要生成serialVersionUID。

3、流程变量作用域

       流程变量的作用域默认是一个流程实例(processInstance),也可以是一个任务(task)或一个执行实例(execution),这三个作用域流程实例的范围最大,可以称为global变量,任务和执行实例仅仅是针对一个任务和一个执行实例范围,范围没有流程实例大,称为local变量。
global变量中,变量名不允许重复,设置相同名称的变量,后设置的值会覆盖前设置的变量值。
Local变量由于在不同的任务或不同的执行实例中,作用域互不影响,变量名可以相同没有影响。
Local变量名也可以和global变量名相同,没有影响。

 

4、流程变量的使用方法

第一步:设置流程变量
第二步:通过UEL表达式使用流程变量
       1> 可以在assignee处设置UEL表达式,表达式的值为任务的负责人
             比如:${assignee},assignee就是一个流程变量名称
             Activiti获取UEL表达式的值 ,即流程变量assignee的值 ,将assignee的值作为任务的负责人进行任务分配
       2> 可以在连线上设置UEL表达式,决定流程走向
             比如:${price>=10000}和${price<10000}:price就是一个流程变量名称,uel表达式结果类型为布尔类型,如果UEL表达式是true,要决定流程执行走向。

5、使用Global变量控制流程

5.1 需求1

       员工创建请假申请单,由部门经理审核,部门经理审核通过后,请假3天及以下的由人事经理直接审核,3天以上先由总经理审核,总经理审核通过再由人事经理存档。

5.2 部署该流程定义

  1.  
    package com.zdw.activiti03;
  2.  
     
  3.  
    import org.activiti.engine.ProcessEngine;
  4.  
    import org.activiti.engine.ProcessEngines;
  5.  
    import org.activiti.engine.RepositoryService;
  6.  
    import org.activiti.engine.repository.Deployment;
  7.  
     
  8.  
    /**
  9.  
    * 流程变量的测试类
  10.  
    */
  11.  
    public class VariablesTest {
  12.  
    //部署流程定义
  13.  
    public static void main(String[] args) {
  14.  
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  15.  
    RepositoryService repositoryService = processEngine.getRepositoryService();
  16.  
    Deployment deployment = repositoryService.createDeployment().addClasspathResource("bpmn/holiday3.bpmn")
  17.  
    .addClasspathResource("bpmn/holiday3.png")
  18.  
    .name("有分支的请假流程").deploy();
  19.  
    System.out.println(deployment.getName());
  20.  
    }
  21.  
    }

5.3 设置global流程变量

       在部门经理审核前设置流程变量,变量值为请假单信息(包括请假天数),部门经理审核后可以根据流程变量的值决定流程走向。

5.3.1 启动流程时设置

在启动流程时设置流程变量,变量的作用域是整个流程实例。
通过map<key,value>设置流程变量,map中可以设置多个变量,这个key就是流程变量的名字。

1、先要定义个实体Holiday,要实现序列化接口:

  1.  
    package com.zdw.activiti03;
  2.  
     
  3.  
    import java.io.Serializable;
  4.  
     
  5.  
    /**
  6.  
    * Create By zdw on 2019/8/6
  7.  
    */
  8.  
    public class Holiday implements Serializable {
  9.  
    private Float num;
  10.  
     
  11.  
    public Float getNum() {
  12.  
    return num;
  13.  
    }
  14.  
     
  15.  
    public void setNum(Float num) {
  16.  
    this.num = num;
  17.  
    }
  18.  
    }

2、启动流程实例,设置流程变量的值

  1.  
    package com.zdw.activiti03;
  2.  
     
  3.  
    import org.activiti.engine.ProcessEngine;
  4.  
    import org.activiti.engine.ProcessEngines;
  5.  
    import org.activiti.engine.RepositoryService;
  6.  
    import org.activiti.engine.RuntimeService;
  7.  
    import org.activiti.engine.repository.Deployment;
  8.  
    import org.activiti.engine.runtime.ProcessInstance;
  9.  
     
  10.  
    import java.util.HashMap;
  11.  
    import java.util.Map;
  12.  
     
  13.  
    /**
  14.  
    * 流程变量的测试类
  15.  
    */
  16.  
    public class VariablesTest {
  17.  
     
  18.  
    //启动流程实例的时候,设置流程变量
  19.  
    public static void main(String[] args) {
  20.  
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  21.  
    RuntimeService runtimeService = processEngine.getRuntimeService();
  22.  
    //设置变量
  23.  
    Map<String,Object> params = new HashMap<>();
  24.  
    params.put("applicator","小丽");
  25.  
    params.put("bumenjingli","毛毛");
  26.  
    params.put("zongjingli","天天");
  27.  
    params.put("hrjingli","滴滴");
  28.  
     
  29.  
    Holiday holiday = new Holiday();
  30.  
    holiday.setNum(5f);
  31.  
    params.put("holiday",holiday);
  32.  
     
  33.  
    //启动流程实例,设置变量
  34.  
    ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("myholiday3", "businessKey:1001",params);
  35.  
    System.out.println("流程实例的ID:"+processInstance.getId());
  36.  
    System.out.println("流程定义的ID:"+processInstance.getProcessDefinitionId());
  37.  
    System.out.println("业务key:"+processInstance.getBusinessKey());
  38.  
     
  39.  
    }
  40.  
    }

说明:startProcessInstanceByKey(processDefinitionKey, variables)流程变量作用域是一个流程实例,流程变量使用Map存储,同一个流程实例设置变量map中key相同,后者覆盖前者。

查看act_ru_variable表,里面有各个变量的值:

这个表中holiday变量的值的类型是serializable,它对应了一个BYTEARRAY_ID的值是5005,所以它的值真正存储在表act_ge_bytearray中,

 

 5.3.2 任务办理时设置

       在完成任务时设置流程变量,该流程变量只有在该任务完成后其它结点才可使用该变量,它的作用域是整个流程实例,如果设置的流程变量的key在流程实例中已存在相同的名字则后设置的变量替换前边设置的变量。
       这里需要在创建请假单任务完成时设置流程变量

我们前面已经设置了各个变量的值,现在我们可以在完成任务的时候,再次设置变量的值,如果map中的key相同,那么我们现在设置的值会替换掉前面设置的变量的值。

  1.  
    public class VariablesTest {
  2.  
     
  3.  
    //完成任务时,设置流程变量
  4.  
    public static void main(String[] args) {
  5.  
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  6.  
    TaskService taskService = processEngine.getTaskService();
  7.  
    TaskQuery taskQuery = taskService.createTaskQuery();
  8.  
    //查询小丽的任务
  9.  
    Task task = taskQuery.processDefinitionKey("myholiday3").taskAssignee("小丽").singleResult();
  10.  
    //我们可以在小丽完成任务的时候,设置部门经理的执行人为 小李,之前设置的是毛毛
  11.  
    Map<String,Object> params = new HashMap<>();
  12.  
    params.put("bumenjingli","小李");
  13.  
     
  14.  
    //完成任务,设置变量
  15.  
    if(task!=null){
  16.  
    taskService.complete(task.getId(),params);
  17.  
    System.out.println("任务执行完成,设置了后面的变量");
  18.  
    }
  19.  
    }
  20.  
    }

查看表:act_ru_variable,发现变量的值已经变了:

说明:通过当前任务设置流程变量,需要指定当前任务id,如果当前执行的任务id不存在则抛出异常。
任务办理时也是通过map<key,value>设置流程变量,一次可以设置多个变量。

 

5.3.3 通过当前流程实例设置

       通过流程实例id设置全局变量,该流程实例必须未执行完成。该方式和5.3.1 方式是差不多的,5.3.1是启动流程实例的时候,一并把变量给设置了。而当前的这种方式就是:在启动流程实例的时候,忘记了设置流程变量的值,在流程实例已经启动之后,我们得到流程实例的ID,然后来设置全局的流程变量的值

  1.  
    public class VariablesTest {
  2.  
     
  3.  
    //通过流程实例的id来设置流程变量的值
  4.  
    public static void main(String[] args) {
  5.  
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  6.  
    RuntimeService runtimeService = processEngine.getRuntimeService();
  7.  
    //我们可以设置部门经理的执行人为 小李子,之前设置的是小李
  8.  
    Map<String,Object> params = new HashMap<>();
  9.  
    params.put("bumenjingli","小李子");
  10.  
    //流程实例的ID
  11.  
    String processInstanceId = "5001";
  12.  
     
  13.  
    runtimeService.setVariables(processInstanceId,params);
  14.  
    }
  15.  
    }

查看表act_ru_variable的变量的值:

注意:processInstanceId必须当前未结束 流程实例的执行id,通常此id设置流程实例 的id。
也可以通过runtimeService.getVariable()获取流程变量

5.3.4 通过当前任务设置

这种方式和5.3.2 的原理是一样的。5.3.2 是在执行代办的任务的时候,把流程变量的值设置好,而现在的这种方式是我们单独查询到了代办任务的id,然后根据该任务id来设置流程变量的值。

  1.  
    public class VariablesTest {
  2.  
     
  3.  
    //通过代办任务的id来设置流程变量的值
  4.  
    public static void main(String[] args) {
  5.  
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  6.  
    TaskService taskService = processEngine.getTaskService();
  7.  
    //代办任务的id
  8.  
    String taskId = "7502";
  9.  
    //我们可以设置部门经理的执行人为 李志,之前设置的是小李子
  10.  
    Map<String,Object> params = new HashMap<>();
  11.  
    params.put("bumenjingli","李志");
  12.  
     
  13.  
    taskService.setVariables(taskId,params);
  14.  
    System.out.println("通过任务id设置流程变量完成");
  15.  
    }
  16.  
    }

查看表act_ru_variable的变量的值:

注意:任务id必须是当前待办任务id,act_ru_task中存在。如果该任务已结束,报错:

也可以通过taskService.getVariable()获取流程变量。

5.3.5 注意事项

1、 如果UEL表达式中流程变量名不存在则报错。
2、 如果UEL表达式中流程变量值为空NULL,流程不按UEL表达式去执行,而流程结束 。
3、 如果UEL表达式都不符合条件,流程结束
4、 如果连线不设置条件,会走flow序号小的那条线

 

6、设置local流程变量

6.1 任务办理时设置

       任务办理时设置local流程变量,当前运行的流程实例只能在该任务结束前使用,任务结束该变量无法在当前流程实例使用,可以通过查询历史任务查询。

  1.  
    //完成任务时,设置流程变量
  2.  
    public static void main3(String[] args) {
  3.  
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  4.  
    TaskService taskService = processEngine.getTaskService();
  5.  
    TaskQuery taskQuery = taskService.createTaskQuery();
  6.  
    //查询小丽的任务
  7.  
    Task task = taskQuery.processDefinitionKey("myholiday3").taskAssignee("小丽").singleResult();
  8.  
    //我们可以在小丽完成任务的时候,设置部门经理的执行人为 小李,之前设置的是毛毛
  9.  
    Map<String,Object> params = new HashMap<>();
  10.  
    params.put("bumenjingli","小李");
  11.  
     
  12.  
    //完成任务,设置局部Local变量
  13.  
    if(task!=null){
  14.  
    taskService.setVariablesLocal(task.getId(),params);
  15.  
    taskService.complete(task.getId());
  16.  
    }
  17.  
    }

说明:设置作用域为任务的local变量,每个任务可以设置同名的变量,互不影响。

6.2 通过当前任务设置

  1.  
    //通过代办任务的id来设置局部Local流程变量的值
  2.  
    public static void main5(String[] args) {
  3.  
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  4.  
    TaskService taskService = processEngine.getTaskService();
  5.  
    //代办任务的id
  6.  
    String taskId = "7502";
  7.  
    //我们可以设置部门经理的执行人为 李志,之前设置的是小李子
  8.  
    Map<String,Object> params = new HashMap<>();
  9.  
    params.put("bumenjingli","李志");
  10.  
     
  11.  
    taskService.setVariablesLocal(taskId,params);
  12.  
    System.out.println("通过任务id设置流程变量完成");
  13.  
    }

注意:任务id必须是当前待办任务id,act_ru_task中存在。

Local变量在任务结束后无法在当前流程实例执行中使用,如果后续的流程执行需要用到此变量则会报错。

6.3 Local变量值的查询

       在部门经理审核、总经理审核、人事经理审核时设置local变量,可通过historyService查询每个历史任务时将流程变量的值也查询出来。

  1.  
    // 创建历史任务查询对象
  2.  
    HistoricTaskInstanceQuery historicTaskInstanceQuery = historyService.createHistoricTaskInstanceQuery();
  3.  
    // 查询结果包括 local变量
  4.  
    ArrayList<HistoricTaskInstance> list = historicTaskInstanceQuery.includeTaskLocalVariables();
  5.  
    for (HistoricTaskInstance historicTaskInstance : list) {
  6.  
    System.out.println("==============================");
  7.  
    System.out.println("任务id:" + historicTaskInstance.getId());
  8.  
    System.out.println("任务名称:" + historicTaskInstance.getName());
  9.  
    System.out.println("任务负责人:" + historicTaskInstance.getAssignee());
  10.  
    System.out.println("任务local变量:"+ historicTaskInstance.getTaskLocalVariables());
  11.  
    }

注意:查询历史流程变量,特别是查询pojo变量需要经过反序列化,不推荐使用。