(4)activiti工作流引擎之uel表达式
有了前面几章,我们肯定有一定的困惑,activiti如何与实际业务整合,比如一条采购单,如何跟一个流程实例互相关联起来?
这里就需要使用到activiti启动流程实例时设置一个流程实例的businessKey(一般存储我们一条采购单的id)
1,启动流程实例设置其businessKey
/** * 启动一个流程实例,设置其业务id */ @Test public void startProInsWithKey() { RuntimeService runtimeService = engine.getRuntimeService(); String processDefinitionKey = "purchasingflow"; //设置一个businessKey,在我实际业务中可能是一个采购单,或者订单之类的id String businessKey ="111"; // 根据流程定义的key启动一个流程实例 ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey,businessKey); System.out.println("流程实例id:" + processInstance.getId()); System.out.println("流程定义id:" + processInstance.getProcessDefinitionId()); }
一般情况下,我们是在用户保存一条采购单的时候,启动这个实例,并且动态获取采购单的id(也就是用作流程实例的businessKey),并且我们还会在采购单表中保存这个流程实例的id,双向一对一绑定,方便业务查询
2,根据采购单的id(也就是用作流程实例的businessKey),动态的查询出对应的流程实例
/** * 通过businessKey查询流程实例 */ @Test public void queryProInsWithKey(){ RuntimeService runtimeService = engine.getRuntimeService(); String businessKey ="111"; ProcessInstanceQuery instanceQuery = runtimeService.createProcessInstanceQuery(); //根据其流程定义key和业务id businessKey查询出对应的流程实例,一般只有一条 instanceQuery.processInstanceBusinessKey(businessKey); //查询出唯一的一条流程实例 ProcessInstance processInstance = instanceQuery.singleResult(); System.out.println("流程实例id:"+processInstance.getId()); System.out.println("流程定义id:"+processInstance.getProcessDefinitionId()); }
到这里,我们就清楚了一个流程实例和实际业务数据的绑定
大家到这里也启动了很多的流程实例了,发现我们的任务办理人都是写死为zhangsan,lisi之类的,那么我们可以动态的指定吗,这就需要使用到我们的uel表达式了
首先uel表达式到底是什么呢?
UEL是java EE6规范的一部分,UEL(Unified Expression Language)即统一表达式语言,activiti支持两个UEL表达式:UEL-value和UEL-method。我们都会分别做介绍
(一)我们先来演示uel-value
首先做个简单的使用带大家入门
使用步骤:(看不明白的运行下面的代码走一遍再回头看)
1,在任务的节点,不直接指定处理人的id,设置处理人表达式${assignee},并且重新部署流程定义
2, 启动一个流程实例是设置启动变量动态设置assignee的值
3,查看并验证下一个任务的处理人是否为我们动态设置
1,首先我们设置节点
2,在启动代码时动态设置assignee的值
/** * 启动流程实例时设置全局变量 */ @Test public void startProInsWithArgs(){ RuntimeService runtimeService = engine.getRuntimeService(); String processDefinitionKey = "purchasingflow"; //设置其启动的全局变量参数,其value可以是javabean,也可以是普通字符串,数字 Map<String,Object> variables = new HashMap<String, Object>(); variables.put("assignee", "feige"); //设置流程启动时,设置参数启动 ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey,variables); System.out.println("流程实例id:" + processInstance.getId()); System.out.println("流程定义id:" + processInstance.getProcessDefinitionId()); }
3,这个时候我们通过查看数据库,查看act_ru_task当前运行的任务,查看
从上面的我们可以看到流程走到了,创建采购单,并且采购的assignee处理人为feige
因为当我们走到了创建采购单时,会从当前流程实例的全局变量查找name为assignee的值,如果查询到,则将这个assignee的值赋给这个节点,如果我们动态设置了,但是走到这个节点前(为什么说节点前呢,因为还可以在别的时候设置变量,后面的章节会讲)
没有找到这个assignee变量的值,会报错的,大家可以自己试一试
当然除了上面的设置方法,我们还有其他的设置方法来动态设置其节点的值吗?答案是肯定的,启动的流程时候的参数的value,不仅仅可以是String类型,还可以是Object对象(序列化的),Map,List,Array
我们这里采用对象做演示,执行步骤如下
1,设置流程的第一个节点动态的值为${user.userId},他会默认查找变量name为user的对应的值的getUserId获取值,重新部署流程定义
2,启动流程时,设置这个user的javabean到流程的全局变量中
3,查看走到这个节点的当前任务的处理人是否是我们user的userId变量的值
1,设置节点
2,设置javabean User对象并且在流程实例启动时设置进去
public class User implements Serializable{ /** * 用于序列化 */ private static final long serialVersionUID = 7717000074223077256L; private String userId; private String sex; private String name; public User(String userId, String sex, String name) { super(); this.userId = userId; this.sex = sex; this.name = name; } public String getUserId() { return userId; } public void setUserId(String userId) { this.userId = userId; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
/** * 启动流程设置一个user用户到全局变量中 */ @Test public void startProInsWithObj(){ RuntimeService runtimeService = engine.getRuntimeService(); String processDefinitionKey = "purchasingflow"; //设置其启动的全局变量参数,其value设置为user对象,这里写死 User user = new User("101","男","张三"); Map<String,Object> variables = new HashMap<String, Object>(); variables.put("user", user); //设置流程启动时,设置参数启动 ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey,variables); System.out.println("流程实例id:" + processInstance.getId()); System.out.println("流程定义id:" + processInstance.getProcessDefinitionId()); }
3,到这里流程实例启动成功,我们观察数据库的当前任务表,观察第一个节点的处理人是否为我们设置的101的userId
设置成功!
(二)演示uel-method
执行步骤
1,设置节点的执行人为${method.getUserNameByUserId(userId)} ,其中method方法是我们注入到spring中的一个类,userId是我们设置的全局变量
2,将method方法注入到activiti的processEngineConfiguration的bean中(在我们的activiti.cfg.xml中)
3,启动一个流程设置全局变量userId作为启动参数,看看是否走到这个节点的处理人是我们method方法getUserNameByUserId返回的name
好了,直接上代码
1,设置节点,重新部署流程定义
2,method的java方法和activiti.cfg.xml的注入配置
public class CommonMethod{ public String getUserNameByUserId(int userId){ return "activiti"+userId; } }
<bean class="cn.nfcm.po.CommonMethod" id="commonmethod"></bean> <bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration"> <!-- 数据源 --> <property name="dataSource" ref="dataSource" /> <!-- activiti数据库表处理策略 --> <property name="databaseSchemaUpdate" value="true" /> <!-- 可以注入多个类到activiti的beans中,其中key对应的就是我们的类名 --> <property name="beans"> <map> <entry key="commonmethod" value-ref="commonmethod" /> </map> </property> </bean>
3,启动一个流程实例
/** * 根据方法得到值 */ @Test public void startProInsWithMethod(){ RuntimeService runtimeService = engine.getRuntimeService(); String processDefinitionKey = "purchasingflow"; //这里设置userId为8,走到第一个节点会查找我们注入进去的commonmethod的getUserNameByUserId方法并传递userId Map<String,Object> variables = new HashMap<String, Object>(); variables.put("userId", 8); //设置流程启动时,设置参数启动 ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey,variables); System.out.println("流程实例id:" + processInstance.getId()); System.out.println("流程定义id:" + processInstance.getProcessDefinitionId()); }
到这里我们会发现流程启动成功,并且流程的当前任务处理人为acitiviti8,在实际的应用中因为都是spring管理的类,我们可以进行多种多样的查询赋值,非常方便!