杰瑞科技汇

Activiti工作流如何高效集成Java开发?

目录

  1. 什么是工作流引擎?为什么需要它?
  2. Activiti 简介
  3. 核心概念与术语
  4. Activiti 与 Spring/Spring Boot 集成(最常用)
  5. 实战案例:一个简单的请假流程
    • 1. 定义流程 (BPMN 2.0)
    • 2. 部署流程
    • 3. 启动流程实例
    • 4. 查询任务并处理(申请、审批)
    • 5. 流程图可视化
  6. 常用 API 详解
  7. 高级特性
  8. Activiti 7 的重大变化
  9. Activiti 的优缺点与适用场景
  10. 学习资源与总结

什么是工作流引擎?为什么需要它?

工作流引擎是一个可以执行由流程定义(Process Definition)描述的业务流程的软件服务,你可以把它想象成一个“流程虚拟机”,它读取流程图(BPMN 2.0 格式),并严格按照流程图的定义来驱动业务一步步执行。

Activiti工作流如何高效集成Java开发?-图1
(图片来源网络,侵删)

为什么需要它?

在没有工作流引擎时,我们的业务流程代码通常是这样写的:

// 伪代码:一个混乱的请假审批流程
public void applyForLeave(LeaveApplication application) {
    // 1. 保存申请
    saveApplication(application);
    // 2. 如果请假天数 <= 3天,主管审批
    if (application.getDays() <= 3) {
        notifyManager(application.getEmployeeId());
        if (managerApproved(application)) {
            // 3. 主管通过,流程结束
            application.setStatus("APPROVED");
            updateApplication(application);
        } else {
            // 4. 主管拒绝,流程结束
            application.setStatus("REJECTED");
            updateApplication(application);
        }
    } else {
        // 5. 如果请假天数 > 3天,需要总监审批
        notifyDirector(application.getEmployeeId());
        if (directorApproved(application)) {
            // 6. 总监通过,再让主管审批
            notifyManager(application.getEmployeeId());
            if (managerApproved(application)) {
                application.setStatus("APPROVED");
                updateApplication(application);
            } else {
                application.setStatus("REJECTED");
                updateApplication(application);
            }
        } else {
            application.setStatus("REJECTED");
            updateApplication(application);
        }
    }
}

这段代码有什么问题?

  • 硬编码逻辑:审批流程写死在代码里,如果流程规则变了(比如增加一个HR审批环节),就必须修改、重新编译、部署代码。
  • 可读性差:复杂的 if-else 逻辑难以理解和维护。
  • 业务与代码耦合:业务人员(HR、经理)看不懂代码,无法参与流程设计。
  • 扩展性差:增加新的流程(如报销、采购)需要写更多类似的硬编码逻辑。

使用工作流引擎后:

Activiti工作流如何高效集成Java开发?-图2
(图片来源网络,侵删)
  1. 业务流程可视化:业务分析师使用图形化工具(如 Camunda Modeler, Activiti Modeler)画出流程图,这张图就是流程定义。
  2. 代码与流程分离:Java 代码不再关心具体的流程走向,它只需要调用引擎的几个简单 API:
    • deployProcess():部署流程图。
    • startProcess():启动一个流程实例(一次具体的申请)。
    • getTasks(user):获取某个用户待办的任务列表。
    • complete(taskId, comment):处理一个任务(审批通过/拒绝)。
  3. 动态调整:流程规则变了?只需要修改流程图,然后重新部署,Java 代码完全不需要改动

Activiti 简介

Activiti 是一个开源的、灵活的、功能强大的工作流引擎和业务流程管理 (BPM) 平台,它由 Tom Baeyens(jBPM 的创始人之一)创建,并捐赠给了 Apache 软件基金会。

核心特点:

  • 遵循 BPMN 2.0 标准:BPMN (Business Process Model and Notation) 是一种业务流程建模和标准化的图形化语言,使得流程定义具有了通用性和可移植性。
  • 轻量级且高性能:基于 Java 开发,易于集成,性能优异。
  • 丰富的功能:支持用户任务、服务任务、网关、事件、监听器等几乎所有 BPMN 2.0 元素。
  • 强大的社区和生态:拥有活跃的社区和大量的文档,是 Java 世界中最流行的工作流引擎之一。

核心概念与术语

理解这些术语是掌握 Activiti 的关键:

术语 英文名 解释
流程定义 Process Definition 描述一个业务流程的“蓝图”,通常以 BPMN 2.0 的 XML 文件(.bpmn20.xml)形式存在,一次部署对应一个流程定义。
流程实例 Process Instance 流程定义的一次具体执行,张三提交一次请假申请,就创建了一个请假流程的实例。
任务 Task 流程中的一个工作单元,需要人或系统来处理,最常见的是用户任务
用户任务 User Task 需要由特定人员(用户)来处理的任务,如“经理审批”。
服务任务 Service Task 由后端服务自动完成的任务,如“发送邮件”、“调用REST API”、“更新数据库”。
任务监听器 Task Listener 在任务生命周期(创建、完成、删除等)的特定时刻执行的 Java 代码。
执行监听器 Execution Listener 在流程执行流到达某个节点(进入、离开)时执行的 Java 代码。
部署 Deployment 将流程定义文件(.bpmn, .bar)加载到 Activiti 中,并持久化到数据库。
工作流存储库 Repository Service 管理流程定义,提供部署、查询、删除流程定义等功能。
运行时管理 Runtime Service 管理流程实例,提供启动、查询、删除流程实例等功能。
任务管理 Task Service 管理任务,提供查询、认领、完成、转办任务等功能,这是与用户交互最频繁的 Service。
历史管理 History Service 查询流程实例、任务等的历史数据。
身份标识 Identity Service 管理用户和组。

Activiti 与 Spring/Spring Boot 集成(最常用)

在现代 Java 应用中,Activiti 几乎总是与 Spring 或 Spring Boot 一起使用。

Activiti工作流如何高效集成Java开发?-图3
(图片来源网络,侵删)

Maven 依赖:

<dependency>
    <groupId>org.activiti</groupId>
    <artifactId>activiti-spring-boot-starter</artifactId>
    <version>7.7.0</version> <!-- 使用与 Spring Boot 版本匹配的版本 -->
</dependency>

核心配置 (application.yml):

# 自动部署 resources/processes 目录下的所有 .bpmn 和 .bpmn20.xml 文件
activiti:
  # 检查并自动升级数据库表结构
  database-schema-update: true
  # 是否在启动时进行流程定义的校验
  check-process-definitions: true
  # 关闭安全认证(开发环境),生产环境需要配置
  # security: enabled: false 

集成后,Spring Boot 会自动:

  1. 创建并配置 ProcessEngine(Activiti 的核心引擎)。
  2. 自动扫描 src/main/resources/processes 目录,并将找到的 BPMN 文件部署到数据库中。
  3. RepositoryService, RuntimeService, TaskService 等核心服务注入到 Spring 容器中,我们可以直接使用 @Autowired 注入。

实战案例:一个简单的请假流程

我们将实现一个“员工申请 -> 经理审批”的流程。

1. 定义流程 (BPMN 2.0)

src/main/resources/processes 目录下,创建一个 leave-request.bpmn20.xml 文件。

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
             xmlns:activiti="http://activiti.org/bpmn"
             targetNamespace="http://www.activiti.org/processdef">
  <process id="leaveRequest" name="请假申请流程">
    <!-- 开始事件 -->
    <startEvent id="startEvent" name="开始" />
    <!-- 用户任务:员工填写申请 -->
    <userTask id="submitLeave" name="提交申请" activiti:candidateUsers="employee" />
    <!-- 用户任务:经理审批 -->
    <userTask id="approveLeave" name="经理审批" activiti:candidateUsers="manager" />
    <!-- 排他网关:判断审批结果 -->
    <exclusiveGateway id="decision" name="审批决策" />
    <!-- 结束事件:审批通过 -->
    <endEvent id="approvedEnd" name="审批通过" />
    <sequenceFlow id="flow1" sourceRef="decision" targetRef="approvedEnd" name="通过">
      <conditionExpression xsi:type="tFormalExpression">${approved == 'true'}</conditionExpression>
    </sequenceFlow>
    <!-- 结束事件:审批拒绝 -->
    <endEvent id="rejectedEnd" name="审批拒绝" />
    <sequenceFlow id="flow2" sourceRef="decision" targetRef="rejectedEnd" name="拒绝">
      <conditionExpression xsi:type="tFormalExpression">${approved == 'false'}</conditionExpression>
    </sequenceFlow>
    <!-- 连接各个节点 -->
    <sequenceFlow id="flow3" sourceRef="startEvent" targetRef="submitLeave" />
    <sequenceFlow id="flow4" sourceRef="submitLeave" targetRef="approveLeave" />
    <sequenceFlow id="flow5" sourceRef="approveLeave" targetRef="decision" />
  </process>
</definitions>

关键点解释:

  • id: 节点的唯一标识,在代码中会用到。
  • name: 节点的显示名称。
  • activiti:candidateUsers="employee": 指定这个任务的候选人组是 "employee",任何 "employee" 组的用户都可以认领这个任务。
  • exclusiveGateway: 排他网关,相当于 if-else,它会根据第一个 true 的条件表达式来选择出口。
  • conditionExpression: 流程流转的条件,这里的 ${approved} 变量会在任务完成时由代码传入。

2. 部署流程

在 Spring Boot 启动时,activiti-spring-boot-starter 会自动完成部署,你也可以手动通过代码部署:

@Autowired
private RepositoryService repositoryService;
// 手动部署(通常不需要,自动部署已足够)
public void deployProcess() {
    Deployment deployment = repositoryService.createDeployment()
            .addClasspathResource("processes/leave-request.bpmn20.xml")
            .deploy();
    System.out.println("部署成功,部署ID: " + deployment.getId());
}

3. 启动流程实例

当员工提交请假申请时,需要启动一个流程实例。

@Autowired
private RuntimeService runtimeService;
public void startLeaveProcess(String employeeName, int days) {
    // 使用流程的 key (id) 来启动
    Map<String, Object> variables = new HashMap<>();
    variables.put("employee", employeeName);
    variables.put("days", days);
    // 这里可以设置一些初始变量
    ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("leaveRequest", variables);
    System.out.println("流程启动成功,实例ID: " + processInstance.getId());
}

4. 查询任务并处理

这是工作流的核心交互部分。

员工查询并提交自己的申请:

@Autowired
private TaskService taskService;
// 员工查询自己的待办任务
public List<Task> findEmployeeTasks() {
    return taskService.createTaskQuery().taskCandidateUser("employee").list();
}
// 员工认领任务(任务从候选池变为个人待办)
public void claimTask(String taskId, String assignee) {
    taskService.claim(taskId, assignee);
    System.out.println("任务 " + taskId + " 已被 " + assignee + " 认领");
}
// 员工完成申请任务(通常只是提交,不直接审批)
public void submitLeaveTask(String taskId) {
    taskService.complete(taskId);
    System.out.println("任务 " + taskId + " 已提交");
}

经理查询并审批:

// 经理查询自己的待办任务
public List<Task> findManagerTasks() {
    return taskService.createTaskQuery().taskCandidateUser("manager").list();
}
// 经理审批任务
public void approveLeaveTask(String taskId, boolean isApproved) {
    Map<String, Object> variables = new HashMap<>();
    variables.put("approved", isApproved); // 设置网关判断条件
    taskService.complete(taskId, variables);
    System.out.println("任务 " + taskId + " 已审批,结果: " + (isApproved ? "通过" : "拒绝"));
}

5. 流程图可视化

为了方便调试和展示,Activiti 提供了生成流程图的功能。

@Autowired
private RepositoryService repositoryService;
@Autowired
private RuntimeService runtimeService;
@Autowired
private TaskService taskService;
public void generateDiagram(String processInstanceId) throws IOException {
    ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
            .processInstanceId(processInstanceId)
            .singleResult();
    BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());
    // 使用流程引擎自带的工具生成图片
    ProcessDiagramGenerator diagramGenerator = new DefaultProcessDiagramGenerator();
    InputStream inputStream = diagramGenerator.generateDiagram(
        bpmnModel, 
        "png", 
        runtimeService.getActiveActivityIds(processInstanceId),
        Collections.emptyList(), 
        "宋体", 
        "宋体", 
        "宋体",
        1.0, 
        true
    );
    // 将输入流保存为文件
    Files.copy(inputStream, Paths.get("leave-process.png"));
    inputStream.close();
    System.out.println("流程图已生成: leave-process.png");
}

常用 API 详解

Service 主要用途 常用 API
RepositoryService 流程定义仓库 createDeployment(), deploy(), getProcessDefinition(), getBpmnModel()
RuntimeService 运行时管理 startProcessInstanceByKey(), startProcessInstanceById(), getProcessInstance(), signal() (用于信号事件)
TaskService 任务管理 createTaskQuery(), complete(), claim(), assignTask(), addComment() (添加任务评论)
HistoryService 历史数据查询 createHistoricTaskInstanceQuery(), createHistoricProcessInstanceQuery()
ManagementService 引擎管理 getDatabaseTableNames() (用于检查表结构)

高级特性

  • 服务任务: 在 BPMN 中定义一个节点,并指定 activiti:classactiviti:expression 来调用一个 Java 类或 Spring Bean 的方法,实现业务逻辑的自动化。
  • 事件监听器: 在流程的生命周期(如流程开始、任务完成)中执行自定义逻辑,可以通过实现 TaskListenerExecutionListener 接口,或使用表达式来调用方法。
  • 流程变量: 在流程中传递数据的载体,可以通过 RuntimeService 启动时传入,或通过 TaskService 完成任务时传入,变量可以是基本类型、JavaBean,甚至是 JSON 对象。
  • 网关:
    • 排他网关: if-else
    • 并行网关: fork-join,会同时创建多个分支,等所有分支都结束后再汇合。
    • 包容网关: for-eachif-then-else,可以同时满足多个条件。
  • 子流程: 将一个复杂的流程拆分成多个可复用的子流程,提高模块化程度。

Activiti 7 的重大变化

Activiti 7 是一个重要的版本,它引入了与 Spring Security 的深度集成,并提出了 "Flowable" 概念。

  1. Flowable 引擎: Activiti 7 的核心引擎基于 Flowable 5.22,并在此基础上发展,这意味着 Activiti 7 和 Flowable 在底层技术上非常接近。
  2. Spring Security 集成: Activiti 7 默认使用 Spring Security 来管理用户和权限。IdentityService 不再独立管理用户,而是直接从 Spring Security 的 Authentication 对象中获取当前登录用户,这简化了与现有 Spring 应用的集成。
  3. 新的 API 风格: 引入了一些新的、更符合 Spring 风格的 API,但旧的 API 仍然保持兼容。
  4. 社区变化: 由于与 Flowable 的渊源,Activiti 社区出现了分裂,导致其发展势头有所减弱,但它在企业中仍有大量存量应用。

注意: 如果新项目,也可以考虑直接使用 Flowable,它的社区和商业支持似乎更为活跃。


Activiti 的优缺点与适用场景

优点:

  • 标准化: 基于 BPMN 2.0,流程定义通用、可读性强。
  • 功能强大: 覆盖了工作流的大部分场景,用户任务、服务任务、网关、事件等一应俱全。
  • 易于集成: 与 Spring/Spring Boot 集成非常方便,开箱即用。
  • 生态成熟: 拥有大量文档、教程和社区支持。

缺点:

  • 学习曲线: 概念较多,需要理解 BPMN、流程实例、任务、监听器等。
  • 数据库依赖: 需要一套数据库表来存储流程状态,对数据库有一定侵入性。
  • 灵活性限制: 流程一旦启动,其主干路径就固定了,动态修改主干比较困难(虽然可以通过事件等方式变通)。
  • Activiti 7 社区问题: 如上所述,社区存在一些不确定性。

适用场景:

  • 有明确、固定流程的业务: 如请假、报销、采购、订单处理、合同审批等。
  • 需要流程可视化和动态调整的场景: 让业务人员能参与到流程设计中。
  • 需要将流程状态持久化,并支持任务分派和跟踪的系统

不适用场景:

  • 简单的、一次性的状态机: 如果逻辑非常简单,用 if-else 或状态机模式可能更轻量。
  • 需要极高灵活性的、路径完全动态生成的场景

学习资源与总结

学习资源:

  • 官方文档: Activiti User Guide (这是最重要的资源)
  • 书籍: 《Activiti in Action》
  • 博客: CSDN、掘金、思否等平台有大量 Activiti 教程和实战文章。
  • 视频: B站等平台有相关的视频课程。

Activiti 是一个成熟、强大的工作流引擎,能够有效地将复杂的业务逻辑从代码中剥离出来,实现业务流程的标准化、可视化和动态管理,通过将其与 Spring Boot 框架集成,可以快速地在企业级应用中构建出流程驱动的功能模块。

虽然 Activiti 7 之后的社区发展存在一些波折,但它庞大的存量用户和成熟的解决方案使其在很长一段时间内仍然是 Java 工作流领域的重要选择,掌握 Activiti,对于构建复杂的企业级应用是一项非常有价值的技能。

分享:
扫描分享到社交APP
上一篇
下一篇