本文开始重新回归一些activiti的基本使用。感觉前面太想展开源码和架构方面的探讨,但在这过程中,又涉及到activiti很多基本的用法我没有熟练使用,例如表达式、网关、多实例任务、作业、边界事件等等。导致在探讨源码的时候常常一笔带过,虽然对整体理解没有太大的问题,但是总感觉缺了点什么。所以后续决定也要熟练这些基本功能的使用。毕竟很多业务场景还是需要使用这些功能的。

表达式简述

activiti使用UEL处理表达式。UEL(Unified Expression Language)是EE6规范的一部分。activiti使用JUEL以支撑所有的运行环境及最新的UEL规范。

表达式可以用在很多场景中,例如ServiceTask、执行监听器、任务监听器以及连线的条件上。

表达式表达式含义
${param}获取变量param的值
${param.age}获取对象param的age属性的值
${param.test()}调用对象param的test方法
${param.test("hello")}调用对象param的test方法,参数为字符串"hello"

除了上面所说的这些表达式,还有以下三种流程引擎的内置变量

变量名称变量含义
task通过此变量可以获取用户任务相关的属性。实现了DelegateTask接口
execution通过此变量可以获取流程实例的信息。实现了DelegateExecution接口
authenticatedUserId在启动流程时调用IdentityService的setAuthenticatedUserId方法设置

表达式举例

现在有个场景,新建一个请假流程,员工提交给经理审批,流程到员工待办和到经理待办时都有提示,流程结束时也会提示流程完成。

这个例子我们使用系列文章最简单的activiti.cfg.xml作为流程引擎配置文件,在《activiti学习(一)——activiti流程引擎的配置与初始化操作》中查找。流程文档expressBPM.bpmn如下:

<?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">
  <process id="expressProcess" name="Express process" isExecutable="true">
    <startEvent id="startevent1" name="Start"></startEvent>
    <userTask id="usertask1" name="usertask1" activiti:assignee="${employee}">
      <extensionElements>
        <activiti:taskListener event="create" expression="${notice.callEmployee(task)}"></activiti:taskListener>
      </extensionElements>
    </userTask>
    <sequenceFlow id="flow1" sourceRef="startevent1" targetRef="usertask1"></sequenceFlow>
    <userTask id="usertask2" name="usertask2" activiti:assignee="${manager}">
      <extensionElements>
        <activiti:taskListener event="create" delegateExpression="${callManager}"></activiti:taskListener>
      </extensionElements>
    </userTask>
    <sequenceFlow id="flow2" sourceRef="usertask1" targetRef="usertask2"></sequenceFlow>
    <endEvent id="endevent1" name="End">
      <extensionElements>
        <activiti:executionListener event="end" expression="${notice.callComplete(execution)}"></activiti:executionListener>
      </extensionElements>
    </endEvent>
    <sequenceFlow id="flow3" sourceRef="usertask2" targetRef="endevent1"></sequenceFlow>
  </process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_expressProcess">
    <bpmndi:BPMNPlane bpmnElement="expressProcess" id="BPMNPlane_expressProcess">
      <bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">
        <omgdc:Bounds height="35.0" width="35.0" x="150.0" y="160.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="usertask1" id="BPMNShape_usertask1">
        <omgdc:Bounds height="55.0" width="105.0" x="230.0" y="150.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="usertask2" id="BPMNShape_usertask2">
        <omgdc:Bounds height="55.0" width="105.0" x="380.0" y="150.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1">
        <omgdc:Bounds height="35.0" width="35.0" x="530.0" y="160.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
        <omgdi:waypoint x="185.0" y="177.0"></omgdi:waypoint>
        <omgdi:waypoint x="230.0" y="177.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
        <omgdi:waypoint x="335.0" y="177.0"></omgdi:waypoint>
        <omgdi:waypoint x="380.0" y="177.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3">
        <omgdi:waypoint x="485.0" y="177.0"></omgdi:waypoint>
        <omgdi:waypoint x="530.0" y="177.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

第5行usertask1节点通过${employee}表达式设置assignee。第7行为usertask1新增一个监听create事件的任务监听器,使用expression方式创建,调用notice对象的callEmployee方法,并把task内置变量作为参数传入。第11行usertask2节点通过${manager}表达式设置assignee。13行为usertask2新增一个监听create事件的任务监听器,使用delegateExpression方式创建,监听器对象为${callManager}变量。19行为endevent1节点新增一个监听end事件的执行监听器,调用notice对象的callComplete方法,并把execution内置变量作为参数传入。流程图本身并不复杂,但添加了几个监听器,并使用了表达式,初学者可能有点乱。

现在我们创建一个Notice类,将来把它的实例作为流程变量notice传入。这个类作为expression创建任务监听器的处理类,这种处方式不需要创建监听器类,只需要在流程变量中传入指定类的对象即可等待activiti调用对象的指定方法:

public class Notice implements Serializable{

	public void callEmployee(DelegateTask task) {
		String assignee = (String)task.getVariable("employee");
		System.out.println(assignee + " 有一个新的待办");
	}
	
	public void callComplete(DelegateExecution execution) {
		System.out.println(execution.getProcessBusinessKey() + " 流程完成");
	}
}

Notice类的实例因为要作为流程变量传入,所以必须实现Serializable接口。我们定义callEmployee和callComplete方法,分别表示通知员工有新待办和通知流程结束。注意这两个方法的参数,它们是通过内置变量task和execution传入,可往回查看流程文档对应的地方。

接下来创建一个ManagerListener,作为usertask2的任务监听器。这个类作为delegateExpression方法创建任务监听器的处理类,和class方式创建的任务监听器差不多,区别是class方式需指定具体类,而delegateExpression方式则是通过流程变量指定:

public class ManagerListener implements TaskListener{

	public void notify(DelegateTask delegateTask) {
		String assignee = delegateTask.getAssignee();
		System.out.println(assignee + "经理有待办需要审批");
	}
}

这里notify实现很简单,就是简单输出某某精力有待办的提示。

下面是我们的客户调用,App.java

public class App {
	
	private ProcessEngine pe;

	public void getFromProcessEngineConfiguration() {
		ProcessEngineConfiguration pec = ProcessEngineConfiguration
				.createProcessEngineConfigurationFromResource("activiti.cfg.xml");
		pe = pec.buildProcessEngine();
	}
	
	public void deploy() {
		RepositoryService repositoryService = pe.getRepositoryService();
		DeploymentBuilder deploymentBuilder = repositoryService.createDeployment();
		
		InputStream inputStream = null;
		try {
			inputStream = App.class.getClassLoader().getResource("bpmn/expressBPM.bpmn").openStream();
			deploymentBuilder.addInputStream("express.bpmn", inputStream);
			deploymentBuilder.name("expressDeployment");
			Deployment deployment = deploymentBuilder.deploy();
			System.out.println("部署完成");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {
		App app = new App();
		app.getFromProcessEngineConfiguration();
		app.deploy();
	}
}

5-9行为初始化流程引擎。11-25行为部署流程文档。这个和以前没什么区别,部署流程文档之后,查看数据库的情况,查看act_re_procdef:

接下来我们添加启动函数并调用之:

public void startProcessByIdWithVars() {
    RuntimeService runtimeService = pe.getRuntimeService();
    Notice notice = new Notice();
    String businessKey = "请假单01";
    Map<String, Object> vars = new HashMap<String, Object>();
    vars.put("employee", "张三");
    vars.put("notice", notice);
    ProcessInstance pi = runtimeService.startProcessInstanceById("expressProcess:1:4", businessKey, vars);
    System.out.println("流程开始");
}

第7-8行把notice作为流程变量,并在启动流程时添加进去。此时控制台输出:

 可以看到流程已经成功调用Notice的callEmployee方法,使用expression创建监听器成功。此时act_re_task表:

 接着定义一个employeeCompleteTask,员工环节提交的方法并调用之:

public void employeeCompleteTask() {
	TaskService taskService = pe.getTaskService();
	TaskListener taskListener = new ManagerListener();
	Map<String, Object> vars = new HashMap<String, Object>();
	vars.put("callManager", taskListener);
	vars.put("manager", "李四");
	taskService.complete("2508", vars);
	System.out.println("完成提交");
}

第5行设置ManagerListener到callManager变量中,第6行把“李四”设置到“manager”变量中。执行后看看控制台输出:

可以看到已经成功触发了任务监听器ManagerListener,通过delegateExpression创建监听器成功。再看此时act_re_task表:

 

 接下来定义经理审批方法manageCompleteTask并调用之:

public void manageCompleteTask() {
	TaskService taskService = pe.getTaskService();
	TaskListener taskListener = new ManagerListener();
	Map<String, Object> vars = new HashMap<String, Object>();
	taskService.complete("5006", vars);
	System.out.println("完成提交");
}

执行该方法,正常来说流程结束。此时看看控制台:

可以看到已经成功触发Notice的callComplete方法。

到目前为止我们已经掌握表达式的基本用法,并把它使用在任务监听器、执行监听器以及设置assignee。在ServiceTask、连线条件上的使用大同小异,请读者自行学习,后面的文章中也会进行使用。

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐