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:
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring-boot-starter</artifactId>
<version>6.7.2</version>
</dependency>Configure the database in application.yaml:
spring:
datasource:
username: root
password: 123
url: jdbc:mysql:///flowable?serverTimezone=Asia/Shanghai&useSSL=falseWhen 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.xml under 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:
<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> <process>: defines the whole workflow. <startEvent>: start node. <endEvent>: end node. <userTask>: approval tasks with flowable:assignee attribute. <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:
@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();
}
}4.2 Start a Process
@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());
}4.3 Submit to Team Lead
@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);
}
}4.4 Team‑Lead Approval
Approve:
@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);
}
}Reject:
@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);
}
}4.5 Manager Approval
Approve:
@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);
}
}Reject:
@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);
}
}4.6 Failure Handling
If either approver rejects, the process follows the
serviceTask sendMail, which invokes org.javaboy.flowable.AskForLeaveFail:
public class AskForLeaveFail implements JavaDelegate {
@Override
public void execute(DelegateExecution execution) {
System.out.println("请假失败。。。");
}
}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.
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.
