How to Integrate Flowable BPM with Spring Boot 3: A Step‑by‑Step Guide
This article provides a comprehensive, code‑driven tutorial on integrating the Flowable BPM engine with Spring Boot 3, covering dependency setup, YAML configuration, thread‑pool tuning, process definition using the Flowable UI, deployment, starting processes, querying deployments and instances, handling tasks, completing and rejecting workflows, all illustrated with practical examples and screenshots.
1. Flowable Introduction
Flowable is a lightweight open‑source BPM and workflow engine that supports BPMN 2.0. It provides process definition, execution, task management and history query.
2. Spring Boot 3 Integration
Environment: JDK 21, Spring Boot 3.4.1, Flowable 7.1.0.
1. Add Dependency
<!-- Flowable starter dependency -->
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring-boot-starter</artifactId>
<version>7.1.0</version>
</dependency>2. yml Configuration
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/flowable?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: 123456
type: com.alibaba.druid.pool.DruidDataSource
flowable:
async-executor-activate: true
database-schema-update: true3. Thread‑Pool Configuration
@Configuration
public class ThreadPoolTaskConfig {
@Bean("applicationTaskExecutor")
public ThreadPoolTaskExecutor applicationTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
int core = Runtime.getRuntime().availableProcessors();
executor.setCorePoolSize(core);
executor.setMaxPoolSize(core * 2 + 1);
executor.setKeepAliveSeconds(120);
executor.setQueueCapacity(120);
executor.setThreadNamePrefix("thread-default-execute");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
return executor;
}
}3. Process Definition
Use the Flowable UI (docker run -p 9096:8080 -d --name flow flowable/flowable-ui:6.8.0) to design a process, set user tasks, assign a fixed user (user1), and connect the nodes.
4. Deploy Process
Place the exported XML file in the resources directory and deploy it programmatically.
@Resource
private RepositoryService repositoryService;
...
public void initFlow() {
ClassPathResource bpmnFolder = new ClassPathResource("bpmn/");
File[] files = bpmnFolder.getFile().listFiles((dir, name) -> name.endsWith(".bpmn20.xml"));
if (files != null && files.length > 0) {
DeploymentBuilder deploymentBuilder = repositoryService.createDeployment();
for (File file : files) {
deploymentBuilder.addInputStream(file.getName(), file.toURI().toURL().openStream());
}
deploymentBuilder.deploy();
}
}5. Start Process
@GetMapping("/startFlow")
@Transactional
public String startFlow(@RequestParam("key") String key) {
Map<String, Object> vars = Map.of(
"businessType", "业务类型(业务审批、请假、出差等)",
"day", 1,
"refuseFlag", false
);
String userId = SysConstan.USER_ID;
String businessKey = "PO00001";
identityService.setAuthenticatedUserId(userId);
ProcessInstance pi = runtimeService.startProcessInstanceByKey(key, businessKey, vars);
identityService.setAuthenticatedUserId(null);
return pi.getId();
}6. Query Deployed Processes
@GetMapping("/queryAllDeployedProcesses")
public List<JSONObject> queryAllDeployedProcesses() {
List<ProcessDefinition> defs = repositoryService.createProcessDefinitionQuery()
.orderByProcessDefinitionKey().asc()
.latestVersion()
.list();
List<JSONObject> result = new ArrayList<>();
for (ProcessDefinition d : defs) {
JSONObject o = new JSONObject();
o.put("id", d.getId());
o.put("key", d.getKey());
o.put("name", d.getName());
o.put("version", d.getVersion());
result.add(o);
}
return result;
}7. Query All Process Instances
@GetMapping("queryAllprocess")
public List<JSONObject> queryAllprocess() {
List<HistoricProcessInstance> instances = historyService.createHistoricProcessInstanceQuery()
.orderByProcessInstanceStartTime().asc()
.list();
List<JSONObject> list = new ArrayList<>();
for (HistoricProcessInstance pi : instances) {
JSONObject json = new JSONObject();
json.put("status", pi.getEndTime() == null ? "审批中" : "审批完成");
json.put("id", pi.getProcessDefinitionId());
json.put("processInstanceId", pi.getId());
json.put("startUser", pi.getStartUserId());
json.put("key", pi.getProcessDefinitionKey());
json.put("businessKey", pi.getBusinessKey());
json.put("name", pi.getProcessDefinitionName());
json.put("deleteReason", pi.getDeleteReason());
json.put("startTime", DateUtil.format(pi.getStartTime(), "yyyy-MM-dd HH:mm:ss"));
json.put("endTime", pi.getEndTime() != null ? DateUtil.format(pi.getEndTime(), "yyyy-MM-dd HH:mm:ss") : "");
List<HistoricVariableInstance> vars = historyService.createHistoricVariableInstanceQuery()
.processInstanceId(pi.getId())
.list();
for (HistoricVariableInstance v : vars) {
json.put(v.getVariableName(), v.getValue());
if ("refuseFlag".equals(v.getVariableName()) && Boolean.TRUE.equals(v.getValue())) {
json.put("status", "审批驳回");
}
}
list.add(json);
}
return list;
}8. My Tasks
@GetMapping("/allTasks")
public List<JSONObject> getTasks() {
List<Task> tasks = taskService.createTaskQuery().list();
List<JSONObject> result = new ArrayList<>();
for (Task t : tasks) {
JSONObject json = new JSONObject();
json.put("id", t.getId());
json.put("name", t.getName());
json.put("user", t.getAssignee());
json.put("processDefinitionId", t.getProcessDefinitionId());
json.put("processInstanceId", t.getProcessInstanceId());
json.putAll(taskService.getVariables(t.getId()));
result.add(json);
}
return result;
}9. Complete Task
@GetMapping("/testComplete")
@Transactional
public boolean testComplete(@RequestParam("id") String taskId) {
Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
if (task == null) {
return false;
}
String processInstanceId = task.getProcessInstanceId();
taskService.addComment(taskId, processInstanceId, "备注信息test");
taskService.complete(taskId);
return processInstanceFinished(processInstanceId);
}10. Reject Process
@GetMapping("stopFlow")
@Transactional
public void stopFlow(@RequestParam("id") String processInstanceId) {
runtimeService.setVariable(processInstanceId, "refuseFlag", true);
runtimeService.deleteProcessInstance(processInstanceId, "驳回任务备注原因");
}Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Top Architect
Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
