SpringBoot整合flowable及其简单的使用实例教程

 更新时间:2025年12月04日 09:28:54   作者:不会编程的猿丁  
Flowable是一个轻量级的Java开源业务流程引擎,支持BPMN2.0规范,提供流程定义、部署、任务管理、事件处理、表单管理、监控和身份管理等功能,本文介绍了Flowable的核心组件、实现类、使用场景以及Spring Boot整合Flowable的步骤,包括配置和测试流程,感兴趣的朋友一起看看吧

1. 什么是flowable?

Flowable是一个使用Java编写的开源轻量级业务流程引擎,基于Apache 2.0协议,支持BPMN 2.0规范。它提供了从流程建模、执行到监控的全生命周期管理能力,能够处理流程流转相关的业务,如审批流、工作流等。Flowable由Activiti项目发展而来,在保持原有功能的基础上进行了优化和扩展。

核心功能

  • 流程定义与部署:支持BPMN 2.0流程定义的部署,可创建流程实例,进行查询以及访问运行中或历史的流程实例与相关数据。
  • 任务管理:提供任务管理功能,允许用户查询、处理和完成任务,任务可以是人工任务或系统任务。
  • 事件处理:支持事件处理机制,允许在流程执行过程中触发和监听各种事件,如开始事件、结束事件等。
  • 表单管理:可与表单设计器配合使用,在流程中定义动态表单,并挂载表单数据。
  • 实时监控与报告:提供丰富的监控功能,允许用户实时查看流程实例的执行状态、任务执行情况等。

核心组件

  • Flowable IDM:身份管理应用,提供单点登录认证功能,管理用户、组与权限。
  • Flowable Modeler:用于创建流程模型、表单、选择表与应用定义。
  • Flowable Task:运行时任务应用,提供启动流程实例、编辑任务表单等功能。
  • Flowable Admin:管理应用,查询BPMN、DMN等引擎,并提供修改流程实例等功能。
  • API服务:通过ProcessEngine与Flowable交互,提供工作流/BPM方法。

使用场景

  • 审批流程:如请假申请、报销审批等,可设计流程、部署定义、启动实例、处理任务。
  • 工作流管理:适用于复杂多变的业务场景,根据业务流程建模,处理任务。
  • 企业数字化转型:作为企业实现数字化转型的重要工具,提高业务处理的效率和灵活性。

特点

  • 轻量级:以JAR形式发布,可轻易加入任何Java环境。
  • 高性能:专注于提供高性能的业务流程管理解决方案。
  • 灵活性:能够灵活地嵌入到各种应用和服务中。
  • 扩展性:支持NoSQL、LDAP等,具有良好的扩展性。

2. flowable的重要实现类

流程引擎相关

  • ProcessEngine:Flowable的核心组件,负责解析BPMN 2.0流程定义文件,并执行其中的流程实例。它提供了丰富的API,方便开发者对流程实例进行各种操作,如启动、挂起、恢复、终止等。
  • ProcessEngineConfiguration:用于配置和初始化流程引擎,可以设置数据库连接、事务管理器、监听器等信息。
  • RuntimeService:提供运行时流程实例和执行的相关操作,如启动流程实例、查询运行时流程实例、获取流程变量等。
  • TaskService:与任务相关的服务类,用于管理任务的创建、分配、完成等操作。
  • RepositoryService:用于管理流程定义和部署,可以部署流程定义文件、查询流程定义、获取流程模型等。

事务管理相关

  • CommandExecutorImpl:命令执行器的实现类,负责执行具体的命令。
  • SpringTransactionInterceptor:Spring事务拦截器,用于处理事务的传播行为,确保流程操作在正确的事务上下文中执行。
  • TransactionContextInterceptor:事务上下文拦截器,用于管理事务上下文中的命令执行。

监听器相关

  • TaskListener:任务监听器接口,用于在任务的生命周期中监听特定事件,如任务创建、分配、完成等。
  • ExecutionListener:执行监听器接口,用于在流程执行的生命周期中监听特定事件。

流程图生成相关

  • ProcessDiagramGenerator:流程图生成器,用于根据BPMN模型生成流程图。

身份管理相关

  • Flowable IDM:身份管理应用,为所有Flowable UI应用提供单点登录认证功能,并且为拥有IDM管理员权限的用户提供了管理用户、组与权限的功能。

建模与任务相关

  • Flowable Modeler:基于Web的建模工具,可用于创建分别符合BPMN 2.0、CMMN和DMN规范的业务流程。
  • Flowable Task:运行时任务应用,提供了启动流程实例、编辑任务表单、完成任务,以及查询流程实例与任务的功能。

管理与监控相关

  • Flowable Admin:管理应用,让具有管理员权限的用户可以查询BPMN、DMN、Form及Content引擎,并提供了许多选项用于修改流程实例、任务、作业等。

3. SpringBoot整合flowable

maven导入flowable

maven中引入flowable-spring-boot-starter的相关依赖,我导入的是6.6.0版本,然后再导入数据库相关依赖(flowable需要配合数据库使用,且支持多种数据库,我这里使用MySQL8.0为例)

        <dependency>
            <groupId>org.flowable</groupId>
            <artifactId>flowable-spring-boot-starter</artifactId>
            <version>6.6.0</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.33</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

制作流程图

在idea中安装bpmn流程图的制作工具 Flowable BPMN visualizer

我这以IntelliJ IDEA 2024.3.1.1 (Ultimate Edition)为例

左上角四横线图片--File--Setting--Plugins--Installed,搜索 Flowable BPMN visualizer后点击安装

在resource目录下新建processes文件夹(flowable默认读这个文件夹下的流程图),新建文件xxx.bpmn20.xml(流程图文件名必须以.bpmn20.xml或者bpmn结尾),右键文件选择View BPMN

打开BPMN编辑页面就可以制作自己想要的流程图。

为了测试flowable我这里提供了一个简单的流程图

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:flowable="http://flowable.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.flowable.org/processdef">
  <!-- id为流程图名称要遵循xml的CNAME的起名规则  name为流程图中文名称 -->
  <process id="leave_flow" name="请假流程" isExecutable="true">
    <startEvent id="startEvent1"/>
    <!-- flowable:assignee="${userTask}"表示的意思是从哪个字段中获取申请人的名称   -->
    <userTask id="applyPerson" name="申请人" flowable:assignee="${userTask}">
      <extensionElements>
        <modeler:initiator-can-complete xmlns:modeler="http://flowable.org/modeler"><![CDATA[false]]></modeler:initiator-can-complete>
      </extensionElements>
    </userTask>
    <sequenceFlow id="sid-78321D10-B222-41F2-A655-2550B97688E0" sourceRef="startEvent1" targetRef="applyPerson"/>
    <userTask id="manager" name="经理">
      <extensionElements>
        <!--  这是流程监听器所指定的审批人,class要指定监听      -->
        <flowable:taskListener event="create" class="com.sd.taskListener.ManagerTaskListener"/>
      </extensionElements>
    </userTask>
    <sequenceFlow id="sid-25A43AFD-67C2-41F7-87DF-25826AA220F8" sourceRef="applyPerson" targetRef="manager"/>
    <userTask id="boss" name="老板">
      <extensionElements>
        <!--  这是流程监听器所指定的审批人,class要指定监听      -->
        <flowable:taskListener event="create" class="com.sd.taskListener.BossTaskListener"/>
      </extensionElements>
    </userTask>
    <endEvent id="sid-27AE7469-EC65-4EF0-B85E-3A658BD85EE3"/>
    <!--  这里表示指定两个流程监听器的处理动作,同意/驳回  -->
    <sequenceFlow id="refuse" name="驳回" sourceRef="manager" targetRef="applyPerson">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${result == "驳回"}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="pass" name="通过" sourceRef="manager" targetRef="boss">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${result == "同意"}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="refuse2" name="驳回" sourceRef="boss" targetRef="applyPerson">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${result == "驳回"}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="pass2" name="通过" sourceRef="boss" targetRef="sid-27AE7469-EC65-4EF0-B85E-3A658BD85EE3">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${result == "同意"}]]></conditionExpression>
    </sequenceFlow>
  </process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_leave_flow">
    <bpmndi:BPMNPlane bpmnElement="leave_flow" id="BPMNPlane_leave_flow">
      <bpmndi:BPMNShape bpmnElement="startEvent1" id="BPMNShape_startEvent1">
        <omgdc:Bounds height="30.0" width="30.0" x="100.0" y="163.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="applyPerson" id="BPMNShape_applyPerson">
        <omgdc:Bounds height="80.0" width="100.0" x="175.0" y="138.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="manager" id="BPMNShape_manager">
        <omgdc:Bounds height="80.0" width="100.0" x="345.0" y="138.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="boss" id="BPMNShape_boss">
        <omgdc:Bounds height="80.0" width="100.0" x="510.0" y="138.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="sid-27AE7469-EC65-4EF0-B85E-3A658BD85EE3" id="BPMNShape_sid-27AE7469-EC65-4EF0-B85E-3A658BD85EE3">
        <omgdc:Bounds height="28.0" width="28.0" x="735.0" y="164.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="refuse" id="BPMNEdge_refuse">
        <omgdi:waypoint x="395.0" y="217.95000000000002"/>
        <omgdi:waypoint x="395.0" y="291.0"/>
        <omgdi:waypoint x="225.0" y="291.0"/>
        <omgdi:waypoint x="225.0" y="217.95"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="pass" id="BPMNEdge_pass">
        <omgdi:waypoint x="444.9499999998897" y="178.0"/>
        <omgdi:waypoint x="509.99999999998465" y="178.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="pass2" id="BPMNEdge_pass2">
        <omgdi:waypoint x="609.9499999999675" y="178.0"/>
        <omgdi:waypoint x="735.0" y="178.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="refuse2" id="BPMNEdge_refuse2">
        <omgdi:waypoint x="560.0" y="138.0"/>
        <omgdi:waypoint x="560.0" y="76.0"/>
        <omgdi:waypoint x="225.0" y="76.0"/>
        <omgdi:waypoint x="225.0" y="138.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="sid-78321D10-B222-41F2-A655-2550B97688E0" id="BPMNEdge_sid-78321D10-B222-41F2-A655-2550B97688E0">
        <omgdi:waypoint x="129.9499984899576" y="178.0"/>
        <omgdi:waypoint x="175.0" y="178.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="sid-25A43AFD-67C2-41F7-87DF-25826AA220F8" id="BPMNEdge_sid-25A43AFD-67C2-41F7-87DF-25826AA220F8">
        <omgdi:waypoint x="274.95" y="178.0"/>
        <omgdi:waypoint x="344.99999999993565" y="178.0"/>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

查看流程图

设置SpringBoot相关配置

spring:
  application:
    name: springboot-flowable
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/flowable_test?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2b8&nullCatalogMeansCurrent=true
    username: root
    password: 123456
server:
  port: 3333
flowable:
  #关闭定时任务job
  async-executor-activate: false
  #对数据库表进行操作,如果表不存在则会新建
  database-schema-update: true

配置flowable相关配置

防止流程图中文乱码配置

@Configuration
public class FlowableConfig implements EngineConfigurationConfigurer<SpringProcessEngineConfiguration> {
    @Override
    public void configure(SpringProcessEngineConfiguration springProcessEngineConfiguration) {
        springProcessEngineConfiguration.setActivityFontName("宋体");
        springProcessEngineConfiguration.setLabelFontName("宋体");
        springProcessEngineConfiguration.setAnnotationFontName("宋体");
    }
}

配置流程监听器

经理监听器

public class ManagerTaskListener implements TaskListener {
    @Override
    public void notify(DelegateTask delegateTask) {
        delegateTask.setAssignee("经理");
        System.out.println("经理收到了流程");
    }
}

配置老板监听器

public class BossTaskListener implements TaskListener {
    @Override
    public void notify(DelegateTask delegateTask) {
        delegateTask.setAssignee("老板");
        System.out.println("老板收到了流程");
    }
}

编写flowable相关操作

我这里演示发起流程,查看流程,获取审批流程,审批同意,驳回以及查看流程图

其他操作跟这些都大差不大,可自行扩展

package com.sd.controller;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.engine.*;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.image.ProcessDiagramGenerator;
import org.flowable.task.api.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.*;
/**
 * @author sd
 * @create 2025-06-17-下午 4:06
 */
@RestController
@RequestMapping("/flowable")
public class FlowableController {
    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private ProcessEngine processEngine;
    /**
     * 发起请假
     *
     * @param userId    用户Id
     * @param days     请假天数
     * @param desc 描述
     */
    @GetMapping(value = "/add")
    public String addExpense(String userId,Integer days,String desc) {
        //设置流程参数,用户名等等
        HashMap<String, Object> map = new HashMap<>();
        map.put("userTask", userId);
        map.put("days", days);
        map.put("desc", desc);
        //启动一个流程
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("leave_flow", map);
        //生成一个任务
        Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
        //第一步任务流程完成后,会按照流程图自动分配给下一个流程
        taskService.complete(task.getId());
        return "提交成功\n流程Id为:" + processInstance.getId()
                + "\n任务id: " + task.getId()
                + "\n请假人为:" +userId
                + "\n请假天数为:" + days
                + "\n描述为:" + desc;
    }
    /**
     * 查询请假流程
     *
     * @param userId 查询人
     *
     * */
    @GetMapping("findTask")
    public String findTask(String userId) {
        //从引擎中获取任务
        TaskService taskService = processEngine.getTaskService();
        Task task = taskService.createTaskQuery().taskAssignee(userId).singleResult();
        return userId + "的任务ID:"+task.getId();
    }
    /**
     * 获取审批管理列表
     *
     * @param userId 查询人
     *
     */
    @GetMapping(value = "/list")
    public Map<String,String> list(String userId) {
        Map<String,String> result = new HashMap<>();
        List<Task> tasks = taskService.createTaskQuery().taskAssignee(userId).orderByTaskCreateTime().desc().list();
        for (Task task : tasks) {
            result.put(task.getName(),task.getId());
        }
        return result;
    }
    /**
     * 批准
     *
     * @param taskId 任务ID
     */
    @GetMapping(value = "apply")
    public String apply(String taskId) {
        //获得任务
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (task == null) {
            throw new RuntimeException("流程不存在");
        }
        //通过审核
        HashMap<String, Object> map = new HashMap<>();
        map.put("result", "同意");
        taskService.complete(taskId, map);
        return  "任务ID:" + taskId + "---批准!";
    }
    /**
     * 驳回
     */
    @GetMapping(value = "reject")
    public String reject(String taskId) {
        //获得任务
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (task == null) {
            throw new RuntimeException("流程不存在");
        }
        HashMap<String, Object> map = new HashMap<>();
        map.put("result", "驳回");
        taskService.complete(taskId, map);
        return  "任务ID:" + taskId + "---驳回!";
    }
    /**
     * 生成流程图
     *
     * @param taskId 任务ID
     */
    @GetMapping(value = "processDiagram")
    public void genProcessDiagram(HttpServletResponse httpServletResponse, String taskId) throws Exception {
        ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(taskId).singleResult();
        //流程走完的不显示图
        if (pi == null) {
            return;
        }
        Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult();
        //使用流程实例ID,查询正在执行的执行对象表,返回流程实例对象
        String InstanceId = task.getProcessInstanceId();
        List<Execution> executions = runtimeService
                .createExecutionQuery()
                .processInstanceId(InstanceId)
                .list();
        //得到正在执行的flowable的Id
        List<String> flowableIds = new ArrayList<>();
        List<String> flows = new ArrayList<>();
        for (Execution exe : executions) {
            List<String> ids = runtimeService.getActiveActivityIds(exe.getId());
            flowableIds.addAll(ids);
        }
        //获取流程图
        BpmnModel bpmnModel = repositoryService.getBpmnModel(pi.getProcessDefinitionId());
        ProcessEngineConfiguration engconf = processEngine.getProcessEngineConfiguration();
        ProcessDiagramGenerator diagramGenerator = engconf.getProcessDiagramGenerator();
        InputStream in = diagramGenerator.generateDiagram(bpmnModel, "png", flowableIds, Collections.emptyList(), engconf.getActivityFontName(), engconf.getLabelFontName(), engconf.getAnnotationFontName(), null, 1.0, false);
        OutputStream out = null;
        byte[] buf = new byte[10240];
        int legth = 0;
        try {
            httpServletResponse.setContentType("image/png"); // 设置响应类型为图片
            out = httpServletResponse.getOutputStream();
            while ((legth = in.read(buf)) != -1) {
                out.write(buf, 0, legth);
            }
        } catch (IOException e) {
            System.out.println("生成流程图失败");
            throw new RuntimeException(e);
        } finally {
            if (in != null) {
                in.close();
            }
            if (out != null) {
                out.close();
            }
        }
    }
}

4. 测试flowable

如果在yml中配置了 database-schema-update: true 那么初次启动项目会在控制台打印的日志中看到很多建表语句,flowable会对不存在的表进行创建

我没有写前端页面就全部使用postman进行测试,查看流程图操作我会附着在发起,审批同意,驳回操作后面

发起流程

查询对应流程

这里只返回了查询人和任务ID,其他操作可自行进行扩展

获得审批列表

这里只返回了审批人以及流程ID

审批通过

审批通过后会执行到流程图设置的下一步

驳回

驳回后会返回到流程图设置的上一步

先获取老板的任务ID

注意:为什么在经理审批时不用回去经理的流程ID?因为经理是提交申请后的第一步,提交申请我直接打印了任务ID,直接使用就行

进行驳回

驳回后又返回到了第一步操作

到此这篇关于SpringBoot整合flowable及其简单的使用实例教程的文章就介绍到这了,更多相关SpringBoot flowable使用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 如何用Java注解和反射实现依赖注入

    如何用Java注解和反射实现依赖注入

    这篇文章主要介绍了如何用Java注解和反射实现依赖注入,对依赖注入感兴趣的同学,可以实验一下
    2021-05-05
  • idea不使用maven如何将项目打包

    idea不使用maven如何将项目打包

    使用IDEA 2021版本,不借助Maven进行打WAR包的步骤是:首先点击Project Structure,然后点击Artifacts,接着选择需要的打包类型,设置好包的名称,最后进行打包,这种方法适用于不使用Maven进行项目管理的情况
    2024-09-09
  • SpringBoot/Spring AOP默认动态代理方式实例详解

    SpringBoot/Spring AOP默认动态代理方式实例详解

    这篇文章主要给大家介绍了关于SpringBoot/Spring AOP默认动态代理方式的相关资料,Spring AOP是一款基于Java的AOP框架,其中默认采用动态代理方式实现AOP功能,本文将详细介绍动态代理的实现原理和使用方法,需要的朋友可以参考下
    2023-03-03
  • Android开发Activity管理工具类的操作方法

    Android开发Activity管理工具类的操作方法

    这篇文章主要介绍了Android开发Activity管理工具类,下面是一个完整的Activity管理工具类实现,可以帮助你管理应用中的所有Activity,方便一键退出应用、获取当前Activity等操作,需要的朋友可以参考下
    2021-02-02
  • java 线程池的实现方法

    java 线程池的实现方法

    在本篇文章里小编给大家整理了关于java 线程池的实现方法,有兴趣的朋友们可以学习参考下。
    2020-02-02
  • Mybatis批量插入和批量更新失败问题

    Mybatis批量插入和批量更新失败问题

    这篇文章主要介绍了Mybatis批量插入和批量更新失败问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-08-08
  • 带你入门Java的方法

    带你入门Java的方法

    这篇文章主要介绍了java基础之方法详解,文中有非常详细的代码示例,对正在学习java基础的小伙伴们有非常好的帮助,需要的朋友可以参考下
    2021-07-07
  • Netty实现简易版的RPC框架过程详解

    Netty实现简易版的RPC框架过程详解

    这篇文章主要为大家介绍了Netty实现简易版的RPC框架过程详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-02-02
  • Spring myBatis数据库连接异常问题及解决

    Spring myBatis数据库连接异常问题及解决

    这篇文章主要介绍了Spring myBatis数据库连接异常问题及解决,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-06-06
  • 分析JVM的组成结构

    分析JVM的组成结构

    JVM(虚拟机):指以软件的方式模拟具有完整硬件系统功能、运行在一个完全隔离环境中的完整计算机系统 ,是物理机的软件实现。JVM和VMware,Virtual Box等虚拟机一样,都是运行在操作系统之上的计算机系统
    2021-06-06

最新评论