Backend Development 15 min read

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.

macrozheng
macrozheng
macrozheng
Build a Simple Leave Approval Workflow with Flowable and Spring Boot

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&amp;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.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:

<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>
&lt;process&gt;

: defines the whole workflow.

&lt;startEvent&gt;

: start node.

&lt;endEvent&gt;

: end node.

&lt;userTask&gt;

: approval tasks with

flowable:assignee

attribute.

&lt;serviceTask&gt;

: custom service execution.

&lt;exclusiveGateway&gt;

: decision point.

&lt;sequenceFlow&gt;

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

JavaBPMNSpring BootWorkflow EngineFlowable
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.