Flowable工作流引擎:Spring Boot集成指南

Flowable工作流引擎:Spring Boot集成指南

  • 前言
  • 开始集成
    • 相关配置文件
    • pom文件

前言

在快速变化的软件开发世界中,工作流管理成为了企业应用不可或缺的组成部分。无论是简化复杂业务流程、提升操作效率还是确保流程的一致性和透明性,一个强大的工作流引擎都显得至关重要。在这样的背景下,Flowable作为一个轻量级、可嵌入的工作流引擎,因其卓越的灵活性和易于集成的特点,赢得了开发社区的广泛青睐。

为什么选择Flowable?

Flowable提供了一个功能丰富的工作流和业务流程管理(BPM)平台。它不仅支持BPMN 2.0标准,还提供了易于使用的API和用户友好的管理界面。与同类工作流引擎相比,Flowable拥有活跃的社区支持、频繁的更新发布以及一系列成熟的工具和插件,这些都使得它在处理复杂的业务流程时变得更加得心应手。

Flowable的应用场景

Flowable适用于多种业务场景,包括但不限于文档审批流程、订单处理系统、IT服务管理等。它的灵活性允许开发者根据具体需求定制流程,而其高效的性能则确保了即使在高负载情况下也能平稳运行。

集成需求

对于构建在Spring框架之上的现代应用来说,将Flowable集成进Spring Boot应用中可以带来巨大的便利。这样的集成不仅简化了流程管理,而且充分利用了Spring生态中的丰富资源和便捷性。在本系列文章中,我们将深入探讨如何在Spring Boot环境下集成并使用Flowable,以及如何利用这一集成来优化业务流程。

文章结构

本系列博客将从环境搭建开始,逐步介绍Flowable的基础概念、核心组件以及实际案例。我们会通过详细的步骤和示例代码,演示如何创建和管理流程定义,如何启动和查询流程实例,以及如何处理各种异常情况。最后,我们还会讨论如何将Flowable与Spring Security等其他流行技术结合使用,以实现更加安全和强大的工作流解决方案。

目标读者

无论你是寻求提高现有工作流程效率的开发者,还是需要构建新的工作流程管理系统的软件工程师,甚至是想要了解当前最热门工作流技术的项目经理,本系列博客都将为你提供有价值的信息和指导。

准备工作

在阅读本系列文章之前,建议读者具备一定的Java编程经验和基本的Spring Boot知识。此外,对工作流的基本理解将有助于更好地把握文章的内容。

版本信息

为了确保教程的准确性和可操作性,我们将基于Flowable 6.x版本和Spring Boot 2.x进行讲解。请确保按照这些版本进行环境的搭建。

资源和帮助

在官方Flowable网站(https://flowable.org/)上,你可以找到丰富的文档资源和社区论坛。如果遇到问题,不要犹豫,在论坛中寻求帮助或直接查阅FAQs。

现在,让我们一起踏上探索Flowable和Spring Boot集成之旅,开启高效工作流管理的新纪元。

开始集成

相关配置文件

FlowableConfig

@SuppressWarnings("all")
@Configuration
public class FlowableConfig {@Autowiredprivate DataSource dataSource;@Autowiredprivate PlatformTransactionManager transactionManager;@Autowiredprivate IdWorkerIdGenerator idWorkerIdGenerator;@Value("${workflow.databaseUpdate}")private Boolean databaseUpdate;/*** SpringProcessEngineConfiguration配置** @return*/@Beanpublic SpringProcessEngineConfiguration getSpringProcessEngineConfiguration() {// 创建SpringProcessEngineConfiguration对象SpringProcessEngineConfiguration config = new SpringProcessEngineConfiguration();// 设置活动元素的字体名称为"宋体"config.setActivityFontName("宋体");// 设置注解元素的字体名称为"宋体"config.setAnnotationFontName("宋体");// 设置标签元素的字体名称为"黑体"config.setLabelFontName("黑体");// 设置数据源config.setDataSource(dataSource);// 设置事务管理器config.setTransactionManager(transactionManager);// 禁用身份和访问管理引擎config.setDisableIdmEngine(true);// 设置数据库类型为MySQLconfig.setDatabaseType(ProcessEngineConfigurationImpl.DATABASE_TYPE_MYSQL);// 设置数据库模式更新策略为自动更新 生产环境设置DB_SCHEMA_UPDATE_FALSEif (null != databaseUpdate && databaseUpdate) {config.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);} else {config.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_FALSE);}// 设置委托表达式字段注入模式为混合模式config.setDelegateExpressionFieldInjectionMode(DelegateExpressionFieldInjectionMode.MIXED);// 设置ID生成器为idWorkerIdGeneratorconfig.setIdGenerator(idWorkerIdGenerator);// 激活异步执行器 关闭异步执行器config.setAsyncExecutorActivate(Boolean.FALSE);// 设置HTTP客户端配置HttpClientConfig httpClientConfig = new HttpClientConfig();httpClientConfig.setConnectTimeout(1000000); // 连接超时时间httpClientConfig.setConnectionRequestTimeout(1000000); // 连接请求超时时间httpClientConfig.setSocketTimeout(1000000); // 建立socket连接超时时间httpClientConfig.setRequestRetryLimit(3); // 请求失败重试次数config.setHttpClientConfig(httpClientConfig);// 历史数据记录级别config.setHistoryLevel(HistoryLevel.AUDIT);// 设置知识库缓存限制为200config.setKnowledgeBaseCacheLimit(200);// 设置流程定义缓存限制为200config.setProcessDefinitionCacheLimit(200);// 添加自定义的JobHandlerList<JobHandler> customJobHandlers = new ArrayList<>();customJobHandlers.add(new CustomJobHandler());config.setCustomJobHandlers(customJobHandlers);return config;}@Bean@Primarypublic TaskExecutor primaryTaskExecutor() {return new ThreadPoolTaskExecutor();}}

IdWorkerIdGenerator

@Component
public class IdWorkerIdGenerator implements IdGenerator {@Overridepublic String getNextId() {return String.valueOf(SpringUtils.getBean(IdWorker.class).nextId());}
}
@Component
public class IdWorker {// 时间起始标记点,作为基准,一般取系统的最近时间(一旦确定不能变动)private final static long twepoch = 1288834974657L;// 机器标识位数private final static long workerIdBits = 5L;// 数据中心标识位数private final static long datacenterIdBits = 5L;// 机器ID最大值private final static long maxWorkerId = -1L ^ (-1L << workerIdBits);// 数据中心ID最大值private final static long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);// 毫秒内自增位private final static long sequenceBits = 12L;// 机器ID偏左移12位private final static long workerIdShift = sequenceBits;// 数据中心ID左移17位private final static long datacenterIdShift = sequenceBits + workerIdBits;// 时间毫秒左移22位private final static long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;private final static long sequenceMask = -1L ^ (-1L << sequenceBits);/* 上次生产id时间戳 */private static long lastTimestamp = -1L;// 0,并发控制private long sequence = 0L;private final long workerId;// 数据标识id部分private final long datacenterId;public IdWorker() {this.datacenterId = getDatacenterId(maxDatacenterId);this.workerId = getMaxWorkerId(datacenterId, maxWorkerId);}/*** @param workerId     工作机器ID* @param datacenterId 序列号*/public IdWorker(long workerId, long datacenterId) {if (workerId > maxWorkerId || workerId < 0) {throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));}if (datacenterId > maxDatacenterId || datacenterId < 0) {throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));}this.workerId = workerId;this.datacenterId = datacenterId;}/*** 获取下一个ID** @return*/public synchronized long nextId() {long timestamp = timeGen();if (timestamp < lastTimestamp) {throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));}if (lastTimestamp == timestamp) {// 当前毫秒内,则+1sequence = (sequence + 1) & sequenceMask;if (sequence == 0) {// 当前毫秒内计数满了,则等待下一秒timestamp = tilNextMillis(lastTimestamp);}} else {sequence = 0L;}lastTimestamp = timestamp;// ID偏移组合生成最终的ID,并返回IDlong nextId = ((timestamp - twepoch) << timestampLeftShift)| (datacenterId << datacenterIdShift)| (workerId << workerIdShift) | sequence;return nextId;}private long tilNextMillis(final long lastTimestamp) {long timestamp = this.timeGen();while (timestamp <= lastTimestamp) {timestamp = this.timeGen();}return timestamp;}private long timeGen() {return System.currentTimeMillis();}/*** <p>* 获取 maxWorkerId* </p>*/protected static long getMaxWorkerId(long datacenterId, long maxWorkerId) {StringBuffer mpid = new StringBuffer();mpid.append(datacenterId);String name = ManagementFactory.getRuntimeMXBean().getName();if (!name.isEmpty()) {/** GET jvmPid*/mpid.append(name.split("@")[0]);}/** MAC + PID 的 hashcode 获取16个低位*/return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);}/*** <p>* 数据标识id部分* </p>*/protected static long getDatacenterId(long maxDatacenterId) {long id = 0L;try {InetAddress ip = InetAddress.getLocalHost();NetworkInterface network = NetworkInterface.getByInetAddress(ip);if (network == null) {id = 1L;} else {byte[] mac = network.getHardwareAddress();id = ((0x000000FF & (long) mac[mac.length - 1])| (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6;id = id % (maxDatacenterId + 1);}} catch (Exception e) {System.out.println(" getDatacenterId: " + e.getMessage());}return id;}
}

CustomJobHandler

@Slf4j
public class CustomJobHandler implements JobHandler {public static final String TYPE = "custom-handler";@Overridepublic String getType() {return "custom-handler";}@Overridepublic void execute(JobEntity jobEntity, String s, VariableScope variableScope, CommandContext commandContext) {log.info("============执行自定义定时任务============");log.info("定时任务详情={}", JSON.toJSONString(jobEntity));}
}

pom文件

  <flowable.version>6.7.2</flowable.version><!-- Flowable相关依赖项 --><dependency><groupId>org.flowable</groupId><artifactId>flowable-spring-boot-starter</artifactId><version>${flowable.version}</version><exclusions><!-- 排除flowable-spring-boot-starter-app,因为不需要该功能 --><exclusion><groupId>org.flowable</groupId><artifactId>flowable-spring-boot-starter-app</artifactId></exclusion><!-- 排除flowable-form-spring-configurator,因为不需要该功能 --><exclusion><groupId>org.flowable</groupId><artifactId>flowable-form-spring-configurator</artifactId></exclusion><!-- 排除flowable-idm-spring-configurator,因为不需要该功能 --><exclusion><groupId>org.flowable</groupId><artifactId>flowable-idm-spring-configurator</artifactId></exclusion><!-- flowable-spring-boot-starter-cmmn,因为不需要该功能 --><exclusion><groupId>org.flowable</groupId><artifactId>flowable-cmmn-spring-configurator</artifactId></exclusion><!-- flowable-spring-boot-starter-cmmn,因为不需要该功能 --><exclusion><groupId>org.flowable</groupId><artifactId>flowable-spring-boot-starter-cmmn</artifactId></exclusion><!-- flowable-spring-boot-starter-dmn,因为不需要该功能 --><exclusion><groupId>org.flowable</groupId><artifactId>flowable-dmn-spring-configurator</artifactId></exclusion><!-- flowable-spring-boot-starter-dmn,因为不需要该功能 --><exclusion><groupId>org.flowable</groupId><artifactId>flowable-spring-boot-starter-dmn</artifactId></exclusion><!-- 排除mybatis,因为与其他依赖项存在冲突 --><exclusion><artifactId>mybatis</artifactId><groupId>org.mybatis</groupId></exclusion></exclusions></dependency><!-- flowable-bpmn-model依赖项,用于创建和操作BPMN 2.0流程模型 --><dependency><groupId>org.flowable</groupId><artifactId>flowable-bpmn-model</artifactId><version>${flowable.version}</version><scope>compile</scope></dependency><!-- flowable-json-converter依赖项,用于将BPMN模型转换为JSON格式,并在需要时进行反向转换 --><dependency><groupId>org.flowable</groupId><artifactId>flowable-json-converter</artifactId><version>${flowable.version}</version><scope>compile</scope></dependency><!-- flowable-bpmn-converter依赖项,用于将BPMN模型转换为Flowable内部的可执行流程定义 --><dependency><groupId>org.flowable</groupId><artifactId>flowable-bpmn-converter</artifactId><version>${flowable.version}</version><scope>compile</scope></dependency><!-- flowable-bpmn-layout依赖项,用于自动布局BPMN模型,以确保流程图的可读性和美观性 --><dependency><groupId>org.flowable</groupId><artifactId>flowable-bpmn-layout</artifactId><version>${flowable.version}</version></dependency>

至此便完成了初步集成,可在项目中使用flowable的api完成工作流的使用。

使用也很简单 ,直接注入使用即可

  @Autowiredprivate IProcessDefinitionService iProcessDefinitionService;@Autowiredprivate RepositoryService repositoryService;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/118.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python二级题目-仅使用 Python 基本语法,即不使用任何模块,编写 Python 程序计算下列数学表达式的结果并输出,小数点后保留 3 位。

x(((3**4)5*(6**7))/8)**0.5 .format 用法一&#xff1a; print({}.format(1)) 1 print(这个是format的用法{}。。。.format(3)) 这个是format的用法3 ’大括号1:{},大括号2:{},大括号3:{}‘.format(3,4,5) print(’大括号1:{},大括号2:{},大括号3:{}‘.form…

牛客小白月赛90(A,B,C,D,E,F)

比赛链接 官方题解&#xff08;视频&#xff09; 这场偏思维&#xff0c;感觉好像没啥算法。E需要动态维护前 k k k 小&#xff0c;F是个离散化加dp&#xff0c;状态和递推方程比较 非常规&#xff0c;建议还是看一下涨涨姿势。 A 小A的文化节 思路&#xff1a; 签到 cod…

hive使用sqoop与oracle传输数据

下载地址 http://archive.apache.org/dist/sqoop 两个版本sqoop1&#xff08;1.4.x&#xff09;和sqoop2&#xff08;1.99.x&#xff09;&#xff0c;两种不同的架构。 本文使用sqoop1。 sqoop是apache旗下一款“hadoop与关系数据库之间传送数据”的工具。 导入数据&#xf…

5374: 【数学】最后一击

题目描述 小爱和小艾两人组队打一只怪兽。一开始怪兽有 n 点生命值&#xff0c;当 n 变成 0 或更低时&#xff0c;怪兽就被消灭了。他们两人是同时开始攻击的&#xff0c;小爱每分钟可以攻击 a 下&#xff0c;小艾每分钟可以攻击 b 下。若 a2&#xff0c;b4&#xff0c;则小爱…

深圳市第六批专精特新“小巨人”企业申报和第三批专精特新“小巨人”企业申报开始了

各区&#xff08;新区、特别合作区&#xff09;相关工作部门&#xff0c;各企业&#xff1a; 根据《工业和信息化部办公厅关于开展第六批专精特新“小巨人”企业培育和第三批专精特新“小巨人”企业复核工作的通知》&#xff08;工信厅企业函〔2024〕142号&#xff09;要求&…

中仕公考:2024年广东省高校毕业生‘三支一扶‘公告

2024年广东省三支一扶共计划招募3000名左右高校毕业生&#xff0c;服务期限为两年&#xff0c;具体招募岗位和条件可通过广东人事考试网查询。 招考条件&#xff1a; 年龄不超过30周岁(1993年4月22日后出生); 支教岗位须是已取得教师资格证的高校毕业生&#xff0c;支医专业…

Phpstorm环境配置与应用

PhpStorm是一款功能强大的PHP集成开发环境&#xff0c;配置与应用涉及以下步骤&#xff1a; 下载与安装&#xff1a; 访问 PhpStorm 官网下载地址&#xff0c;选择合适的版本进行下载。双击下载的安装包文件进行安装&#xff0c;过程类似于其他IntelliJ IDEA产品。 个性化设…

C++ | Leetcode C++题解之第23题合并K个升序链表

题目: 题解&#xff1a; class Solution {// 21. 合并两个有序链表ListNode *mergeTwoLists(ListNode *list1, ListNode *list2) {auto dummy new ListNode(); // 用哨兵节点简化代码逻辑auto cur dummy; // cur 指向新链表的末尾while (list1 && list2) {if (list1…

秋招嵌入式面经

24秋招-汇川嵌入式面经--超详细&#xff01; 一面 8月24日投递 9月3日一面&#xff08;30min&#xff09; 自我介绍&#xff1a; 介绍一下你的第一个项目吧 对于MCU的选型为什么使用F4的STM32&#xff1f; 项目里面用到了SPI进行两块MCU之间的通信&#xff0c;介绍一下SPI…

Linux操作系统配置git的ssh

系统&#xff1a;Ubuntu20.04LTS 安装git&#xff1a; sudo apt install git 配置git&#xff1a; # 添加全局git用户和邮箱 git config --global user.name "用户名" git config --global user.email "邮箱" # 查看用户名和邮箱是否有误 git config --li…

JavaEE初阶Day 13:多线程(11)

目录 Day 13&#xff1a;多线程&#xff08;11&#xff09;常见的锁策略1. 悲观锁 vs 乐观锁2. 重量级锁 vs 轻量级锁3. 自旋锁 vs 挂起等待锁4. 可重入锁 vs 不可重入锁5. 公平锁 vs 非公平锁6. 互斥锁 vs 读写锁 synchronized实现原理1. 锁升级2. 锁消除3. 锁粗化 CAS Day 13…

自助棋牌室如何用一招留住80%的客户?

棋牌室如何用一招守住80%的回头客&#xff0c;你想知道吗&#xff1f; 记得收藏&#xff0c;希望对你有一点点帮助&#xff01; 今天我就分享一个非常简单的方法&#xff0c;简单到所有的棋牌室老板你立马就可以去执行的方法&#xff01;第一步&#xff0c;加好友&#xff0c;…

Java面试八股之Iterator接口和Iterable接口

1. Java为什么不直接实现Iterator接口&#xff0c;而是实现Iterable? 这道题算是一道比较基础的题&#xff0c;面试官肯定也不是想让回答得多深入&#xff0c;只是考查对迭代器的了解程度&#xff0c;最好是看过源码&#xff0c;实际上迭代器的源码并不难。我们把注释折叠起来…

LIUNX文件系统

目录 1.磁盘的物理结构 2.CHS定位法 3.磁盘的逻辑存储 4.系统层面 inode.block[15] 创建文件的流程 查找文件的流程 了解文件系统&#xff0c;首先要了解磁盘是如何存储和读取数据的。 1.磁盘的物理结构 可以理解这个盘上有很多的小磁铁&#xff0c;通过旋转盘面和摆动…

葡韵饼店:云上清明节,千里寄哀思

清明&#xff0c;又称踏青节、祭祖节、行清节&#xff0c;节期在仲春与暮春之交&#xff0c;源自于上古时代的祖先信仰和春祭礼俗&#xff0c;兼具人文与自然两大内涵。 每当到了这个时候&#xff0c;人们都会携带祭祀物品&#xff0c;与家人们齐聚结伴&#xff0c;登山祭祖&am…

【Python】用python实现编译脚本

这个脚本可以自动扫描目录下的.c和.s文件并编译&#xff0c;同时生成hex和bin文件 &#xff0c;可以代替Makefile工作。cortex-m 单片机 # -*- coding: gbk -*-import os import sys import time修改编译脚本之后需要全编译一次# CC gcc CC C:\\ARM_GCC\\bin\\arm-none-eabi-…

支付宝支付之SpringBoot整合支付宝创建自定义支付二维码

文章目录 自定义支付二维码pom.xmlapplication.yml自定义二维码类AlipayService.javaAlipayServiceImpl.javaAlipayController.javaqrCode.html 自定义支付二维码 继&#xff1a;SpringBoot支付入门 pom.xml <dependency><groupId>org.springframework.boot<…

PHP中常见的@注释的含义

api: 提供给第三方使用的接口 author: 标明作者 param: 参数 return: 返回值 todo: 待办 version: 版本号 inheritdoc: 文档继承 property: 类属性 property-read: 只读属性 property-write: 只写属性 const: 常量 deprecated: 过期方法 example: 示例 final: 标识类是终态, 禁…

前端开发该不该“跳槽”到鸿蒙?

前言 面对互联网行业的激烈竞争&#xff0c;许多人都深感2023年已是不易&#xff0c;而展望2024年&#xff0c;似乎更是难上加难。这一切的根源&#xff0c;皆因行业多年发展后&#xff0c;人才市场的饱和现象愈发严重。那么&#xff0c;作为前端开发者&#xff0c;我们究竟该…

速看!2024年强基计划报考流程及常见问答

01什么是强基计划&#xff1f; 为加强基础学科拔尖创新人才选拔培养&#xff0c;教育部在深入调研、总结高校自主招生和上海等地高考综合改革试点经验的基础上&#xff0c;制定出台了《关于在部分高校开展基础学科招生改革试点工作的意见》&#xff08;也称“强基计划”&#…