Spring Boot 整合 工作流引擎:Activiti 7 入门
基本概念
工作流
工作流(Workflow),就是通过计算机对业务流程自动化执行管理。它主要解决的是“使在多个参与者之间按照某种预定义的规则自动进行传递文档、信息或任务的过程,从而实现某个预期的业务目标,或者促使此目标的实现”。
工作流系统
一个软件系统中具有工作流的功能,我们把它称为工作流系统,一个系统中工作流的功能是什 么?就是对系统的业务流程进行自动化管理,所以工作流是建立在业务流程的基础上,所以一个软件的系统核心根本上还是系统的业务流程,工作流只是协助进行业务流程管理。即使没有工作流业务系统也可以开发运行,只不过有了工作流可以更好的管理业务流程,提高系统的可扩展性。
Activiti介绍
Alfresco 软件在 2010 年 5 月 17 日宣布 Activiti
业务流程管理(BPM
)开源项目的正式启动,其首席架构师由业务流程管理 BPM 的专家 Tom Baeyens 担任,Tom Baeyens 就是原来 jbpm
的架构师,而 jbpm
是一个非常有名的工作流引擎,当然 activiti
也是一个工作流引擎。 Activiti
是一个工作流引擎, activiti
可以将业务系统中复杂的业务流程抽取出来,使用专门的建模语言(BPMN2.0
)进行定义,业务系统按照预先定义的流程进行执行,实现了业务系统的业务流程由 activiti
进行管理,减少业务系统由于流程变更进行系统升级改造的工作量,从而提高系统的健壮性,同时也减少了系统开发维护成本。
Activiti如何使用
Activiti
是一个工作流引擎(其实就是一堆 jar 包 API),业务系统使用 activiti
来对系统的业务流程进行自动化管理,为了方便业务系统访问(操作)activiti
的接口或功能,通常将 activiti
环境与业务系统的环境集成在一起。
- 使用
activiti
流程建模工具(activity-designer)定义业务流程(.bpmn
文件) 。.bpmn
文件就是业务流程定义文件,通过 xml
定义业务流程。
- 向
activiti
部署业务流程定义(.bpmn
文件)。使用 activiti
提供的 api 向 activiti
中部署.bpmn
文件(一般情况还需要一块儿部署业务流程的图片.png)
- 启动一个流程实例表示开始一次业务流程的运行,比如员工请假流程部署完成,如果张三要请 假就可以启动一个流程实例,如果李四要请假也启动一个流程实例,两个流程的执行互相不影响,就好比定义一个
java
类,实例化两个对象一样,部署的流程就好比 java 类,启动一个流程实例就好比 new 一个 java
对象。
- 因为现在系统的业务流程已经交给
activiti
管理,通过 activiti
就可以查询当前流程执行到哪了,当前用户需要办理什么任务了,这些 activiti
帮我们管理了,而不像上边需要我们在 sql
语句中的where
条件中指定当前查询的状态值是多少。
- 用户查询待办任务后,就可以办理某个任务,如果这个任务办理完成还需要其它用户办理,比如采购单创建后由部门经理审核,这个过程也是由
activiti
帮我们完成了,不需要我们在代码中硬编码指定下一个任务办理人了。
- 当任务办理完成没有下一个任务/结点了,这个流程实例就完成了。
首次运行创建数据表
准备工作
Mysql
数据库,用于存放activiti
生成的数据表
- 流程图绘制工具,这里使用
IDEA
中的actBPM
插件(在IDEA
中的plugins
中搜索actBPM
安装即可)
注意:本文所使用的是环境版本为: Spring Boot 2.x
+ Activiti 7
创建数据表
Activiti
相关的数据表,由框架自动创建,而我们需要做的只是配置好Activiti
。
引入依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
| <?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>
<groupId>com.imxushuai</groupId> <artifactId>activiti-demo-1</artifactId> <version>1.0-SNAPSHOT</version>
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.2.RELEASE</version> <relativePath/> </parent>
<dependencies> <dependency> <groupId>org.activiti</groupId> <artifactId>activiti-spring-boot-starter</artifactId> <version>7.0.56</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
<dependencyManagement> <dependencies> <dependency> <groupId>org.activiti</groupId> <artifactId>activiti-dependencies</artifactId> <version>7.0.56</version> <scope>import</scope> <type>pom</type> </dependency> </dependencies> </dependencyManagement>
<repositories> <repository> <id>alfresco</id> <name>Activiti Releases</name> <url>https://artifacts.alfresco.com/nexus/content/repositories/activiti-releases/</url> <releases> <enabled>true</enabled> </releases> </repository> </repositories>
</project>
|
注意事项:
- 这里使用的是版本号不是官方提供的公开的releases版本,而是直接从
Activiti
私服的版本号
- 如果使用非
Bete
版本,需要JDK 11
- 如果使用
h2
数据库的话,不需要jdbc
依赖,但是如果使用其他数据库,需要引入数据源依赖。
application.yml
1 2 3 4 5 6 7 8 9 10 11 12
| spring: activiti: database-schema-update: true history-level: full db-history-used: true copy-variables-to-local-for-tasks: false datasource: url: jdbc:mysql:///activiti?characterEncoding=utf-8&serverTimezone=GMT username: root password: 123456 driver-class-name: com.mysql.jdbc.Driver
|
注意事项:
- spring.activiti.database-schema-update:此项可以不配置,默认为:true
- spring.activiti.history-level:保存历史数据级别设置为full最高级别,建议为:full,否则生成的表格不全
- spring.activiti.db-history-used:检查历史表是否存在,建议为:true
启动类(省略)
普通的Spring Boot
启动类即可。
启动测试
查看数据库,已有25张表生成
数据表命名规则
Activiti 的表都以 ACT_开头。 第二部分是表示表的用途的两个字母标识。 用途也和服务的 API 对应。
ACT_RE_*: ‘RE’表示 repository。 这个前缀的表包含了流程定义和流程静态资源 (图片,规则,等等)。
ACT_RU_*:’RU’表示 runtime。 这些运行时的表,包含流程实例,任务,变量,异步任务,等运行中的数据。 Activiti 只在流程实例执行过程中保存这些数据, 在流程结束时就会删除这些记录。 这样运行时表可以一直很小速度很快。
ACT_HI_*:’HI’表示 history。 这些表包含历史数据,比如历史流程实例, 变量,任务等等。
ACT_GE_*:GE 表示 general。通用数据, 用于不同场景下。
防坑指南
问题一:运行报错
解决方法:
查看是否引入数据源依赖引入spring-boot-starter-jdbc
依赖
1 2 3 4
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency>
|
问题二:创建的表只有17张
解决方法:
修改配置文件:application.yml
1 2 3 4 5 6 7
| spring: activiti: database-schema-update: true history-level: full db-history-used: true copy-variables-to-local-for-tasks: false
|
入门示例
主要API类:Service
Service 是工作流引擎提供用于进行工作流部署、执行、管理的服务接口。
常用Service介绍:
- RepositoryService:
activiti
的资源管理类
- RuntimeService :
activiti
的流程运行管理类
- TaskService :
activiti
的任务管理类
- HistoryService:
activiti
的历史管理类
- ManagerService:
activiti
的引擎管理类
绘制流程图
绘制一个简单的流程图
创建流程实例
编写Test
类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| package com.imxushuai;
import lombok.extern.slf4j.Slf4j; import org.activiti.engine.*; import org.activiti.engine.runtime.ProcessInstance; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner;
import java.util.HashMap;
@Slf4j @RunWith(SpringRunner.class) @SpringBootTest public class ActivitiTest {
@Autowired private ProcessEngine processEngine;
private RuntimeService runtimeService; private TaskService taskService; private RepositoryService repositoryService; private HistoryService historyService;
@Before public void init() { runtimeService = processEngine.getRuntimeService(); taskService = processEngine.getTaskService(); repositoryService = processEngine.getRepositoryService(); historyService = processEngine.getHistoryService(); }
@Test public void start() { ProcessInstance instance = runtimeService.startProcessInstanceByKey("qingjia", new HashMap<>()); log.info("流程实例初始化成功:[{}]", instance.getId()); }
}
|
运行结果:
填写请假单
编写测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13
|
@Test public void apply1() { Task task = taskService.createTaskQuery().processInstanceId("e0e3e681-c80e-11e9-bf76-005056c00001").singleResult(); Map<String, Object> params = new HashMap<>(); params.put("dayNum", 1); params.put("remark", "我妈叫我回家吃饭"); taskService.complete(task.getId(), params); }
|
运行结果,查看运行变量表(act_ru_variable):
流程变量已经记录成功
审批流程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
@Test public void apply2() { Task task = taskService.createTaskQuery().processInstanceId("e0e3e681-c80e-11e9-bf76-005056c00001").singleResult(); Map<String, Object> params = taskService.getVariables(task.getId()); log.info(params.toString()); taskService.complete(task.getId());
params = new HashMap<>(); params.put("remark", "批准,注意安全"); taskService.complete(task.getId(), params); }
|
运行结果:
成功打印出请假单的参数
查看数据表是否成功审批完成
可以看到全部流程已经执行完毕
代码获取
Github: