Build a Simple Leave Approval Workflow with Flowable and Spring Boot
This tutorial walks through creating a basic leave‑approval process using the Flowable BPMN engine in a Spring Boot project, covering project setup, BPMN diagram design, XML definition, and Java code for deploying, visualizing, and handling task approvals and rejections.
1. Introduction to Flowable
Flowable is a lightweight Java‑based BPMN2.0 workflow engine that can deploy process definitions, start instances, query, and access runtime or historic data.
Another Java workflow engine is Activiti; mastering one makes the other easy.
2. Create Project
Start a Spring Boot project with Web and MySQL dependencies, then add the Flowable starter:
<code><dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring-boot-starter</artifactId>
<version>6.7.2</version>
</dependency></code>Configure the database in
application.yaml:
<code>spring:
datasource:
username: root
password: 123
url: jdbc:mysql:///flowable?serverTimezone=Asia/Shanghai&useSSL=false
</code>When the application starts, Flowable automatically creates the required tables.
3. Design BPMN Diagram
Use the Flowable BPMN visualizer plugin for IntelliJ IDEA to create a BPMN file
ask_for_leave.bpmn20.xmlunder
resources/processes. The diagram includes a start event, user tasks for leave request, team‑lead approval, manager approval, exclusive gateways for decision branches, a service task for failure notification, and an end event.
The XML definition looks like:
<code><process id="ask_for_leave" name="ask_for_leave" isExecutable="true">
<userTask id="leaveTask" name="请假" flowable:assignee="#{leaveTask}"/>
<userTask id="zuzhangTask" name="组长审核" flowable:assignee="#{zuzhangTask}"/>
<userTask id="managerTask" name="经理审核" flowable:assignee="#{managerTask}"/>
<exclusiveGateway id="managerJudgeTask"/>
<exclusiveGateway id="zuzhangJudeTask"/>
<endEvent id="endLeave" name="结束"/>
<startEvent id="startLeave" name="开始"/>
<!-- sequence flows omitted for brevity -->
<serviceTask id="sendMail" flowable:exclusive="true" name="发送失败提示"
isForCompensation="true"
flowable:class="org.javaboy.flowable.AskForLeaveFail"/>
</process>
</code> <process>: defines the whole workflow.
<startEvent>: start node.
<endEvent>: end node.
<userTask>: approval tasks with
flowable:assigneeattribute.
<serviceTask>: custom service execution.
<exclusiveGateway>: decision point.
<sequenceFlow>: connects nodes.
4. Implement API and Tests
4.1 View Process Diagram
REST endpoint to generate the current diagram image:
<code>@RestController
public class HelloController {
@Autowired RuntimeService runtimeService;
@Autowired TaskService taskService;
@Autowired RepositoryService repositoryService;
@Autowired ProcessEngine processEngine;
@GetMapping("/pic")
public void showPic(HttpServletResponse resp, String processId) throws Exception {
ProcessInstance pi = runtimeService.createProcessInstanceQuery()
.processInstanceId(processId)
.singleResult();
if (pi == null) return;
List<Execution> executions = runtimeService.createExecutionQuery()
.processInstanceId(processId)
.list();
List<String> activityIds = new ArrayList<>();
for (Execution exe : executions) {
activityIds.addAll(runtimeService.getActiveActivityIds(exe.getId()));
}
BpmnModel bpmnModel = repositoryService.getBpmnModel(pi.getProcessDefinitionId());
ProcessEngineConfiguration engconf = processEngine.getProcessEngineConfiguration();
ProcessDiagramGenerator diagramGenerator = engconf.getProcessDiagramGenerator();
InputStream in = diagramGenerator.generateDiagram(bpmnModel, "png",
activityIds, Collections.emptyList(),
engconf.getActivityFontName(), engconf.getLabelFontName(),
engconf.getAnnotationFontName(), engconf.getClassLoader(),
1.0, false);
OutputStream out = resp.getOutputStream();
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) != -1) {
out.write(buf, 0, len);
}
in.close(); out.close();
}
}
</code>4.2 Start a Process
<code>@Test
void askForLeave() {
HashMap<String, Object> map = new HashMap<>();
map.put("leaveTask", "1000");
ProcessInstance pi = runtimeService.startProcessInstanceByKey("ask_for_leave", map);
runtimeService.setVariable(pi.getId(), "name", "javaboy");
runtimeService.setVariable(pi.getId(), "reason", "休息一下");
runtimeService.setVariable(pi.getId(), "days", 10);
logger.info("创建请假流程 processId:{}", pi.getId());
}
</code>4.3 Submit to Team Lead
<code>@Test
void submitToZuzhang() {
List<Task> list = taskService.createTaskQuery()
.taskAssignee(staffId)
.orderByTaskId().desc()
.list();
for (Task task : list) {
Map<String, Object> map = new HashMap<>();
map.put("zuzhangTask", "90");
taskService.complete(task.getId(), map);
}
}
</code>4.4 Team‑Lead Approval
Approve:
<code>@Test
void zuZhangApprove() {
List<Task> list = taskService.createTaskQuery()
.taskAssignee(zuzhangId)
.orderByTaskId().desc()
.list();
for (Task task : list) {
Map<String, Object> map = new HashMap<>();
map.put("managerTask", managerId);
map.put("checkResult", "通过");
taskService.complete(task.getId(), map);
}
}
</code>Reject:
<code>@Test
void zuZhangReject() {
List<Task> list = taskService.createTaskQuery()
.taskAssignee(zuzhangId)
.orderByTaskId().desc()
.list();
for (Task task : list) {
Map<String, Object> map = new HashMap<>();
map.put("checkResult", "拒绝");
taskService.complete(task.getId(), map);
}
}
</code>4.5 Manager Approval
Approve:
<code>@Test
void managerApprove() {
List<Task> list = taskService.createTaskQuery()
.taskAssignee(managerId)
.orderByTaskId().desc()
.list();
for (Task task : list) {
Map<String, Object> map = new HashMap<>();
map.put("checkResult", "通过");
taskService.complete(task.getId(), map);
}
}
</code>Reject:
<code>@Test
void managerReject() {
List<Task> list = taskService.createTaskQuery()
.taskAssignee(managerId)
.orderByTaskId().desc()
.list();
for (Task task : list) {
Map<String, Object> map = new HashMap<>();
map.put("checkResult", "拒绝");
taskService.complete(task.getId(), map);
}
}
</code>4.6 Failure Handling
If either approver rejects, the process follows the
serviceTask sendMail, which invokes
org.javaboy.flowable.AskForLeaveFail:
<code>public class AskForLeaveFail implements JavaDelegate {
@Override
public void execute(DelegateExecution execution) {
System.out.println("请假失败。。。");
}
}
</code>5. Summary
This simple leave‑approval example demonstrates how to set up Flowable in a Spring Boot application, design BPMN diagrams, deploy processes, visualize runtime state, and implement task handling logic.
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.