目录
Activiti 概述 与 下载
1、工作流(Workflow):是对工作流程及其各个操作步骤之间业务规则的抽象、概况和描述。
2、工作流模型:将工作流程中的工作如何前后组织在一起的逻辑和规则,在计算机中以恰当的模型表达并对其实施计算。
3、工作流要解决的问题是实现某个业务目标、利用计算机在多个参与者之间按某种预定规则自动传递文档、信息或者任务。
4、主流的工作流框架:Activiti、OSWorkflow、JBoss 的 JBPM、Apache 的 ofBiz 等。生活中有很多的流程管理,比如请假流程、报销流程、审批流程等等。
5、Activiti 历史:JBPM 4 的时候团队内部发生分歧,其中部分开发人员脱离了大部队(JBoss),出来独立写出了 Activiti5;JBoss 则继续 JBPM 的开发,后续又有了 JBPM 5 ...。
6、所以 Activiti 5 基本等同于 JBPM4,2016 年 11 月开源了 Activiti 5.22.0,2017 年 5 月开源了 Activiti 6.0.0,2018 年开始开源 Activiti 7.X。Activiti 7 分为了 Activiti Core 与 Activiti Cloud 两个方向,更倾向于云环境、微服务。
7、Activiti 易于与 Spring、Spring boot、Spring cloud 集成,BPM 全称 Business Process Management - 业务流程管理
8、Activiti 支持主流的数据库(官方文档):
Activiti database type | Example JDBC URL | Notes |
---|---|---|
h2 |
jdbc:h2:tcp://localhost/activiti |
Default configured database |
mysql |
jdbc:mysql://localhost:3306/activiti?autoReconnect=true |
Tested using mysql-connector-java database driver |
oracle |
jdbc:oracle:thin:@localhost:1521:xe |
|
postgres |
jdbc:postgresql://localhost:5432/activiti |
|
db2 |
jdbc:db2://localhost:50000/activiti |
|
mssql |
jdbc:sqlserver://localhost:1433;databaseName=activiti (jdbc.driver=com.microsoft.sqlserver.jdbc.SQLServerDriver) OR jdbc:jtds:sqlserver://localhost:1433/activiti (jdbc.driver=net.sourceforge.jtds.jdbc.Driver) |
Tested using Microsoft JDBC Driver 4.0 (sqljdbc4.jar) and JTDS Driver |
Activiti 官网:https://www.activiti.org
Activiti Github 开源地址:https://github.com/Activiti/Activiti
Activiti v7.X 官方开发手册:https://activiti.gitbook.io/activiti-7-developers-guide/
Activiti v6.X 官方开发手册:https://www.activiti.org/userguide/
Activiti v5.X 官方开始手册:https://www.activiti.org/5.x/userguide/
本文暂时使用 Activiti 6.X 版本,如果需要使用 Activiti 做微服务开发的,则应该使用 Activiti 7.X 版本。
Hello World
1、千里之行,始于足下。学习一个新框架得先有一个 Hello World 压压惊,否则会很迷茫。
2、本文环境:Java JDK 1.8 + IDEA 2018 +Maven 3.1.6 + Activiti 6.0.0 + H2 Database + Spring Boot 2.1.4 进行开发。
1)使用 Maven 管理,不再去 Activiti 官网下载 jar 包,然后导入到项目中
2)暂时使用 H2 内存数据库,让 Activiti 的表和数据先直接在内存数据库中使用。
3、特别提醒:
1)Acticiti 工作流开发,第一步是需要绘制工作流程图(本文开头的那种图),第二步就是编码。
2)下载 Acticiti 6.0.0 开发包或者源码可以看到,里面提供了针对各种数据库的 sql 文件,然后实际中并不需要手动去执行这些 sql 脚本,因为在后台代码中构建流程引擎(ProcessEngine)的时候,它可以自动执行 sql 建表脚本创建好 28 张表
3)Activiti 6.0.0 有 28 张表,使用 Activiti API 操作工作流的时候,Activiti 会自动操作自己的数据库表。所以对于 activiti 的 28 张数据库表,前期并不需要关心,对我们来说是透明的,入门之后可以再去深入了解。
安装流程设计器插件
官网介绍地址:https://www.activiti.org/userguide/#eclipseDesignerInstallation
1、eclipse、sts、idea 都有绘制 activiti 流程图的插件,但官方文档介绍的是 eclipse(sts本质也是eclipse),所以这里也采用 在 sts 上安装流程图插件。(注:在 sts 插件上绘制好流程图后,本人更喜欢 IDEA 编码,所以后续只需要将 sts 中创建好的 .bpmn 文件拷贝过去即可)
1、这里选择在线安装插件 activiti 插件,打开 help -> Install New Software 安装新的软件(插件),在 Install 面板中,点击 Add 按钮添加插件名称与地址:
Name: Activiti BPMN designer (名称可以随意取)
Location: http://activiti.org/designer/update/
2、点击 "Next" 下一步,然后会联网进行下载,之后在 "Install Detail" 面板直接 "Next",然后进入 "Review license" 查看许可证面板,点击第一项 "I accept ..." 同意,最后点击 "Finish" 完成安装。
3、安装过程中可能会弹框提示,选择安装所有即可,然后会提示重启,重启之后可以点击菜单 "Window -> Show View -> Other..." 看到 activiti 插件:
4、设置保存 bpmn 流程图时,同时保存一张同名的 .png 图片:
绘制工作流程图
1、安装好插件之后就可以开始绘制工作流程图了,随便在某个目录上右键,然后 "New -> Other...-> Activiti -> Activiti Diagram" 创建 Activiti 流程图:
2、基本工作流程图绘制并不难,从右侧的选项中拖到到中间即可,开始的黑色圆圈为开始事件,User Task 表示用户任务,带黑色叉的矩形为排它网关(用于条件判断),末尾的粗黑线圆圈为结束事件,箭头线表示顺序流。
3、排它网关用于条件判断,比如同意则继续往后走、不同意则重新返回,在网关上选择箭头按住拖到目标按钮上即可。
4、在空白处点击,为整个流程设置 id 与 name,在每个按钮上点击,为他们设置 id 与 name 值,这些值将来都是 .bpmn 文件中的属性值,代码中会操作它们,它们也会被自动处理到数据库中。
5、为3个用户任务设置 form 表单参数,即此任务执行需要的参数,因为操作比较长,无法全部做成动图,所以只演示添加1个属性。type 表示参数类型,required 表示参数是否必须要填。
6、用户任务设置的参数表示工作流中用户提供的参数,而这些参数需要在排它网关中进行判断,比如主管审批结果输入为 y 时才能继续往后走,否则输入 n 时,则应该重新回到 "填写审批信息"。当排它网关未作任何限制时,则主管和人事无论输入什么,都会继续往后走,显然这不满足我们的需求,则此时应该设置排它网关的条件,它类似 EL 表达式的写法 ${条件判断} ,|| 表示或,&& 表示与,条件判断的值是上一个用户任务中的 form 表单参数。
7、至此工作流程图绘制完毕,第一次可能比较难理解,但是多画几次,然后结合后面的代码,则容易理解。
注意:这个流程图(.bpmn文件)本质就是一个 .xml 文件,可以用记事本、或者浏览器等打开就可以看出来,后面会提供完整的文件,所以即使没画出来,也没关系,并不影响后面的代码运行。
IDEA 编码实现工作流
1、因为本人习惯使用 IDEA,所以在 IDEA 中新建一个 Maven 项目,Java SE 应用,直接在 main 方法中进行简单使用。
2、将 sts 中插件绘制好的流程文件 .bpmn 拷贝到 IDEA 项目的类路径下,当然直接使用 sts 、eclipse 开发也是一样的。
3、pom.xml 文件内容如下。
spring-boot-starter-parent 与 spring-boot-maven-plugin 是方便 maven 项目打包的,可选
slf4j-api 与 slf4j-log4j12 用于做日志框架,可选
activiti-spring-boot-starter-basic:activiti 与 spring boot 集成的启动器,里面包含了 activiti-engine 引擎,以及 activiti 与 spring 的集成,以及 jdbc 。必选,
h2 是内存数据库,必须使用一个数据库,当然可以换成其它数据库。
guava 方便做集合、字符串等操作,可选
junit 方便做单元测试,可选
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>wmx.com</groupId>
<artifactId>activiti-first</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<!--https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
</dependency>
<!--官网地址:https://www.activiti.org/userguide/#_getting_started_2-->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter-basic</artifactId>
<version>6.0.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.h2database/h2 -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.183</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>27.0-jre</version>
</dependency>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
4、只建了一个 MainApp.java 文件操作工作流,编码分为 4 步:1)创建流程引擎 -> 2)部署流程定义文件 -> 3)启动运行流程、4)处理流程任务。内容如下:
package com.activiti.study;
import com.google.common.collect.Maps;
import org.activiti.engine.*;
import org.activiti.engine.form.FormProperty;
import org.activiti.engine.form.TaskFormData;
import org.activiti.engine.impl.form.DateFormType;
import org.activiti.engine.impl.form.StringFormType;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.DeploymentBuilder;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.ParseException;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
public class MainApp {
private static Logger LOGGER = LoggerFactory.getLogger(MainApp.class);
public static void main(String[] args) throws ParseException {
LOGGER.info(" App start...");
//1、创建流程引擎
ProcessEngine processEngine = getProcessEngine();
//2、部署流程定义文件
ProcessDefinition processDefinition = getProcessDefinition(processEngine);
//3、启动运行流程
ProcessInstance processInstance = getProcessInstance(processEngine, processDefinition.getId());
//4、处理流程任务
handlingTasks(processEngine, processInstance);
LOGGER.info(" App quit...");
}
/**
* 处理流程任务
*
* @param processEngine
* @param processInstance
* @throws ParseException
*/
private static void handlingTasks(ProcessEngine processEngine, ProcessInstance processInstance) throws ParseException {
Scanner scanner = new Scanner(System.in);//从控制台手动输入数据
while (processInstance != null && !processInstance.isEnded()) {
TaskService taskService = processEngine.getTaskService();//获取任务服务
List<Task> taskList = taskService.createTaskQuery().list();
for (Task task : taskList) {
LOGGER.info("当前待处理任务id [{}],任务名称 [{}]", task.getId(), task.getName());
Map<String, Object> dataMap = getStringObjectMap(processEngine, scanner, task);
taskService.complete(task.getId(), dataMap);//提交任务/完成任务
//重新获取流程实例
processInstance = processEngine.getRuntimeService()
.createProcessInstanceQuery()
.processInstanceId(processInstance.getId())
.singleResult();
}
}
scanner.close();
}
/**
* 设置当前任务的表单参数。从控制台输入
*
* @param processEngine
* @param scanner
* @param task
* @return
* @throws ParseException
*/
private static Map<String, Object> getStringObjectMap(ProcessEngine processEngine, Scanner scanner, Task task) throws ParseException {
FormService formService = processEngine.getFormService();//表单服务
TaskFormData taskFormData = formService.getTaskFormData(task.getId());//根据任务id获取任务表单数据对象
List<FormProperty> formPropertyList = taskFormData.getFormProperties();//获取表单属性
Map<String, Object> dataMap = Maps.newHashMap();
for (FormProperty formProperty : formPropertyList) {
String line = null;
if (StringFormType.class.isInstance(formProperty.getType())) {
LOGGER.info("请输入 {}", formProperty.getName());
line = scanner.nextLine();
dataMap.put(formProperty.getId(), line);
} else if (DateFormType.class.isInstance(formProperty.getType())) {
LOGGER.info("请输入 {}", formProperty.getName());
line = scanner.nextLine();
dataMap.put(formProperty.getId(), DateUtils.parseDate(line, "yyyy-MM-dd HH:mm:ss"));
} else {
LOGGER.warn("暂时不支持此类型 [{}]", formProperty.getType());
}
LOGGER.info("您成功输入 [{}]", line);
}
return dataMap;
}
/**
* 启动运行流程
*
* @param processEngine
* @param processDefinitionId
*/
private static ProcessInstance getProcessInstance(ProcessEngine processEngine, String processDefinitionId) {
RuntimeService runtimeService = processEngine.getRuntimeService();
ProcessInstance processInstance = runtimeService.startProcessInstanceById(processDefinitionId);
String processDefinitionKey_ = processInstance.getProcessDefinitionKey();
LOGGER.info("启动流程,流程key [{}] ", processDefinitionKey_);//输出:启动流程,流程key [approvalFor2Level]
return processInstance;
}
/**
* 部署流程定义文件
*
* @param processEngine
* @return
*/
private static ProcessDefinition getProcessDefinition(ProcessEngine processEngine) {
RepositoryService repositoryService = processEngine.getRepositoryService();//获取存储服务
DeploymentBuilder deploymentBuilder = repositoryService.createDeployment();//创建部署构建器
//通过类路径下的 .bpmn 文件进行构建。.bpmn 文件的本质其实就是一个定义好的 .xml 文件
deploymentBuilder.addClasspathResource("approvalFor2Level.bpmn");
//部署流程定义文件。此时 Activiti 会自动将 .bpmn 中的数据添加到数据库中,以备后续数据库操作
Deployment deployment = deploymentBuilder.deploy();
String deploymentId = deployment.getId();
String deploymentKey = deployment.getKey();
String deploymentName = deployment.getName();
Date deploymentTime = deployment.getDeploymentTime();
//输出:部署id [1],部署key [null],部署名称 [null],部署时间 [Wed Jul 24 09:40:00 CST 2019]
LOGGER.info("部署id [{}],部署key [{}],部署名称 [{}],部署时间 [{}]", deploymentId, deploymentKey, deploymentName, deploymentTime);
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
.deploymentId(deploymentId)
.singleResult();
String processDefinitionName = processDefinition.getName();
String processDefinitionId = processDefinition.getId();
String processDefinitionKey = processDefinition.getKey();
//输出:流程定义文件名 [二级审批流程],流程id [approvalFor2Level:1:4],流程key [approvalFor2Level]
LOGGER.info("流程定义文件名 [{}],流程id [{}],流程key [{}]", processDefinitionName, processDefinitionId, processDefinitionKey);
return processDefinition;
}
/**
* 创建流程引擎
*
* @return
*/
private static ProcessEngine getProcessEngine() {
//StandaloneInMemProcessEngineConfiguration:标准的基于内存数据库的流程引擎配置.
ProcessEngineConfiguration pecfg = ProcessEngineConfiguration.createStandaloneInMemProcessEngineConfiguration();
//buildProcessEngine:通过流程引擎配置构建流程引擎,此时会自动创建 activiti 的 28 张表
ProcessEngine processEngine = pecfg.buildProcessEngine();
String name = processEngine.getName();
String version = ProcessEngine.VERSION;
LOGGER.info("流程引擎名称 [{}],版本 [{}]", name, version);//输出:流程引擎名称 [default],版本 [6.0.0.4]
return processEngine;
}
}
5、整个项目源码:https://github.com/wangmaoxiong/activitiFirst,核心就是 pom.xml 文件、MainApp.java、approvalFor2Level.bpmn 流程文件、log4j.properties 日志文件。
1)上面的 hello World 要想成功运行,之前绘制好的 .bpmn 文件是必不可少的,因为内容较长,所以不再这里粘贴,从这里获取:https://github.com/wangmaoxiong/activitiFirst/tree/master/src/main/resources
2)其中可以修改 log4j.properties 的日志输出级别为 DEBUG log4j.rootLogger=DEBUG ,console,logFile,就会发现应用启动到结束,它在不停的操作数据库,做 sql 操作。
6、因为使用的是 StandaloneInMemProcessEngineConfiguration 流程引擎配置,它的源码如下,显然它默认使用的是 h2 内存数据库,create-drop 表示应用启动时自动建表,应用关闭时自动删除,数据不会持久化到磁盘。
public class StandaloneInMemProcessEngineConfiguration extends StandaloneProcessEngineConfiguration {
public StandaloneInMemProcessEngineConfiguration() {
this.databaseSchemaUpdate = "create-drop";
this.jdbcUrl = "jdbc:h2:mem:activiti";
}
}
7、启动应用,测试如下:
上面只是测试了正常情况下,全部通过的情景,也可以放弃提交,主管或者人事输入 n 拒绝通过等否则情况都是没问题的
Activiti 核心 API 预览
1、ProcessEngine:流程引擎,这是最重要的 API,通过它可以获取其它的 API。
2、RepositoryService:存储服务,负责对流程定义和部署的管理,比如流程图片以及 xml 文件。
3、RuntimeService:流程运行服务,用于对流程运行过程的控制,如启动流程实例、暂停、挂起、继续等
4、TaskService:流程任务服务,用于对人工任务的管理(如增删改查),设置操作权限。
5、HistoryService:历史记录相关服务接口。
6、FormService:表单服务。
7、IdentityService:身份服务,提供对流程角色数据进行管理的 API,这些角色数据包括用户组、用户及它们之间的关系。
8、ManagementService:提供对流程引擎进行管理和维护的服务。