【Java】简单易懂的 flowable 工作流上手案例
目录
一、介绍
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学习交流
一个人的精力是有限的,没有那么多的时间去探索新的事物,加入一个交流群就相当于增加了一个接触新事物、新想法、新机会的渠道,这就是交流群存在的意义,期待你的加入!
更多推荐

所有评论(0)