Backend Development 20 min read

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.

macrozheng
macrozheng
macrozheng
Build a Spring Boot + Flowable Leave Approval System: Step‑by‑Step Guide

Program Related

Environment

jdk1.8

maven3

SpringBoot 2.7.5

flowable 6.6.0

MySQL8

Dependencies

<code>&lt;properties&gt;
  &lt;maven.compiler.source&gt;8&lt;/maven.compiler.source&gt;
  &lt;maven.compiler.target&gt;8&lt;/maven.compiler.target&gt;
  &lt;project.build.sourceEncoding&gt;UTF-8&lt;/project.build.sourceEncoding&gt;
  &lt;flowable.version&gt;6.6.0&lt;/flowable.version&gt;
&lt;/properties&gt;

&lt;parent&gt;
  &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
  &lt;artifactId&gt;spring-boot-starter-parent&lt;/artifactId&gt;
  &lt;version&gt;2.7.5&lt;/version&gt;
  &lt;relativePath/&gt;
&lt;/parent&gt;

&lt;dependencies&gt;
  &lt;dependency&gt;
    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
    &lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;
  &lt;/dependency&gt;
  &lt;!-- flowable jars --&gt;
  &lt;dependency&gt;
    &lt;groupId&gt;org.flowable&lt;/groupId&gt;
    &lt;artifactId&gt;flowable-spring-boot-starter&lt;/artifactId&gt;
    &lt;version&gt;${flowable.version}&lt;/version&gt;
  &lt;/dependency&gt;
  &lt;dependency&gt;
    &lt;groupId&gt;org.flowable&lt;/groupId&gt;
    &lt;artifactId&gt;flowable-spring-boot-starter-ui-idm&lt;/artifactId&gt;
    &lt;version&gt;${flowable.version}&lt;/version&gt;
  &lt;/dependency&gt;
  &lt;dependency&gt;
    &lt;groupId&gt;org.flowable&lt;/groupId&gt;
    &lt;artifactId&gt;flowable-spring-boot-starter-ui-modeler&lt;/artifactId&gt;
    &lt;version&gt;${flowable.version}&lt;/version&gt;
  &lt;/dependency&gt;
  &lt;dependency&gt;
    &lt;groupId&gt;org.flowable&lt;/groupId&gt;
    &lt;artifactId&gt;flowable-bpmn-layout&lt;/artifactId&gt;
    &lt;version&gt;${flowable.version}&lt;/version&gt;
  &lt;/dependency&gt;
  &lt;dependency&gt;
    &lt;groupId&gt;mysql&lt;/groupId&gt;
    &lt;artifactId&gt;mysql-connector-java&lt;/artifactId&gt;
    &lt;version&gt;8.0.31&lt;/version&gt;
  &lt;/dependency&gt;
  &lt;dependency&gt;
    &lt;groupId&gt;org.mybatis.spring.boot&lt;/groupId&gt;
    &lt;artifactId&gt;mybatis-spring-boot-starter&lt;/artifactId&gt;
    &lt;version&gt;2.2.2&lt;/version&gt;
  &lt;/dependency&gt;
  &lt;dependency&gt;
    &lt;groupId&gt;org.projectlombok&lt;/groupId&gt;
    &lt;artifactId&gt;lombok&lt;/artifactId&gt;
  &lt;/dependency&gt;
  &lt;dependency&gt;
    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
    &lt;artifactId&gt;spring-boot-starter-test&lt;/artifactId&gt;
    &lt;scope&gt;test&lt;/scope&gt;
  &lt;/dependency&gt;
&lt;/dependencies&gt;</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

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.

<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

genProcessDiagram

generates 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: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.

<code>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" ...&gt;
  &lt;process id="stuLeave" name="学生请假流程" isExecutable="true"&gt;
    &lt;startEvent id="startEvent1" name="开始"/&gt;
    &lt;userTask id="apply" name="请假申请" flowable:assignee="${studentName}"/&gt;
    &lt;userTask id="teacherPass" name="辅导员审批" flowable:candidateGroups="a"/&gt;
    &lt;exclusiveGateway id="judgeTask" name="判断是否大于2天"/&gt;
    &lt;userTask id="principalPass" name="院长审批" flowable:candidateGroups="b"/&gt;
    &lt;endEvent id="over" name="结束"/&gt;
    &lt;sequenceFlow id="flowBeg" sourceRef="startEvent1" targetRef="apply"/&gt;
    &lt;sequenceFlow id="sid-F3C3133B-68E1-4D1B-B06B-761EDD44E9F6" sourceRef="apply" targetRef="teacherPass"/&gt;
    &lt;sequenceFlow id="TeacherNotPassFlow" sourceRef="teacherPass" targetRef="apply"&gt;
      &lt;conditionExpression xsi:type="tFormalExpression"&gt;${outcome=='驳回'}&lt;/conditionExpression&gt;
    &lt;/sequenceFlow&gt;
    &lt;sequenceFlow id="teacherPassFlow" sourceRef="teacherPass" targetRef="judgeTask"&gt;
      &lt;conditionExpression xsi:type="tFormalExpression"&gt;${outcome=="通过"}&lt;/conditionExpression&gt;
    &lt;/sequenceFlow&gt;
    &lt;sequenceFlow id="judgeLess" sourceRef="judgeTask" targetRef="over"&gt;
      &lt;conditionExpression xsi:type="tFormalExpression"&gt;${day&lt;=2}&lt;/conditionExpression&gt;
    &lt;/sequenceFlow&gt;
    &lt;sequenceFlow id="judgeMore" sourceRef="judgeTask" targetRef="principalPass"&gt;
      &lt;conditionExpression xsi:type="tFormalExpression"&gt;${day&gt;2}&lt;/conditionExpression&gt;
    &lt;/sequenceFlow&gt;
    ...
  &lt;/process&gt;
&lt;/definitions&gt;</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‑swarm

project (⭐11K) with a 2024 video tutorial series covering Spring Cloud, microservices, Kubernetes, and more.

JavaworkflowBPMNSpring BootMySQLREST APIFlowable
macrozheng
Written by

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.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.