Build a Spring Boot + Flowable Leave Approval System: Step‑by‑Step Guide
This tutorial walks through setting up a Spring Boot 2.7.5 project with Flowable 6.6.0 and MySQL8, covering environment setup, Maven dependencies, application properties, core Java classes, REST endpoints, process diagram generation, BPMN definition, database tables, and demo screenshots for a complete leave‑approval workflow.
Program Related
Environment
jdk1.8
maven3
SpringBoot 2.7.5
flowable 6.6.0
MySQL8
Dependencies
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<flowable.version>6.6.0</flowable.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.5</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- flowable jars -->
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring-boot-starter</artifactId>
<version>${flowable.version}</version>
</dependency>
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring-boot-starter-ui-idm</artifactId>
<version>${flowable.version}</version>
</dependency>
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring-boot-starter-ui-modeler</artifactId>
<version>${flowable.version}</version>
</dependency>
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-bpmn-layout</artifactId>
<version>${flowable.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.31</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>Configuration
application.properties (can be changed to YAML)
# Port
server.port=8081
# UI related
flowable.idm.app.admin.user-id=admin
flowable.idm.app.admin.password=admin
flowable.idm.app.admin.first-name=xxx
flowable.idm.app.admin.last-name=xxx
flowable.database-schema-update=true # create tables on first run
# Disable async job executor
flowable.async-executor-activate=false
# Database
spring.datasource.url=jdbc:mysql://xxxx:3306/flowable-test?autoReconnect=true&useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
# Logging
logging.level.org.flowable=DEBUGNotes
&nullCatalogMeansCurrent=true must be appended to the JDBC URL, otherwise Flowable cannot create tables automatically.
Create a database named flowable before starting; Flowable will generate the required tables.
After the project starts, the login credentials are user-id: admin, password: admin.
Tables
Many tables are auto‑created; the exact count is not listed.
Classes
FlowableConfig – prevents Chinese characters from becoming garbled in generated diagrams.
@Configuration
public class FlowableConfig implements EngineConfigurationConfigurer<SpringProcessEngineConfiguration> {
@Override
public void configure(SpringProcessEngineConfiguration engineConfiguration) {
engineConfiguration.setActivityFontName("宋体");
engineConfiguration.setLabelFontName("宋体");
engineConfiguration.setAnnotationFontName("宋体");
}
}Result – simple response wrapper.
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result {
private boolean flag;
private String message;
private Object data;
public Result(boolean flag, String message) {
this.flag = flag;
this.message = message;
}
}TaskVO – encapsulates approval list items.
@Data
@NoArgsConstructor
@AllArgsConstructor
public class TaskVO {
private String id;
private String day;
private String name;
}LeaveController – REST endpoints for the leave workflow.
@RestController
@RequestMapping("/leave")
@Slf4j
public class LeaveController {
@Autowired private RuntimeService runtimeService;
@Autowired private TaskService taskService;
@Autowired private RepositoryService repositoryService;
@Autowired private ProcessEngine processEngine;
@PostMapping("add/{day}/{studentUser}")
public Result sub(@PathVariable("day") Integer day, @PathVariable("studentUser") String studentUser) {
Map<String, Object> map = new HashMap<>();
map.put("day", day);
map.put("studentName", studentUser);
ProcessInstance pi = runtimeService.startProcessInstanceByKey("stuLeave", map);
Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult();
taskService.complete(task.getId());
return new Result(true, "提交成功.流程Id为:" + pi.getId());
}
// teacher list, teacher approve/reject, dean list, dean approve/reject, re‑apply, process diagram, etc.
}Process Diagram Generation
Method genProcessDiagram generates a PNG diagram for a running process instance.
public void genProcessDiagram(HttpServletResponse response, @PathVariable("taskId") String taskId) throws Exception {
ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(taskId).singleResult();
if (pi == null) return;
Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult();
String instanceId = task.getProcessInstanceId();
List<Execution> executions = runtimeService.createExecutionQuery().processInstanceId(instanceId).list();
List<String> activityIds = new ArrayList<>();
for (Execution exe : executions) {
activityIds.addAll(runtimeService.getActiveActivityIds(exe.getId()));
}
BpmnModel bpmnModel = repositoryService.getBpmnModel(pi.getProcessDefinitionId());
ProcessEngineConfiguration conf = processEngine.getProcessEngineConfiguration();
ProcessDiagramGenerator generator = conf.getProcessDiagramGenerator();
InputStream in = generator.generateDiagram(bpmnModel, "png", activityIds, new ArrayList<>(),
conf.getActivityFontName(), conf.getLabelFontName(), conf.getAnnotationFontName(),
conf.getClassLoader(), 1.0, true);
response.setHeader("Content-Type", "image/png;charset=utf-8");
OutputStream out = response.getOutputStream();
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) != -1) {
out.write(buf, 0, len);
}
in.close();
out.close();
}After building, run the application at http://localhost:8081 and log in with admin/admin.
Process Definition
Place the BPMN file 学生请假流程.bpmn20.xml under resources/processes. The XML defines start event, user tasks (apply, teacherPass, principalPass), an exclusive gateway that judges whether the leave exceeds two days, and the end event with conditional sequence flows.
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" ...>
<process id="stuLeave" name="学生请假流程" isExecutable="true">
<startEvent id="startEvent1" name="开始"/>
<userTask id="apply" name="请假申请" flowable:assignee="${studentName}"/>
<userTask id="teacherPass" name="辅导员审批" flowable:candidateGroups="a"/>
<exclusiveGateway id="judgeTask" name="判断是否大于2天"/>
<userTask id="principalPass" name="院长审批" flowable:candidateGroups="b"/>
<endEvent id="over" name="结束"/>
<sequenceFlow id="flowBeg" sourceRef="startEvent1" targetRef="apply"/>
<sequenceFlow id="sid-F3C3133B-68E1-4D1B-B06B-761EDD44E9F6" sourceRef="apply" targetRef="teacherPass"/>
<sequenceFlow id="TeacherNotPassFlow" sourceRef="teacherPass" targetRef="apply">
<conditionExpression xsi:type="tFormalExpression">${outcome=='驳回'}</conditionExpression>
</sequenceFlow>
<sequenceFlow id="teacherPassFlow" sourceRef="teacherPass" targetRef="judgeTask">
<conditionExpression xsi:type="tFormalExpression">${outcome=="通过"}</conditionExpression>
</sequenceFlow>
<sequenceFlow id="judgeLess" sourceRef="judgeTask" targetRef="over">
<conditionExpression xsi:type="tFormalExpression">${day<=2}</conditionExpression>
</sequenceFlow>
<sequenceFlow id="judgeMore" sourceRef="judgeTask" targetRef="principalPass">
<conditionExpression xsi:type="tFormalExpression">${day>2}</conditionExpression>
</sequenceFlow>
...
</process>
</definitions>Demo Screenshots
Submission, teacher approval list, process diagrams, dean approval/rejection, re‑apply, etc.
Database Table Overview
ACT_RE – repository tables (process definitions, resources)
ACT_RU – runtime tables (process instances, tasks, variables)
ACT_HI – history tables (completed instances, historic tasks)
ACT_GE – general tables used by various components
ACT_ID – identity tables (users, groups)
Author: 什人 Source: juejin.cn/post/7387380647269253146
Additional resources: the open‑source mall‑swarm project (⭐11K) with a 2024 video tutorial series covering Spring Cloud, microservices, Kubernetes, and more.
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.
macrozheng
Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.
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.
