一、Activiti 简介
Activiti 是一个基于 Java 的、轻量级的开源工作流和业务流程管理 (BPM, Business Process Management) 引擎。它最初由 Alfresco 公司发起,遵循 Apache 2.0 开源协议,并且符合 BPMN 2.0 标准。后来由于团队和商业化等原因,Activiti 项目分裂出了 Flowable 等分支版本,但核心理念与使用方式大同小异。
在各类 BPM 产品中,Activiti 因其轻量、易于与 Spring 等主流框架集成而备受青睐。它提供了一个基于 BPMN2.0 的建模与执行平台,便于对复杂的业务流程进行可视化设计、自动化执行与监控。
二、Activiti 相关定义与主要概念
下面是 Activiti(以及大多数 BPMN 流程引擎)中需要重点理解的几个概念:
-
Process Definition(流程定义)
- 指的是业务流程在 BPMN 中进行建模后的“图”或“流程模型”。该模型描述了业务处理的各个环节(用户任务、服务任务、网关、事件等),以及它们之间的流转逻辑。
- 在实际运行时,会将“流程定义”部署到 Activiti 引擎中,并以相应版本进行管理。
-
BPMN 2.0(Business Process Model and Notation)
- BPMN 2.0 是业务流程建模的标准语言,它定义了一套图形化的符号(如网关、任务、边界事件等)来描述业务流程。
- 通过 BPMN,可以直观地看到流程的执行顺序、分支条件和并行流程等。
-
Process Instance(流程实例)
- 流程定义是模型层面的抽象,而流程实例是该模型每一次真正运行的实体。例如“请假申请流程”是一个流程定义,小王的请假单在运行时就对应该流程定义的一次实例。
- Activiti 根据流程定义来创建并运行流程实例,对应着真实业务数据与当前流程进展。
-
Task(任务)
- BPMN 中常见的“用户任务”、“服务任务”等就是“任务”的具体类型。Activiti 会在引擎中将这些任务进行调度或分配,并在流程流转中把任务指派给具体的人或系统处理。
- “用户任务”需要由人工来完成,可以分配给某个用户或用户组;“服务任务”则是由系统自动执行,如调用一个外部接口或服务。
-
Gateway(网关)
- 流程分支或合并的控制元素。BPMN 中常见的网关有:排他网关 (Exclusive Gateway)、并行网关 (Parallel Gateway)、包容网关 (Inclusive Gateway) 等。
- 通过网关,可以根据业务逻辑和条件控制流程的分支流转或并行分支。
-
Event(事件)
- 事件通常用来表示流程的开始、结束、中断或其他时机。常见的事件类型有:开始事件 (Start Event)、结束事件 (End Event)、边界事件 (Boundary Event) 等。
-
部署(Deployment)
- 将 BPMN 模型(流程定义文件)导入到 Activiti 引擎并进行版本化管理的过程。部署后,Activiti 才能将该流程定义变成可执行的流程模型。
-
引擎接口与 API
- Activiti 提供了丰富的 API 用于启动流程、查询任务、完成任务、跳转流程节点等常见操作。
- 常见接口包括
RuntimeService
,TaskService
,RepositoryService
,HistoryService
等。
三、典型应用场景
-
审批流程
- 请假、报销、采购、用印等常见的OA审批流程。通常存在串行或并行审批环节,甚至需要会签、加签、撤销等功能。
- 通过 Activiti 定义审批流程后,业务系统可以根据当前流程状态自动识别待办任务列表、审批节点、审批历史等。
-
订单处理、售后流程
- 电商系统中,有关订单处理、退货、换货流程等。不同条件下会有不同的审批或自动化处理分支。
- 通过 BPMN,能将业务场景可视化,更直观地对复杂分支进行维护。
-
多人协作、跨部门流程
- 当业务需要多个角色或部门协同完成时,经常存在各种分支、条件、并行处理,对简单的状态机来说难以管理,采用工作流引擎可以灵活定义与扩展。
-
复杂的服务编排、系统间交互
- 在企业级微服务架构中,需要多个服务之间有序或并行的处理工作,Activiti 可以作为“编排者”进行流程控制和状态跟踪。
四、为什么不用简单的 status 字段来处理,而要使用工作流引擎?
在许多业务场景下,若流程比较简单,确实可能只需要一个状态字段(如 Pending -> Approved -> Rejected -> Completed
)就能满足需求。但当流程变复杂时,简单的状态机往往力不从心。主要原因包括:
-
状态种类多、流转逻辑复杂
- 复杂业务中可能有多种分支、并行、条件校验、异常处理等场景,单纯的状态字段没法轻易表示这些逻辑,更别提动态修改或拓展流程了。
- 例如一个审批流程里,用户可能需要临时加签、会签、撤回、跳转到前一步等需求,这都需要在简单的状态模型里做大量“手工维护”。
-
难以维护与扩展
- 复杂流程一旦需要修改,往往要改代码中大量的 if-else 或 switch 分支逻辑,错误或遗漏的概率也会大大增加。
- 而工作流引擎可以让流程逻辑从业务代码中解耦,通过 BPMN 流程图来建模并使用引擎执行,维护和改动相对更直观、更安全。
-
可视化与监控
- 工作流引擎通常提供可视化的流程设计器以及监控工具。运营人员、业务人员可以直观了解流程到达了哪里、每个环节谁在处理、处理耗时等信息,并在必要时进行干预。
- 如果只是用一个状态字段,通常缺乏对流程执行过程的可视化管理,需要自己额外开发大量记录与监控功能。
-
可审计性
- 工作流引擎会自动记录流程节点的执行历史、任务处理人、处理时间、审批结果等,可以在事后对流程进行审计与溯源。
- 简单的状态字段只记录了当下状态,而缺乏中间环节的信息,需要自己额外实现审计。
-
通用的流程设计与复用
- 通过 BPMN 2.0 建立流程模型,可以复用相同或相似的流程,不需要在每个业务功能里重复开发。
- 工作流引擎也能提供“子流程”等特性,用于流程间的组合和复用。
- 工作流引擎 Activiti 提供了从流程建模、部署、执行到监控的完整解决方案,通过 BPMN 2.0 标准将复杂业务流程以图形化的形式抽象并可执行。
- 对于业务流程相对固定且简单的场景,可以使用简单的状态机来处理,但当流程变得复杂,或者对可视化、审计、动态修改流程有更高要求时,工作流引擎能够更好地应对这些挑战。
- 通过将流程逻辑从业务系统中剥离出来,Activiti 可以显著降低程序逻辑的复杂度,提高流程维护和扩展的效率,也有助于企业实现更全面、更精细的流程化管理。
Code Demo
- 项目结构
- 依赖与配置
- 示例流程(BPMN 文件)
- Java 代码示例
- 前置动作:数据库、部署流程
- 如何在前端查看流程图
- 如何运行示例
说明
- Activiti 7 与之前的 Activiti 6.x 有一些变更,也可以选择 Flowable 或者其他 BPMN 引擎,原理都类似。
1. 项目结构
以下是一个示例的 Maven 项目结构:
activiti-demo
├── src
│ ├── main
│ │ ├── java
│ │ │ └── com.example.activiti
│ │ │ ├── ActivitiDemoApplication.java # Spring Boot 启动类
│ │ │ ├── config
│ │ │ │ └── ActivitiConfig.java # Activiti 配置
│ │ │ ├── controller
│ │ │ │ └── LeaveController.java # 示例控制器
│ │ │ ├── listener
│ │ │ │ └── UserTaskListener.java # 示例任务监听器
│ │ │ ├── service
│ │ │ │ └── LeaveService.java # 自定义服务类
│ │ │ └── model
│ │ │ └── LeaveRequest.java # 请假申请实体
│ │ ├── resources
│ │ │ ├── application.yml # Spring Boot 配置
│ │ │ ├── processes
│ │ │ │ └── leave-process.bpmn20.xml # 流程定义文件 (BPMN 2.0)
│ │ │ └── static
│ │ │ └── diagram # 流程图输出目录 (可选)
│ │ └── webapp
│ │ └── index.html # 简单的前端页面(可选)
└── pom.xml
2. 依赖与配置
2.1 Maven 依赖
示例使用 Activiti 7 以及 Spring Boot,一般在 pom.xml
中引入(以最新版本为例,如有更新可自行调整):
<dependencies>
<!-- Spring Boot Starter Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Boot Starter JPA + H2 DB (内存数据库演示) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Activiti 7 Starter -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
<version>7.3.0</version>
</dependency>
<!-- Lombok (可选) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
如果只想试验流程,可以使用内存数据库 H2,方便快速测试。生产环境或正式开发中,应配置 MySQL / PostgreSQL 等数据库,并确保数据库链接无误。
2.2 Spring Boot 配置
使用 application.yml
为例,内容大致如下:
server:
port: 8080
spring:
datasource:
driver-class-name: org.h2.Driver
url: jdbc:h2:mem:activiti-test;DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=-1
username: sa
password:
h2:
console:
enabled: true
path: /h2-console
# 让 Spring Data JPA 自动建表
spring.jpa.hibernate.ddl-auto: update
# Activiti 自定义配置
activiti:
# 这里 Activiti 7 有多种配置方式,比如对用户、组的管理等
3. 示例流程(BPMN 文件)
是个图,但本质还是个xml文件
假设我们有一个请假流程 (Leave Process),最简单的流程定义可能如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
xmlns:dc="http://www.omg.org/spec/DD/20100524/DC"
xmlns:di="http://www.omg.org/spec/DD/20100524/DI"
xmlns:activiti="http://activiti.org/bpmn"
targetNamespace="http://www.activiti.org/test">
<process id="leaveProcess" name="Leave Process" isExecutable="true">
<!-- 开始事件 -->
<startEvent id="startEvent" name="Start">
<outgoing>flow1</outgoing>
</startEvent>
<!-- 申请人提交申请(用户任务) -->
<userTask id="applyTask" name="Apply for Leave" activiti:assignee="${applicant}">
<incoming>flow1</incoming>
<outgoing>flow2</outgoing>
</userTask>
<!-- 部门领导审批(用户任务) -->
<userTask id="approveTask" name="Manager Approval" activiti:assignee="${manager}">
<incoming>flow2</incoming>
<outgoing>flow3</outgoing>
</userTask>
<!-- 排他网关: 同意 or 拒绝 -->
<exclusiveGateway id="exclusiveGateway" name="Exclusive Gateway">
<incoming>flow3</incoming>
<outgoing>flow4</outgoing>
<outgoing>flow5</outgoing>
</exclusiveGateway>
<!-- 结束事件: 同意 -->
<endEvent id="endEventApproved" name="Approved">
<incoming>flow4</incoming>
</endEvent>
<!-- 结束事件: 拒绝 -->
<endEvent id="endEventRejected" name="Rejected">
<incoming>flow5</incoming>
</endEvent>
<!-- 顺序流定义 -->
<sequenceFlow id="flow1" sourceRef="startEvent" targetRef="applyTask" />
<sequenceFlow id="flow2" sourceRef="applyTask" targetRef="approveTask" />
<sequenceFlow id="flow3" sourceRef="approveTask" targetRef="exclusiveGateway" />
<!-- 假设审批人直接在UserTask完成任务时带上变量 “approved”=true/false,用条件判断跳转 -->
<sequenceFlow id="flow4" sourceRef="exclusiveGateway" targetRef="endEventApproved">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${approved == true}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow5" sourceRef="exclusiveGateway" targetRef="endEventRejected">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${approved == false}]]></conditionExpression>
</sequenceFlow>
</process>
</definitions>
- 申请人发起申请 (
applyTask
) - 部门领导进行审批 (
approveTask
) - 根据流程变量
approved
的值,决定走“同意”还是“拒绝” - 对应跳转到不同的结束事件
注意:以上使用
${applicant}
、${manager}
等表达式表示动态分配。你也可以在userTask
中写死某个assignee
或者使用candidateGroups
等方式分配任务。
4. Java 代码示例
4.1 Spring Boot 启动类
package com.example.activiti;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ActivitiDemoApplication {
public static void main(String[] args) {
SpringApplication.run(ActivitiDemoApplication.class, args);
}
}
4.2 Activiti 配置类
package com.example.activiti.config;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ActivitiConfig {
// 一般可以在此处自定义 Bean,比如 RepositoryService, RuntimeService, TaskService,
// 或者引入自定义的 IdentityService 等等。
// Activiti 7 的 starter 会自动进行大部分自动配置,这里可以根据需求进行额外配置
}
4.3 请假申请实体 (可选)
package com.example.activiti.model;
import lombok.Data;
@Data
public class LeaveRequest {
private String applicant; // 申请人
private String reason; // 请假原因
private int days; // 请假天数
}
在 Demo 中,这个实体只是用来演示业务数据的接收与传递,实际项目中可用数据库实体进行持久化。
4.4 Service 类:调用工作流引擎
package com.example.activiti.service;
import com.example.activiti.model.LeaveRequest;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
@Service
public class LeaveService {
@Autowired
private RuntimeService runtimeService;
@Autowired
private TaskService taskService;
/**
* 启动请假流程
*/
public String startLeaveProcess(LeaveRequest leaveRequest) {
// 将业务信息作为流程变量传递
Map<String, Object> vars = new HashMap<>();
vars.put("applicant", leaveRequest.getApplicant());
// manager 可以根据不同申请人或业务规则决定
vars.put("manager", "managerUser");
vars.put("approved", false); // 初始默认未审批
// 使用流程定义的 key
String processDefinitionKey = "leaveProcess";
// 启动流程实例
// 这里返回的是流程实例 ID, 也可以存储到数据库中与业务关联
return runtimeService.startProcessInstanceByKey(processDefinitionKey, vars).getId();
}
/**
* 申请人提交申请(完成第一个UserTask)
*/
public void submitLeaveApply(String taskId) {
// 完成任务,下一步流转到领导审批节点
taskService.complete(taskId);
}
/**
* 领导审批
*/
public void approve(String taskId, boolean approved) {
Map<String, Object> vars = new HashMap<>();
vars.put("approved", approved);
taskService.complete(taskId, vars);
}
}
4.5 Controller 示例
package com.example.activiti.controller;
import com.example.activiti.model.LeaveRequest;
import com.example.activiti.service.LeaveService;
import org.activiti.engine.TaskService;
import org.activiti.engine.task.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/leave")
public class LeaveController {
@Autowired
private LeaveService leaveService;
@Autowired
private TaskService taskService;
/**
* 启动请假流程
*/
@PostMapping("/start")
public String startLeaveProcess(@RequestBody LeaveRequest leaveRequest) {
String processInstanceId = leaveService.startLeaveProcess(leaveRequest);
return "流程已启动,ID=" + processInstanceId;
}
/**
* 获取当前待办任务(仅作演示:获取所有待办)
*/
@GetMapping("/tasks")
public List<Task> getTasks() {
// 这里简单查询所有任务
return taskService.createTaskQuery().list();
}
/**
* 申请人提交申请
*/
@PostMapping("/apply/{taskId}")
public String submitApplication(@PathVariable("taskId") String taskId) {
leaveService.submitLeaveApply(taskId);
return "申请提交成功";
}
/**
* 领导审批
*/
@PostMapping("/approve/{taskId}")
public String approveApplication(@PathVariable("taskId") String taskId,
@RequestParam("approved") boolean approved) {
leaveService.approve(taskId, approved);
return "审批完成,结果=" + (approved ? "通过" : "拒绝");
}
}
5. 前置动作:数据库、部署流程
5.1 Activiti 自动部署流程定义
- 将 BPMN 文件(如
leave-process.bpmn20.xml
)放置在src/main/resources/processes/
目录下,Activiti Starter 会自动扫描并部署。 - 若需手动部署,可在代码中通过
RepositoryService
部署。自动部署可省去手动操作,非常方便。
/*
RepositoryService repositoryService;
Deployment deployment = repositoryService.createDeployment()
.addClasspathResource("processes/leave-process.bpmn20.xml")
.name("Leave Process Deployment")
.deploy();
*/
5.2 运行数据库(H2 内存数据库)
- 由于我们在
application.yml
中配置了 H2 内存数据库,启动 Spring Boot 时会自动初始化。 - 如果想查看表结构,可以通过访问 http://localhost:8080/h2-console,JDBC URL 与用户名密码与 yml 一致。
6. 在前端 / 浏览器中查看流程图
6.1 动态生成流程图
Activiti 提供了一些方法,可以在流程部署后,读取流程定义并生成对应的 流程图(PNG / SVG),然后可供前端展示。
示例代码大致如下:
@GetMapping("/diagram/{processDefinitionId}")
public void generateProcessDiagram(@PathVariable("processDefinitionId") String processDefinitionId,
HttpServletResponse response) throws IOException {
BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
// 使用Activiti的ProcessDiagramGenerator生成图片
ProcessEngineConfiguration processEngineConfiguration = ((RepositoryServiceImpl) repositoryService)
.getProcessEngineConfiguration();
ProcessDiagramGenerator diagramGenerator = processEngineConfiguration.getProcessDiagramGenerator();
// 高亮当前节点或历史节点(可选)
// 这里先不加高亮,直接画所有节点
List<String> highLightedActivities = Collections.emptyList();
List<String> highLightedFlows = Collections.emptyList();
InputStream in = diagramGenerator.generateDiagram(
bpmnModel,
highLightedActivities,
highLightedFlows,
"宋体", "宋体", "宋体",
processEngineConfiguration.getClassLoader(),
1.0, true);
// 写入响应
response.setContentType("image/png");
byte[] buffer = new byte[1024];
int len;
while ((len = in.read(buffer)) != -1) {
response.getOutputStream().write(buffer, 0, len);
}
in.close();
}
- 当我们调用
GET /diagram/{processDefinitionId}
时,后端会动态生成一张流程图 PNG 并输出。前端可将其直接显示。 - 如果需要高亮流程中已经完成的任务节点或正在执行的节点,可以从
HistoryService
或RuntimeService
获取相关信息,然后传入highLightedActivities
、highLightedFlows
。
6.2 BPMN 文件可视化
- 有时也可以直接在前端上传 BPMN 文件到 Activiti Modeler 或其他 BPMN 在线编辑器查看。
- 也可以保留 BPMN XML 文件下载供查看。
7. 如何运行示例
- 克隆或下载 此示例项目到本地。
- 确保安装了 JDK 8+、Maven 3+。
- 在项目根目录执行
mvn clean package
。 - 运行
ActivitiDemoApplication
(IDE 中运行或命令行mvn spring-boot:run
)。 - 访问
http://localhost:8080
,就可以开始进行接口测试。
测试步骤(模拟一个最简单的流程):
-
启动流程
POST /leave/start Request Body: { "applicant": "Alice", "reason": "Take a vacation", "days": 3 }
响应:
流程已启动,ID=xxxxx
-
查询待办任务
GET /leave/tasks
响应示例:
[ { "id": "2505", "name": "Apply for Leave", "assignee": "Alice", ... } ]
-
完成申请人提交 (完成第一个用户任务)
POST /leave/apply/2505
响应:
申请提交成功
-
再次查询待办
GET /leave/tasks
响应示例:
[ { "id": "5005", "name": "Manager Approval", "assignee": "managerUser", ... } ]
-
领导审批 (完成第二个用户任务,传入审批结果)
POST /leave/approve/5005?approved=true
响应:
审批完成,结果=通过
-
流程结束
- 此时可在流程引擎的历史中查到该流程已经结束。
-
生成流程图(可选)
- 在浏览器中访问:
http://localhost:8080/diagram/leaveProcess:1:...
(此处 ID 要换成实际查询到的流程定义 ID 或 instance ID) - 即可查看自动生成的流程图。
- 在浏览器中访问: