JAVAWEB开发之工作流详解(二)——Activiti核心API的使用(流程定义和流程实例的管理、流程变量、监听器...)以及与Spring的集成

本文详细介绍了Activiti的核心API用法,涵盖了流程定义的管理,如部署、查看、删除和查询;流程实例的操作,如启动、查询任务、认领和办理;还涉及流程变量的添加和获取,以及监听器、网关和表达式的应用。最后,讨论了Activiti如何与Spring进行集成。
摘要由CSDN通过智能技术生成

管理流程定义

设计流程定义文档


bpmn文件

设置方式可以直接使用插件图形化界面进行设置


为某任务节点指定任务执行者



保存后的BPMN文件可以使用XML编辑器打开
BPMN 2.0根节点是definitions节点。 这个元素中,可以定义多个流程定义(不过我们建议每个文件只包含一个流程定义, 可以简化开发过程中的维护难度)。 一个空的流程定义看起来像下面这样。注意,definitions元素 最少也要包含xmlns 和 targetNamespace的声明。 targetNamespace可以是任意值,它用来对流程实例进行分类。
<?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://activiti.liuxun/test">
  <!-- 流程定义部分 -->  
  <process id="myProcess" name="My First process" isExecutable="true">
    <startEvent id="startevent1" name="Start"></startEvent>
    <userTask id="usertask1" name="员工申请请假" activiti:assignee="员工"></userTask>
    <sequenceFlow id="flow1" sourceRef="startevent1" targetRef="usertask1"></sequenceFlow>
    <userTask id="usertask2" name="部门经理审批" activiti:assignee="经理"></userTask>
    <sequenceFlow id="flow2" name="提交申请" sourceRef="usertask1" targetRef="usertask2"></sequenceFlow>
    <userTask id="usertask3" name="总经理审批" activiti:assignee="总经理"></userTask>
    <sequenceFlow id="flow3" name="经理审批" sourceRef="usertask2" targetRef="usertask3"></sequenceFlow>
    <endEvent id="endevent1" name="End"></endEvent>
    <sequenceFlow id="flow4" sourceRef="usertask3" targetRef="endevent1"></sequenceFlow>
  </process>
 <!-- BPMN绘图规范定义部分(用来描述节点图标的大小和坐标) -->
  <bpmndi:BPMNDiagram id="BPMNDiagram_myProcess">
    <bpmndi:BPMNPlane bpmnElement="myProcess" id="BPMNPlane_myProcess">
      <bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">
        <omgdc:Bounds height="35.0" width="35.0" x="40.0" y="138.0"></omgdc:Bounds>
        ......
    </bpmndi:BPMNPlane>
       ......
  </bpmndi:BPMNDiagram>
</definitions>
说明:流程定义文档由两部分组成
(1) bpmn文件
流程规则文件。在部署后,每次系统启动时都会被解析,把内容封装成流程定义放入项目缓存中。Activiti框架结合XML文件自动管理流程。
(2) 展示流程图的图片
在系统里需要展示流程进展的图片

部署流程定义

部署流程定义也可以认为是增加流程定义
/**
 * 1.部署流程定义
 */
@Test
public void deploy() {
	// 创建流程引擎
	ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
	// 创建部署环境配置对象
	DeploymentBuilder deploymentBuilder = processEngine.getRepositoryService().createDeployment();
	// 部署流程
	
	// 方式一:读取单个的流程定义文件
	Deployment deployment = deploymentBuilder
			.name("测试") //设置部署流程的名称
			.addClasspathResource("activiti/lx/processdefination/MyProcess.bpmn") //设置流程文件
			.addClasspathResource("activiti/lx/processdefination/MyProcess.png") //设置流程文件
			.deploy(); // 部署
	System.out.println("部署ID: "+deployment.getId());
}
说明
(1) 通过getDefaultProcessEngine() 首先获得默认的流程引擎,在创建时会自动加载classpath下的activiti.cfg.xml或activiti-context.xml
(2) 通过流程引擎获取一个RepositoryService对象(仓库服务对象)
(3) 由仓库的服务对象可以获取一个部署环境的配置对象,用来封装部署环境的相关配置。
(4) 部署环境的配置对象(deploymentBuilder) 支持链式编程,在部署配置对象中设置显示名,上传规则文件相对的classpath的地址
(5) 部署,也是往数据库中存储流程定义的过程。
(6) 部署流程定义在数据库中主要操作三张表
a) ACT_RE_DEPLOYMENT 存放流程定义的显示名和部署时间,每部署一次增加一条记录。

b) ACT_RE_PROCDEF 存放流程定义的属性信息,部署每个新的流程定义都会在这张表中插入一条记录。

c) ACT_RE_BYTEARRAY 
存放流程定义相关的部署信息。即流程定义文档的存放地。每部署一次就会增加两条记录,一条是关于bpmn规则文件的,另一条是图片的(如果部署时只指定了bpmn文件,activiti会在部署时解析bpmn文件内容自动生成流程图)。两个文件不是很大,都是以二进制的形式存储在数据库中。

此种方式只适合部署单个的流程,可以以Zip格式进行部署(将所有的流程规则文件与图片压缩成ZIP格式文件)
/**
 * 1.部署流程定义(部署zip格式文件)
 */
@Test
public void deploy2() {
	// 创建流程引擎
	ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
	// 创建部署环境配置对象
	DeploymentBuilder deploymentBuilder = processEngine.getRepositoryService().createDeployment();
	// 部署流程
	
	ZipInputStream zipInputStream = new ZipInputStream(this.getClass().getClassLoader().getResourceAsStream("activiti/lx/processdefination/process.zip"));
	// 方式二:读取zip压缩文件
	Deployment deployment = deploymentBuilder
			.name("测试zip部署") //设置部署流程的名称
			.addZipInputStream(zipInputStream )
			.deploy();
	System.out.println("部署ID: "+deployment.getId());
}

查看流程定义

/**
 * 2. 查看流程规则的信息(流程定义)
 */
@Test
public void view() {
	// 创建流程引擎对象
	ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
	// 获取流程定义信息
	RepositoryService repositoryService = processEngine.getRepositoryService();
	ProcessDefinitionQuery definitionQuery = repositoryService.createProcessDefinitionQuery();
	// 添加过滤条件
	// definitionQuery.processDefinitionName("");
	 definitionQuery.processDefinitionKey("myProcess");
	// 添加分页条件
	// definitionQuery.listPage(0, 10);
	// 添加排序条件
	// definitionQuery.orderByProcessDefinitionId();
	
	List<ProcessDefinition> list = definitionQuery.list();
	// 迭代效果查看流程定义
	for (ProcessDefinition pd : list) {
		System.out.print("id= "+pd.getId()+", ");
		System.out.print("name= "+pd.getName()+", ");
		System.out.print("key= "+pd.getKey()+", ");
		System.out.print("version= "+pd.getVersion()+", ");
		System.out.println("deploymentId= "+pd.getDeploymentId()+", ");
	}
	
}
结果:

再部署一次的运行结果为:

说明:
(1) 首先获取流程引擎对象
(2) 然后通过流程引擎对象获取RepositoryService
(3)通过RepositoryService实例可以获取查询对象实例
(4) 根据查询实例可以设置过滤参数,设置查询以及排序条件,获得符合条件的流程定义列表。
(5)由运行结果可以看出:
a) Key和Name的值为:bpmn文件process节点的id和name的属性值
  <process id="myProcess" name="My First process" isExecutable="true">
b) key属性被用来区别不同的流程定义。
c) 带有特定key的流程定义第一次部署时,version为1。之后每次部署都会在当前最高版本号上加1
d) Id的值的生成规则为:{processDefinitionKey}:{processDefinitionVersion}:{generated-id}, 这里的generated-id是一个自动生成的唯一的数字
e) 重复部署一次,deploymentId的值以一定的形式变化
f) 流程定义(ProcessDefinition)在数据库中没有相应的表对应,只是从act_ge_bytearray表中取出相应的bpmn和png图片,并进行解析。

删除流程定义

删除部署到activiti中的流程定义
/**
 *3.删除流程定义
 */
@Test
public void  delDeploy() {
	// 创建流程引擎对象
	ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
	// 设置要删除的部署的ID
	String deploymentId = "101";
	// 删除指定的部署信息,如果有关联信息则报错(例如启动了流程定义相关的流程实例)
	// processEngine.getRepositoryService().deleteDeployment(deploymentId );
	// 删除指定的部署信息,如果有关联信息则级联删除
	// 第二个参数cascade,代表是否级联删除
	processEngine.getRepositoryService().deleteDeployment(deploymentId, true);
}
说明:
(1) 因为删除的是流程定义,而流程定义的部署是属于仓库服务的,所以应该首先获取RespositoryService
(2) 如果该流程定义在没有正在运行的情况下,可以使用普通的删除方式。如果有关联的信息,可以使用第二种级联强制删除(删除所有关联的信息)。由于级联删除涉及的数据比较多,一般只开放给超级管理员使用。

获取流程定义文档的资源

查询流程定义文档。主要查看的是图片,用于显示流程使用。
/**
 * 4.获取流程定义中的资源文件(查看流程图片)
 * 
 * @throws IOException
 */
@Test
public void getResource() throws IOException {
	ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
	String deploymentId = "1";
	// 获取指定ID流程定义下的所有资源文件的名称列表
	List<String> names = processEngine.getRepositoryService().getDeploymentResourceNames(deploymentId);
	String resourceName = null;
	// 遍历资源文件名称列表
	for (String string : names) {
		// 获取'.png'结尾名称为流程图片名称
		if (string.endsWith(".png")) {
			resourceName = string;
		}
	}
	// 如果流程图片存在
	if (resourceName != null) {
		InputStream in = processEngine.getRepositoryService().getResourceAsStream(deploymentId, resourceName);
		// 指定拷贝目录
		File file = new File("/Users/liuxun/Downloads/" + resourceName);
		// 原始方式
		// OutputStream out = new FileOutputStream(file);
		// byte[] b = new byte[1024];
		// int len = 0;
		// while((len=in.read(b))!=-1) {
		// out.write(b, 0, len);
		// }
		// out.close();
		// 使用FileUtils文件操作工具类,将流程图片拷贝到指定目录下
		FileUtils.copyInputStreamToFile(in, file);
	}

}
查看ACT_GE_BYTERRAY

运行结果:

1)deploymentId为流程部署ID
2)resourceName为act_ge_bytearray表中NAME_列的值
3)使用repositoryService的getDeploymentResourceNames方法可以获取指定部署下得所有文件的名称
4)使用repositoryService的getResourceAsStream方法传入部署ID和文件名称可以获取部署下指定名称文件的输入流
5)最后的有关IO流的操作,使用FileUtils工具的copyInputStreamToFile方法完成流程流程到文件的拷贝

查询最新版本的流程定义

/**
 * 获取最新版本的流程定义
 */
@Test
public void getLatestVersion() {
	ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
	ProcessDefinitionQuery query = processEngine.getRepositoryService().createProcessDefinitionQuery();
	query.orderByProcessDefinitionId().asc();
	List<ProcessDefinition> list = query.list();
	HashMap<String, ProcessDefinition> map = new HashMap<String, ProcessDefinition>();
	for (ProcessDefinition pd : list) {
		map.put(pd.getKey(), pd);
	}
	ArrayList<ProcessDefinition> lastList = new ArrayList<>(map.values());
	for (ProcessDefinition processDefinition : lastList) {
		System.out.println(processDefinition.getName()+" "+processDefinition.getVersion());
	}
}

流程实例管理

启动流程实例

在完成流程定义部署后,就可以启动流程实例了
// 启动流程实例
@Test
public void startProcess() {
	// 创建流程引擎
	ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
	// 获取流程引擎服务对象
	RuntimeService runtimeService = processEngine.getRuntimeService();
	// 启动流程,返回流程实例对象
	ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("myProcess");
	// 显示相关信息
	System.out.print("pid= "+processInstance.getId()); //流程实例ID
    System.out.println("activityId= "+processInstance.getActivityId());		
}
说明:
(1) 结果为:
(2) 操作数据库的ACT_RU_EXECUTION表,如果是用户任务节点,同时也会在ACT_RU_TASK中添加一条记录
注意:按照key启动流程 会默认根据最新版本的流程定义规则创建流程实例
ACT_RU_EXECUTION

ACT_RU_TASK

查询任务

在activiti任务中,主要分为两大类:
1.确切指定了办理者的任务,这个任务将成为指定者的私有任务
2.无法指定具体的某一个人来办理的任务,可以把任务分配给几个人或者一到多个小组,让这个范围内的用户可以选择性(如有空余时间时)来办理这类任务。

查询指定用户的代办任务

对指定用户的未完成的个人任务执行查询(由某一个人负责办理 在任务列表中通过assignee字段指定)
/**
 * 2.1 查看指定用户的代办任务
 */
@Test
public void findUnfinishedTask() {
	ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
	// 获取个人的代办信息
	TaskService taskService = processEngine.getTaskService();
	// 创建流程任务查询对象
	TaskQuery taskQuery = taskService.createTaskQuery();
	String assignee = "员工";
	// 添加过滤条件
	taskQuery.taskAssignee(assignee);
	// 添加分页条件
	taskQuery.listPage(0, 10);
	// 添加过滤条件
	taskQuery.orderByTaskCreateTime().desc();
	// 执行查询
	List<Task> list = taskQuery.list();
	System.out.println("===============【"+assignee+"】的个人任务列表===============");
	// 迭代结果,查看个人任务
	for (Task task : list) {
		System.out.print("id="+task.getId()+","); //获取任务的ID
		System.out.print("name="+task.getName()+",");//获取任务的名称
		System.out.print("assign="+task.getAssignee()+",");//查询任务的代办人
		System.out.print("createTime="+task.getCreateTime()+",");//查询任务的创建时间
		System.out.println("executionId="+task.getExecutionId());//获取流程执行对象的ID
	}
}
运行结果:

说明:
1)因为是任务查询,所以从processEngine中应该得到TaskService
2)使用TaskService获取到任务查询对象TaskQuery
3)为查询对象添加查询过滤条件,使用taskAssignee指定任务的候选者(即查询指定用户的代办任务),添加分页排序等过滤条件
4)调用list方法执行查询,返回办理者为指定用户的任务列表
5)任务ID、名称、办理人、创建时间可以从act_ru_task表中查到。
6)Execution与ProcessInstance的关系与分支有关。在这种单线流程中,ProcessInstance相当于Execution
7)如果assignee属性为部门经理,结果为空。因为现在流程只到了”填写请假申请”阶段,后面的任务还没有执行,即在数据库中没有部门经理可以办理的任务,所以查询不到。
8)一个Task节点和Execution节点是1对1的情况,在task对象中使用Execution_来标示他们之间的关系
9)任务ID在数据库表act_ru_task中对应“ID_”列

查询指定用户的可接任务(公共任务)

对指定用户的可接收的公共任务执行查询
/**
 * 2.2 查看指定用户的可接任务(公共任务)
 */
@Test
public void findCanTakeTask() {
	ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
	// 获取个人的代办信息
	TaskService taskService = processEngine.getTaskService();
	// 创建流程任务查询对象
	TaskQuery taskQuery = taskService.createTaskQuery();
	String candidateUser = "员工1";
	// 添加过滤条件
	taskQuery.taskCandidateUser(candidateUser );
	// 添加分页条件
	taskQuery.listPage(0, 10);
	// 添加过滤条件
	taskQuery.orderByTaskCreateTime().desc();
	// 执行查询
	List<Task> list 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值