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
<code><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></code>Configuration
application.properties (can be changed to YAML)
<code># 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=DEBUG</code>Notes
&nullCatalogMeansCurrent=true must be appended to the JDBC URL, otherwise Flowable cannot create tables automatically.
Create a database named
flowablebefore 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.
<code>@Configuration
public class FlowableConfig implements EngineConfigurationConfigurer<SpringProcessEngineConfiguration> {
@Override
public void configure(SpringProcessEngineConfiguration engineConfiguration) {
engineConfiguration.setActivityFontName("宋体");
engineConfiguration.setLabelFontName("宋体");
engineConfiguration.setAnnotationFontName("宋体");
}
}
</code>Result – simple response wrapper.
<code>@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;
}
}
</code>TaskVO – encapsulates approval list items.
<code>@Data
@NoArgsConstructor
@AllArgsConstructor
public class TaskVO {
private String id;
private String day;
private String name;
}
</code>LeaveController – REST endpoints for the leave workflow.
<code>@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.
}
</code>Process Diagram Generation
Method
genProcessDiagramgenerates a PNG diagram for a running process instance.
<code>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();
}
</code>After building, run the application at
http://localhost:8081and log in with
admin/admin.
Process Definition
Place the BPMN file
学生请假流程.bpmn20.xmlunder
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.
<code><?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></code>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‑swarmproject (⭐11K) with a 2024 video tutorial series covering Spring Cloud, microservices, Kubernetes, and more.
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.