java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > SpringBoot flowable使用

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

作者:不会编程的猿丁

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

1. 什么是flowable?

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

核心功能

核心组件

使用场景

特点

2. flowable的重要实现类

流程引擎相关

事务管理相关

监听器相关

流程图生成相关

身份管理相关

建模与任务相关

管理与监控相关

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使用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文