Activiti工作流特点的其中之一就是原生支持Spring。前一章节我们介绍了Activit工作流的基本概念,让大家对工作流知识有了一个大体的印象,这一节我们说下在项目中的使用。
1、Spring集成
首先在Maven中将Spring集成包引入进来。
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring</artifactId>
<version>5.22.0<</version>
</dependency>
可以把流程引擎(ProcessEngine)作为一个普通的Springbean进行配置。类org.activiti.spring.ProcessEngineFactoryBean是集成的切入点,这个bean需要一个流程引擎配置来创建流程引擎。
<!-- activiti config -->
<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
<property name="databaseType" value="oracle" />
<property name="dataSource" ref="dataSource" />
<property name="transactionManager" ref="transactionManager" />
<property name="databaseSchemaUpdate" value="true" />
</bean>
<bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
<property name="processEngineConfiguration" ref="processEngineConfiguration" />
</bean>
<bean id="repositoryService" factory-bean="processEngine" factory-method="getRepositoryService" />
<bean id="runtimeService" factory-bean="processEngine" factory-method="getRuntimeService" />
<bean id="taskService" factory-bean="processEngine" factory-method="getTaskService" />
<bean id="historyService" factory-bean="processEngine" factory-method="getHistoryService" />
<bean id="managementService" factory-bean="processEngine" factory-method="getManagementService" />z
其中datasource
是指项目中配置的数据源,transactionManager
是指项目中配置的事务管理器配置。如下:
<!-- 数据源配置 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<!-- 基本属性 url、user、password -->
<property name="driverClassName" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<!-- 配置初始化大小、最小、最大 -->
<property name="initialSize" value="${jdbc.pool.initialSize}" />
<property name="minIdle" value="${jdbc.pool.minIdle}" />
<property name="maxActive" value="${jdbc.pool.maxActive}" />
<!-- 配置获取连接等待超时的时间 -->
<property name="maxWait" value="60000" />
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="30000" />
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="300000" />
<property name="validationQuery" value="SELECT 'x' from dual" />
<property name="testWhileIdle" value="true" />
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" />
<!-- 打开PSCache,并且指定每个连接上PSCache的大小(Oracle使用) -->
<property name="poolPreparedStatements" value="true" />
<property name="maxPoolPreparedStatementPerConnectionSize"value="20" />
<!-- 配置监控统计拦截的filters,去掉后监控界面sql无法统计 -->
<property name="filters" value="stat" />
</bean>
<!-- 事务管理器配置,单数据源事务 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
至此,集成成功,像使用一般bean对象的方式使用Activit对象即可。
2、基础API介绍
项目里用到的基本都是顺序流,没有用到网关,所以在这里不会介绍网关的东西,只介绍项目用到相关知识。
2.1 七项基本操作
- 设计流程图(各种组件,如连线、用户任务、网关)
- 流程定义增删改查
- 流程变量增删改查
- 启动流程定义
- 任务增删改查
- 完成任务
- 历史信息查询
上面列举了Activiti中用到的七项操作种类,每一种操作都有着对应的API,需要用到哪些API请查看官网文档,或者Google,这里不再赘述。
2.2 常用操作
2.2.1 使用Eclipse开始流程设计
Eclipse插件安装请参照Activiti工作流之基础概念介绍
鼠标选中要新建流程图的文件夹,点击右键,选择new->others->Activiti Diagram,即可新建一个图表。记得要把Eclipse切换到Activit视图
,一个简单的流程图类似下图,
只需要关注红框里的元件就可以了,如果复杂的就需要用到其它元件了,比如网关。可以自行拖拽试试,网上这部分操作知识很多,这里不再赘述。
2.2.1 工作流部署
其中recourceClassPath
表示工作流相对项目classpath下的路径,例如maven项目中src/main/resource
下的”bpmn/HelloWorldProcess.bpmn“。resourceName
表示你要部署工作流的名称,这个名称一般用来说明工作流的作用,比如“请假流程“。
public Deployment deployProcess(String resourceClassPath, String resourceName) {
Deployment deployMent = repositoryService.createDeployment().name(resourceName)
.addClasspathResource(resourceClassPath).deploy();
return deployMent;
}
每重复部署一次,act_re_deployment表中新增加的记录的ID值都会随之增大,相应流程启动的时候取得是最新的值,也就是最大的值。如果想避免重复部署,可以使用下面的方式:
public Deployment deployProcess(String resourceClassPath, String resourceName) {
//过滤关键是为流程部署起一个名字`fileName`,没有名字不会过滤
Deployment deployMent = repositoryService.createDeployment().enableDuplicateFiltering().name(fileName)
.name(resourceName).addClasspathResource(resourceClassPath).deploy();
return deployMent;
}
2.2.2 流程启动
其中procDefkey
表示流程定义key, businessKey
表示业务key, processVariables
表示流程执行过程中需要的变量。
public ProcessInstance startProcessInstance(String procDefKey, String businessKey,
Map<String, Object> processVariables) {
// 根据流程定义KEY查询最新版本的流程定义
ProcessDefinitionEntity procDef = (ProcessDefinitionEntity) getProcessDefinitionByKey(procDefKey);
if (procDef == null) {
throw new RuntimeException("流程定义KEY为" + procDefKey + "流程定义未找到,请重新发布");
}
return runtimeService.startProcessInstanceByKey(procDefKey, businessKey, processVariables);
}
2.2.3 任务完成
其中taskId
表示要完成的任务ID,variables
表示流程变量
private void commitProcess(String taskId, Map<String, Object> variables) {
if (variables == null) {
variables = new HashMap<String, Object>();
}
taskService.complete(taskId, variables);
}
2.2.4 流程跳转
这里只说明主要算法,其中taskId
表示任务ID,activityId
表示待跳转的“活动节点Id”
private void commitProcess(String taskId, String activityId) {
// 当前节点
ActivityImpl currActivity = findActivitiImpl(taskId, null);
// 清空当前流向
List<PvmTransition> oriPvmTransitionList = clearTransition(currActivity);
// 创建新流向
TransitionImpl newTransition = currActivity.createOutgoingTransition();
// 目标节点
ActivityImpl pointActivity = findActivitiImpl(taskId, activityId);
// 设置新流向的目标节点
newTransition.setDestination(pointActivity);
// 提交任务
taskService.complete(taskId, variables);
// 删除目标节点新流入
pointActivity.getIncomingTransitions().remove(newTransition);
// 还原以前流向
restoreTransition(currActivity, oriPvmTransitionList);
}
2.2.4 流程终止
其中procId
表示流程实例ID
public void terminateByBillCode(String procId) {
try {
// 终止流程
runtimeService.deleteProcessInstance(procId, "删除原因");
} catch (Throwable e) {
LOG.error("终止流程By业务单号-controller" + e.getMessage(), e);
}
}
2.2.5 工作流删除
其中deploymentId
表示工作流部署ID,这个方法一般很少使用,新工作流启动时会自动选择最新部署的工作流。如果工作流需要更新,只需要重新部署工作就即可。
public void deleteDeployment(String deploymentId) throws Exception {
RepositoryService repositoryService = processEngine.getRepositoryService();
// 普通删除,如果当前规则下有正在执行的流程,则抛出异常
//repositoryService.deleteDeployment(deploymentId);
// 级联删除,会删除和当前规则相关的所有信息,正在执行的信息,也包括历史信息
repositoryService.deleteDeployment(deploymentId,true);
}
2.3 集成自己的用户管理系统
Activiti自带的用户权限管理系统功能不够强大,而一般业务系统都有自己的用户管理平台,你可以选择以某种方式来同步两者之间的用户信息,也可以直接使用业务系统的用户管理平台作为工作流权限管理。
下面说下思路:
1. 新建一个中间表比如ACT_PROC_CONFIG,字段包括流程key/角色ID/组织ID/用户ID。
2. 提供一个页面用于设置对每个工作流进行设置,分别将各环节的任务分配给某个部门中的某个角色的某些人员。这样就将自己的用户管理平台和Activiti工作流有效集成起来了。
3. 针对工作流编写业务代码的时候,将ACT_PROC_CONFIG表融进来,比如判断某个用户是否有当前任务的处理权限,下一环节任务有哪些人员可以处理等。
2.4 流程定制
一般情况下,工作流部署以后流程就是固定的。具体每个用户任务可以根据业务需要提前设置好相应的部门处理人员,这样当执行到某个用户任务的时候,自然会有满足条件的人员可以处理。但是也有特殊情况,比如走到某个用户任务的时候,要根据当前的实际工作来判断后面的流程还需不需要走,还需要哪些流程处理,哪些流程可以跳过,哪些流程我想指定某个人来处理,这就牵扯到流程定制的问题。而这些功能基本上都是需要结合流程变量
自己编码实现的。需要根据实际的业务自行设计,主要就是用到了流程变量
和流程跳转
功能,业务不同设计也不同,这里也不再讲述,只是说明一种应用场景。
2.5、资源的自动部署
Spring的集成也有一个专门用于对资源部署的特性。在流程引擎的配置中,你可以指定一组资源。当流程引擎被创建的时候, 所有在这里的资源都将会被自动扫描与部署。在这里有过滤以防止资源重新部署,只有当这个资源真正发生改变的时候,它才会向Activiti使用的数据库创建新的部署。 这对于很多用例来说,当Spring容器经常重启的情况下(例如 测试),使用它是非常不错的选择。
这里有一个例子:
<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
...
<property name="deploymentResources" value="classpath*:/activiti/*.bpmn" />
</bean>
<bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
<property name="processEngineConfiguration" ref="processEngineConfiguration" />
</bean>
默认,上面的配置会把所有匹配的资源发布到Activiti引擎的一个单独发布包下。用来检测防止未修改资源重复发布的机制会作用到整个发布包中。 有时候,这可能不是你想要的。比如,如果你发布了很多流程资源,但是只修改里其中某一个单独的流程定义, 整个发布包都会被认为变更了,导致整个发布包下的所有流程定义都会被重新发布, 结果就是每个流程定义都生成了新版本,虽然其中只有一个流程发生了改变。
为了定制发布方式,你可以为SpringProcessEngineConfiguration指定一个额外的参数deploymentMode。 这个参数指定了匹配多个资源时的发布处理方式。默认下这个参数支持设置三个值:
- default: 把所有资源放在一个单独的发布包中,对这个发布包进行重复检测。 这是默认值,如果你没有指定参数值,就会使用它。
- single-resource: 为每个单独的资源创建一个发布包,并对这些发布包进行重复检测。 你可以单独发布每个流程定义,并在修改流程定义后只创建一个新的流程定义版本。
- resource-parent-folder: 把放在同一个上级目录下的资源发布在一个单独的发布包中,并对发布包进行重复检测。 当需要多资源需要创建发布包,但是需要根据共同的文件夹来组合一些资源时,可以使用它。
<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
...
<property name="deploymentResources" value="classpath*:/activiti/*.bpmn" />
<property name="deploymentMode" value="single-resource" />
</bean>
项目中虽然没用到工作流的网关等功能,但是因为项目中的流程定制业务较多,加上实际使用场景要考虑很多问题,实际使用时还是蛮复杂的。这还需要在实际项目中历练,才能更加清晰掌握工作流的使用。但是如果只是想要简单的入门,找几个例子学习下就可以了。