在这里插入图片描述

一、介绍

flowable 是一款开源的企业级工作流引擎,基于 Java 开发,支持 BPMN 2.0 标准(业务流程建模与 notation 规范),能帮助开发者快速实现流程自动化,无需重复编写流程控制逻辑
由于 flowable 中涉及的概念较多,为了方便理解,我们主要介绍几个常用的概念

1. 流程定义(Process Definition)

解释:相当于流程的 “设计图纸” 或 “标准模板”,把业务流程的所有步骤、流转规则都固定下来,后续所有相同业务都按这个模板执行。
场景:比如公司统一制定的 “请假申请流程模板”,明确规定必须经过 “员工提交申请→部门主管审批→人事归档” 这三步,这就是一个完整的流程定义

2. 流程实例(Process Instance)

解释:是流程定义的 “一次实际执行”,相当于根据 “设计图纸” 造出的 “具体产品”,每个实例都是独立的,互不干扰。
场景:员工张三 10 月 20 日提交的 “3 天病假申请”、员工李四 10 月 21 日提交的 “5 天年假申请”,分别是 “请假申请流程模板” 的两个不同流程实例

3. 任务(Task)

解释:流程实例中的 “具体待办事项”,是流程落地执行的最小单元,需要人或系统动手完成后,流程才能继续往下走。
场景:在张三的请假流程实例中,“张三填写并提交请假单” 是一个任务,“部门主管审核张三的请假申请” 也是一个任务,每个任务完成后,流程才会进入下一个环节

4. 事件(Event)

解释:流程中的 “特殊触发点” 或 “关键节点”,能主动触发操作,或被动响应情况,甚至改变流程的走向。
场景:“员工点击提交请假单” 触发 “开始事件”,流程正式启动;“人事完成归档” 触发 “结束事件”,流程彻底收尾

5. 条件表达式(Condition Expression)

解释:流程的 “分支开关”,根据实际业务数据判断该走哪条路,让流程能灵活适配不同场景。
场景:在请假流程中,设置条件表达式 “审批结果等于是”,系统会根据部门主管填写的审批结果自动切换路径,不用人工判断分支

二、示例

下面是一个简单的请假工作流,不涉及人员,仅包含3个任务和1个审批分支,当审批结果为 true 时,进入放假分支然后结束工作流,当审批结果为 false 时直接结束
在这里插入图片描述

1、引入 flowable 6.7 依赖

    <properties>
        <java.version>1.8</java.version>
        <maven.compiler.source>${java.version}</maven.compiler.source>
        <maven.compiler.target>${java.version}</maven.compiler.target>
        <maven-compiler-plugin.version>3.8.0</maven-compiler-plugin.version>
        <mysql.version>8.0.17</mysql.version>
        <mybatis-plus.version>3.5.2</mybatis-plus.version>
        <lombok.version>1.18.24</lombok.version>
        <flowable.version>6.7.2</flowable.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.flowable</groupId>
            <artifactId>flowable-spring-boot-starter</artifactId>
            <version>${flowable.version}</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.version}</version>
        </dependency>
        <dependency>
            <groupId>com.oracle</groupId>
            <artifactId>ojdbc14</artifactId>
            <version>${oracle.version}</version>
        </dependency>
    </dependencies>

2、配置文件和启动程序

server:
  port: 8080

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/flowable?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true&innodb_large_prefix=on&character_set_server=utf8mb4&collation_server=utf8mb4_unicode_ci
    username: root
    password: root

# 工作流 Flowable 配置
flowable:
  check-process-definitions: true
  history-level: full
  async-executor-activate: false
  # 新增这行配置,开启自动建表
  database-schema-update: true
package com.luck.cloud.flowable;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * 工作流服务
 * @author luck
 * @date 2024/7/4
 */
@SpringBootApplication
public class LuckFlowAbleApplication {
    public static void main(String[] args) {
        SpringApplication.run(LuckFlowAbleApplication.class, args);
    }
}

3、搭建数据库

在 mysql 数据库创建 flowable 测试库,最好用 8.0 以上的 mysql 。启动项目会扫描数据库中是否存在对应的表,没有表则会自动生成,低版本的 mysql 会因为索引长度问题导致建表报错
启动当前项目(上面的 LuckFlowAbleApplication 或自定义的启动程序),等待表自动生成成功,生成好后关闭项目
在这里插入图片描述
flowable 5 类核心表简化说明:

  • ACT_RE_*(存储类)
    前缀 “RE”=Repository(存储),由 RepositoryService 操作。
    存静态数据:流程定义、流程资源(图片、规则等),长期保留。
  • ACT_RU_*(运行时类)
    前缀 “RU”=Runtime(运行时),由 RuntimeService 操作。
    存动态数据:流程变量、用户任务、职责(job)等,流程结束后自动删除,保证表轻量化。
  • ACT_ID_*(身份类)
    前缀 “ID”=Identity(组织机构)。
    存身份数据:用户、用户组等。
  • ACT_HI_*(历史类)
    前缀 “HI”=History(历史记录),由 HistoryService 操作。
    存归档数据:已结束的流程实例、历史变量、历史任务等,永久保留用于追溯。
  • ACT_GE_*(通用类)
    前缀 “GE”=General(普通)
    存通用数据:供引擎各模块共用的基础信息。

4、安装 flowable 插件

在 Idea 中找到 File -> Settings -> Plugins,搜索 flowable 并点击安装插件,这个插件能帮助我们快速地设计和开发流程
在这里插入图片描述

5、设计工作流

新建 resources/processes 目录,这是 flowable 默认读取的工作流目录,新建一个 test.bpmn20.xml 文件,bpmn20.xml 是 flowabe 常用的文件类型,文件内容如下
在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:flowable="http://flowable.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.flowable.org/processdef">
  <process id="test" name="test" isExecutable="true">
    <startEvent id="sid-40d3725f-72a2-4ccf-953a-e76ce60af2e7"/>
    <endEvent id="sid-b0d8a0bb-8aaf-444b-8948-63fef59c3224"/>
    <userTask id="sid-7fe567ec-a452-47fd-a6b4-d2912218fc63" name="请假"/>
    <sequenceFlow id="sid-26872e30-21fe-4f3c-99b8-e796ec7499e8" sourceRef="sid-40d3725f-72a2-4ccf-953a-e76ce60af2e7" targetRef="sid-7fe567ec-a452-47fd-a6b4-d2912218fc63"/>
    <userTask id="sid-0a2b5d6b-5bfd-4d5d-8ff9-f32a52ea0862" name="审批"/>
    <sequenceFlow id="sid-7cc773e3-0c9d-4bf2-bee3-76e1a36fd032" sourceRef="sid-0a2b5d6b-5bfd-4d5d-8ff9-f32a52ea0862" targetRef="sid-40d76bec-d236-469b-b269-106c19bfc691">
      <documentation/>
      <conditionExpression>${result==true}</conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="sid-1c738e47-cf79-4074-ae28-b99acc4af09f" sourceRef="sid-7fe567ec-a452-47fd-a6b4-d2912218fc63" targetRef="sid-0a2b5d6b-5bfd-4d5d-8ff9-f32a52ea0862"/>
    <userTask id="sid-40d76bec-d236-469b-b269-106c19bfc691" name="放假"/>
    <sequenceFlow id="sid-737c9616-400c-4f3e-aa8a-b396d70d1cd2" sourceRef="sid-40d76bec-d236-469b-b269-106c19bfc691" targetRef="sid-b0d8a0bb-8aaf-444b-8948-63fef59c3224"/>
    <sequenceFlow id="sid-3f2691b9-6747-4ebb-a94d-44c89857adf3" sourceRef="sid-0a2b5d6b-5bfd-4d5d-8ff9-f32a52ea0862" targetRef="sid-b0d8a0bb-8aaf-444b-8948-63fef59c3224">
      <conditionExpression>${result==false}</conditionExpression>
    </sequenceFlow>
  </process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_test">
    <bpmndi:BPMNPlane bpmnElement="test" id="BPMNPlane_test">
      <bpmndi:BPMNShape id="shape-bf4c66e8-1b76-45e0-b234-e81725a2829d" bpmnElement="sid-40d3725f-72a2-4ccf-953a-e76ce60af2e7">
        <omgdc:Bounds x="-445.0" y="-145.0" width="30.0" height="30.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="shape-6a046670-cd1f-4ef0-87af-b9383f01d751" bpmnElement="sid-b0d8a0bb-8aaf-444b-8948-63fef59c3224">
        <omgdc:Bounds x="-60.0" y="-25.00003" width="30.0" height="30.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="shape-182339a3-c187-4a58-8b2a-3d220666a1ca" bpmnElement="sid-7fe567ec-a452-47fd-a6b4-d2912218fc63">
        <omgdc:Bounds x="-365.0" y="-150.00002" width="55.0" height="40.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-609e3e80-6953-48be-bcb1-ef119641fef0" bpmnElement="sid-26872e30-21fe-4f3c-99b8-e796ec7499e8">
        <omgdi:waypoint x="-415.0" y="-130.0"/>
        <omgdi:waypoint x="-365.0" y="-130.00002"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="shape-43fc14f8-626f-475c-a358-f1885fcd647c" bpmnElement="sid-0a2b5d6b-5bfd-4d5d-8ff9-f32a52ea0862">
        <omgdc:Bounds x="-225.0" y="-155.00002" width="65.0" height="50.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-9048913a-31f7-477d-8daa-bb9fff5a4568" bpmnElement="sid-7cc773e3-0c9d-4bf2-bee3-76e1a36fd032">
        <omgdi:waypoint x="-160.0" y="-130.00002"/>
        <omgdi:waypoint x="-75.00001" y="-130.00002"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="edge-cb8926d7-9008-4177-9f25-96713f54cedb" bpmnElement="sid-1c738e47-cf79-4074-ae28-b99acc4af09f">
        <omgdi:waypoint x="-310.0" y="-130.00002"/>
        <omgdi:waypoint x="-225.0" y="-130.00002"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="shape-1bd2e443-f667-4041-b78d-fe9c0db0ee95" bpmnElement="sid-40d76bec-d236-469b-b269-106c19bfc691">
        <omgdc:Bounds x="-75.0" y="-157.50002" width="60.000008" height="55.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-63955b29-9946-4ec9-a0ab-bad12ce2b52a" bpmnElement="sid-737c9616-400c-4f3e-aa8a-b396d70d1cd2">
        <omgdi:waypoint x="-44.999996" y="-102.500015"/>
        <omgdi:waypoint x="-45.0" y="-25.000029"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="edge-5391c294-5adf-45e0-84c2-1fdbda7af15a" bpmnElement="sid-3f2691b9-6747-4ebb-a94d-44c89857adf3">
        <omgdi:waypoint x="-160.0" y="-117.500015"/>
        <omgdi:waypoint x="-60.0" y="-17.50003"/>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>


选中文件右键,选择 View BPMN 就可以在 Idea 查看图形化的工作流
在这里插入图片描述
在这里插入图片描述

审批的过程需要特别提一下,如何根据审批结果(通过 / 不通过)自动走不同分支?其实用工作流引擎的「变量 + 条件表达式」就能轻松实现。在审批任务完成后,通过代码将审批结果(比如 “是否通过”)作为变量(如图中的result)传给引擎;接着在流程分支节点配置条件表达式(比如${result==true}),引擎会自动读取变量并计算表达式 , 满足条件就走对应分支,不满足则走其他分支,全程无需手动干预

6、代码

用一个 main 方法来演示全套请假流程

package com.luck.cloud.flowable.test;

import org.flowable.engine.ProcessEngine;
import org.flowable.engine.ProcessEngineConfiguration;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;

import java.util.HashMap;
import java.util.Map;

public class TestProcess {

    public static void main(String[] args) {
        // 1. 配置MySQL数据库连接
        ProcessEngineConfiguration cfg = ProcessEngineConfiguration
                .createStandaloneProcessEngineConfiguration() // 独立流程引擎配置(非内存)
                .setJdbcUrl("jdbc:mysql://localhost:3306/flowable?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true")
                .setJdbcUsername("root") // 替换为你的MySQL用户名
                .setJdbcPassword("root") // 替换为你的MySQL密码
                .setJdbcDriver("com.mysql.cj.jdbc.Driver");
                // 自动创建表结构(首次运行时使用,后续可注释)
                // .setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);

        // 2. 初始化流程引擎
        ProcessEngine processEngine = cfg.buildProcessEngine();

        try {
            // 3. 部署BPMN流程定义(使用你提供的test.bpmn20.xml)
            Deployment deployment = processEngine.getRepositoryService()
                    .createDeployment()
                    .addClasspathResource("processes/test.bpmn20.xml") // 确保文件在classpath下
                    .deploy();
            System.out.println("流程部署成功,部署ID: " + deployment.getId());

            // 4. 启动流程实例(流程ID为BPMN中的process id="test")
            RuntimeService runtimeService = processEngine.getRuntimeService();
            ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("test");
            System.out.println("流程实例启动成功,实例ID: " + processInstance.getId() + 
                               ", 流程定义ID: " + processInstance.getProcessDefinitionId());

            // 5. 处理"请假"用户任务(对应BPMN中的userTask)
            TaskService taskService = processEngine.getTaskService();
            while (true) {
                // 查询当前流程实例的待办任务(仅1个,若流程设计为串行)
                Task task = taskService.createTaskQuery()
                        .processInstanceId(processInstance.getId())
                        .singleResult();

                // 若没有待办任务,检查流程是否已结束
                if (task == null) {
                    // 查询流程实例是否仍在运行
                    ProcessInstance runningInstance = runtimeService.createProcessInstanceQuery()
                            .processInstanceId(processInstance.getId())
                            .singleResult();

                    if (runningInstance == null) {
                        System.out.println("所有任务已处理完毕,流程已结束");
                    } else {
                        System.out.println("当前无待办任务,但流程未结束(可能包含自动节点)");
                    }
                    break; // 退出循环
                }

                // 处理找到的任务
                System.out.println("找到待处理任务: " + task.getName() + ", 任务ID: " + task.getId());
                if(task.getName().equals("审批")){
                    Map<String, Object> variables = new HashMap<>();
                    variables.put("result", true);
                    taskService.complete(task.getId(), variables);
                }else{
                    taskService.complete(task.getId());
                }
                System.out.println("任务处理完成: " + task.getName());
            }

            // 6. 验证流程是否结束
            ProcessInstance finishedInstance = runtimeService.createProcessInstanceQuery()
                    .processInstanceId(processInstance.getId())
                    .singleResult();
            
            if (finishedInstance == null) {
                System.out.println("流程已成功结束");
            } else {
                System.out.println("流程仍在运行中");
            }

        } finally {
            // 关闭流程引擎
            processEngine.close();
        }
    }
}

执行一遍 main 方法,追踪日志可以看到工作流用到了哪些表。在所有任务结束后,执行过程中产生的数据都会存档到 his 前缀的表

三、学习交流

链接: Java-AI学习交流
一个人的精力是有限的,没有那么多的时间去探索新的事物,加入一个交流群就相当于增加了一个接触新事物、新想法、新机会的渠道,这就是交流群存在的意义,期待你的加入!

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐