Flowable 流程引擎从入门到精通:基础概念与实践
一、引言
在企业级应用开发中,我们常常会遇到这样的场景:需要为报销系统实现多级审批流程,为订单系统设计状态流转逻辑,或者为人力资源系统构建复杂的入职流程。传统的实现方式往往是在业务代码中硬编码流程逻辑,随着业务复杂度的提升,这种方式会导致代码变得冗长、难以维护,并且缺乏灵活性。
让我们看一个典型的问题场景:某企业的报销流程需要根据金额不同,分别由部门经理、财务总监和总经理审批。用传统方式实现可能会写出这样的代码:
// 传统方式实现报销审批流程(简化示例)
public void processReimbursement(ReimbursementForm form) {
// 1. 提交申请
saveForm(form);
// 2. 根据金额判断审批人
User approver = null;
if (form.getAmount() < 1000) {
approver = userService.getDeptManager(form.getApplicant().getDeptId());
} else if (form.getAmount() < 5000) {
approver = userService.getCfo();
} else {
approver = userService.getCeo();
}
// 3. 发送审批通知
notificationService.sendApprovalRequest(approver, form);
// 4. 等待审批结果
ApprovalResult result = waitForApproval(approver.getId(), form.getId());
// 5. 根据审批结果处理
if (result.isApproved()) {
if (form.getAmount() >= 5000) {
// 总经理审批后还需财务确认
User finance = userService.getFinanceStaff();
notificationService.sendApprovalRequest(finance, form);
result = waitForApproval(finance.getId(), form.getId());
if (result.isApproved()) {
processPayment(form);
} else {
rejectForm(form);
}
} else {
processPayment(form);
}
} else {
rejectForm(form);
}
}
这种实现方式存在明显的问题:
-
流程逻辑与业务代码高度耦合,难以修改和扩展
-
复杂的条件判断使代码变得臃肿,可读性差
-
无法跟踪流程执行状态,难以进行监控和优化
-
当业务规则变化时,需要修改核心代码,风险高
而使用 Flowable 流程引擎,我们可以用更优雅的方式实现相同的功能:
// 使用Flowable实现报销审批流程
public class ReimbursementProcessService {
@Autowired
private RuntimeService runtimeService;
@Autowired
private TaskService taskService;
public String startReimbursementProcess(ReimbursementForm form) {
// 准备流程变量
Map<String, Object> variables = new HashMap<>();
variables.put("applicantId", form.getApplicantId());
variables.put("amount", form.getAmount());
variables.put("reason", form.getReason());
// 启动流程实例
ProcessInstance instance = runtimeService.startProcessInstanceByKey(
"reimbursementProcess", variables);
return instance.getId();
}
public void approveTask(String taskId, String userId, boolean approved, String comments) {
// 验证任务归属
Task task = taskService.createTaskQuery()
.taskId(taskId)
.taskAssignee(userId)
.singleResult();
if (task == null) {
throw new IllegalArgumentException("任务不存在或无权处理");
}
// 设置审批结果
Map<String, Object> variables = new HashMap<>();
variables.put("approved", approved);
variables.put("comments", comments);
// 完成任务
taskService.complete(taskId, variables);
}
// 其他流程操作方法...
}
对应的 BPMN 2.0 流程定义文件(简化):
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL">
<process id="reimbursementProcess" name="报销审批流程">
<startEvent id="startEvent"/>
<!-- 提交申请 -->
<userTask id="submitTask" name="提交报销申请">
<extensionElements>
<flowable:formProperty id="amount" type="double" required="true"/>
<flowable:formProperty id="reason" type="string" required="true"/>
</extensionElements>
</userTask>
<!-- 根据金额判断审批级别 -->
<exclusiveGateway id="amountGateway" name="金额判断"/>
<!-- 部门经理审批 -->
<userTask id="deptManagerTask" name="部门经理审批">
<extensionElements>
<flowable:assignee>${deptManagerId}</flowable:assignee>
</extensionElements>
</userTask>
<!-- 财务总监审批 -->
<userTask id="cfoTask" name="财务总监审批">
<extensionElements>
<flowable:assignee>${cfoId}</flowable:assignee>
</extensionElements>
</userTask>
<!-- 总经理审批 -->
<userTask id="ceoTask" name="总经理审批">
<extensionElements>
<flowable:assignee>${ceoId}</flowable:assignee>
</extensionElements>
</userTask>
<!-- 财务确认 -->
<userTask id="financeTask" name="财务确认">
<extensionElements>
<flowable:assignee>${financeId}</flowable:assignee>
</extensionElements>
</userTask>
<!-- 结束事件 -->
<endEvent id="approveEnd" name="审批通过"/>
<endEvent id="rejectEnd" name="审批拒绝"/>
<!-- 连线定义 -->
<sequenceFlow id="flow1" sourceRef="startEvent" targetRef="submitTask"/>
<sequenceFlow id="flow2" sourceRef="submitTask" targetRef="amountGateway"/>
<!-- 金额条件分支 -->
<sequenceFlow id="flow3" sourceRef="amountGateway" targetRef="deptManagerTask">
<conditionExpression xsi:type="tFormalExpression">${amount < 1000}</conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow4" sourceRef="amountGateway" targetRef="cfoTask">
<conditionExpression xsi:type="tFormalExpression">${amount < 5000}</conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow5" sourceRef="amountGateway" targetRef="ceoTask">
<conditionExpression xsi:type="tFormalExpression">${amount >= 5000}</conditionExpression>
</sequenceFlow>
<!-- 其他连线... -->
</process>
</definitions>
通过对比可以看出,使用 Flowable 后:
-
流程逻辑与业务代码分离,流程变更只需修改 BPMN 文件
-
代码结构更清晰,核心业务逻辑得以简化
-
支持可视化流程设计,非技术人员也能参与流程优化
-
提供完整的流程监控和历史记录功能
-
便于实现复杂的流程模式,如并行处理、子流程等
作为一个在多个企业级项目中重度使用 Flowable 的开发者,我深知其强大之处。在接下来的内容中,我将深入讲解 Flowable 的各个方面,从基础概念到高级应用,帮助你快速掌握这一利器。
二、Flowable 技术架构解析
Flowable 是一个轻量级、高性能的开源流程引擎,其技术架构主要包含以下组件:
-
核心引擎:基于 Apache License 2.0 的开源实现,提供流程执行的核心能力
-
Modeler:可视化流程设计工具,支持 BPMN 2.0 标准
-
REST API:提供标准的 REST 接口,便于与其他系统集成
-
多种服务组件:包括 RepositoryService、RuntimeService、TaskService 等
-
扩展模块:支持 DMN 决策表、CMMN 案例管理等高级特性
与其他主流流程引擎相比,Flowable 具有更好的扩展性和社区活跃度,特别适合需要深度定制的企业级应用场景。
三、快速搭建开发环境
以下是基于 Spring Boot 的 Flowable 开发环境配置:
<dependencies>
<!-- Flowable核心依赖 -->
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring-boot-starter</artifactId>
<version>6.8.0</version>
</dependency>
<!-- Web支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 数据库支持 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- 测试依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
配置 application.properties:
# Flowable配置
flowable.database-schema-update=true
flowable.async-executor-activate=true
# 数据库配置
spring.datasource.url=jdbc:mysql://localhost:3306/flowable?useUnicode=true&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=yourpassword
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
四、流程定义详解
下面通过一个完整的请假流程示例,展示 Flowable 的流程定义方式:
@Configuration
public class ProcessEngineConfig {
@Autowired
private RepositoryService repositoryService;
@PostConstruct
public void deployProcess() {
repositoryService.createDeployment()
.addClasspathResource("processes/employee-leave.bpmn20.xml")
.name("员工请假流程")
.deploy();
}
}
BPMN 2.0 XML 定义:
<?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"
targetNamespace="http://flowable.org/processdef">
<process id="employeeLeaveProcess" name="员工请假流程" isExecutable="true">
<!-- 开始事件 -->
<startEvent id="startEvent" name="开始"/>
<!-- 提交请假申请任务 -->
<userTask id="submitLeaveRequest" name="提交请假申请">
<extensionElements>
<flowable:formProperty id="employeeId" name="员工ID" type="string" required="true"/>
<flowable:formProperty id="leaveType" name="请假类型" type="enum" required="true">
<flowable:value id="annual" name="年假"/>
<flowable:value id="sick" name="病假"/>
<flowable:value id="personal" name="事假"/>
</flowable:formProperty>
<flowable:formProperty id="startDate" name="开始日期" type="date" required="true"/>
<flowable:formProperty id="endDate" name="结束日期" type="date" required="true"/>
<flowable:formProperty id="reason" name="请假原因" type="string" required="true"/>
</extensionElements>
</userTask>
<!-- 连线 -->
<sequenceFlow id="flow1" sourceRef="startEvent" targetRef="submitLeaveRequest"/>
<!-- 部门经理审批 -->
<userTask id="deptManagerApproval" name="部门经理审批">
<extensionElements>
<flowable:formProperty id="approvalResult" name="审批结果" type="enum" required="true">
<flowable:value id="approved" name="同意"/>
<flowable:value id="rejected" name="拒绝"/>
</flowable:formProperty>
<flowable:formProperty id="comments" name="审批意见" type="string"/>
</extensionElements>
</userTask>
<sequenceFlow id="flow2" sourceRef="submitLeaveRequest" targetRef="deptManagerApproval"/>
<!-- 结束事件 -->
<endEvent id="endEvent" name="结束"/>
<sequenceFlow id="flow3" sourceRef="deptManagerApproval" targetRef="endEvent"/>
</process>
</definitions>
五、流程实例操作
以下是操作流程实例的核心 API 示例:
@Service
public class LeaveProcessService {
@Autowired
private RuntimeService runtimeService;
@Autowired
private TaskService taskService;
@Autowired
private HistoryService historyService;
/**
* 启动请假流程
*/
public String startLeaveProcess(String employeeId, String leaveType,
Date startDate, Date endDate, String reason) {
Map<String, Object> variables = new HashMap<>();
variables.put("employeeId", employeeId);
variables.put("leaveType", leaveType);
variables.put("startDate", startDate);
variables.put("endDate", endDate);
variables.put("reason", reason);
ProcessInstance instance = runtimeService.startProcessInstanceByKey(
"employeeLeaveProcess", variables);
return instance.getId();
}
/**
* 获取用户待办任务
*/
public List<Task> getPendingTasks(String assignee) {
return taskService.createTaskQuery()
.taskAssignee(assignee)
.active()
.list();
}
/**
* 完成任务
*/
public void completeTask(String taskId, Map<String, Object> variables) {
taskService.complete(taskId, variables);
}
/**
* 获取历史流程实例
*/
public List<HistoricProcessInstance> getHistoricProcessInstances() {
return historyService.createHistoricProcessInstanceQuery()
.processDefinitionKey("employeeLeaveProcess")
.finished()
.orderByProcessInstanceEndTime()
.desc()
.list();
}
}
六、Flowable 数据模型
Flowable 使用以下核心数据表存储流程相关信息:
-
ACT_RE_ 表*:存储流程定义和资源信息
- ACT_RE_DEPLOYMENT:部署信息表
- ACT_RE_PROCDEF:流程定义表
- ACT_RE_MODEL:模型信息表
-
ACT_RU_ 表*:存储流程运行时数据
- ACT_RU_EXECUTION:执行实例表
- ACT_RU_TASK:任务实例表
- ACT_RU_VARIABLE:变量表
- ACT_RU_EVENT_SUBSCR:事件订阅表
-
ACT_HI_ 表*:存储历史数据
- ACT_HI_PROCINST:历史流程实例表
- ACT_HI_TASKINST:历史任务实例表
- ACT_HI_VARINST:历史变量表
- ACT_HI_DETAIL:历史详情表
-
ACT_GE_ 表*:存储通用数据
- ACT_GE_BYTEARRAY:二进制资源表
- ACT_GE_PROPERTY:属性表
七、流程监控与管理
Flowable 提供多种方式监控和管理流程:
@Service
public class ProcessMonitorService {
@Autowired
private ManagementService managementService;
@Autowired
private RepositoryService repositoryService;
/**
* 获取流程引擎统计信息
*/
public Map<String, Object> getEngineStatistics() {
Map<String, Object> stats = new HashMap<>();
// 获取流程定义数量
long processDefinitionCount = repositoryService.createProcessDefinitionQuery().count();
// 获取正在运行的流程实例数量
long runningInstancesCount = runtimeService.createProcessInstanceQuery().active().count();
// 获取已完成的流程实例数量
long completedInstancesCount = historyService.createHistoricProcessInstanceQuery()
.finished()
.count();
stats.put("processDefinitionCount", processDefinitionCount);
stats.put("runningInstancesCount", runningInstancesCount);
stats.put("completedInstancesCount", completedInstancesCount);
return stats;
}
/**
* 暂停流程定义
*/
public void suspendProcessDefinition(String processDefinitionKey) {
repositoryService.suspendProcessDefinitionByKey(processDefinitionKey);
}
/**
* 激活流程定义
*/
public void activateProcessDefinition(String processDefinitionKey) {
repositoryService.activateProcessDefinitionByKey(processDefinitionKey);
}
}
八、最佳实践与常见问题
最佳实践
- 使用业务键 (Business Key) 关联业务数据
- 合理设置流程变量,避免存储大对象
- 使用事件监听器处理流程中的业务逻辑
- 定期清理历史数据,保持数据库性能
- 对复杂流程进行子流程拆分
常见问题
- 流程定义部署失败:检查 BPMN XML 格式是否正确
- 任务无法分配:确保 assignee 或 candidateUsers 设置正确
- 流程卡在某个节点:检查网关条件和连线设置
- 性能问题:优化数据库查询,避免频繁获取流程变量
- 历史数据丢失:检查数据库配置,确保历史级别设置正确
九、总结与展望
通过本文的学习,我们掌握了 Flowable 流程引擎的基本概念、环境搭建、流程定义和实例操作等核心技能。Flowable 不仅提供了强大的流程执行能力,还通过丰富的 API 和工具支持,使开发者能够轻松集成到各种企业应用中。
在下一篇文章中,我们将深入探讨 Flowable 的高级特性,包括动态流程变更、DMN 决策表、多实例任务等,进一步提升流程设计的灵活性和效率。