Spring状态机简单实现
一、什么是状态机
状态机,又称有限状态自动机,是表示有限个状态以及在这些状态之间的转移和动作等行为的计算模型。状态机的概念其实可以应用的各种领域,包括电子工程、语言学、哲学、生物学、数学和逻辑学等,例如日常生活中的电梯、风扇、门闸机等,都会涉及到多种状态,随着动作的执行会进行状态的转移,而在软件编程领域,采用状态机的思路同样可以简化我们的设计流程,会使代码的可读性和可维护性得到增加。
目前用的比较多的开源状态机如:Spring StateMachine、Squirrel StateMachine以及阿里开源一款轻量的Cola-StateMachine,本文主要介绍Spring的状态机的使用。
二、Spring状态机的核心概念
- transition:转换是原状态和目标状态之间的关系,用于将状态机从一种状态转移到另一种状态
- source:节点的当前状态
- target:节点的目标状态
- event:触发节点从当前状态到目标状态的动作,如从State A 到 State B
- guard:也叫“门卫”,当事件请求触发时,可以定义校验规则,用于校验是否可以执行后续action
- action:用于实现当前节点对应的业务逻辑(事件发生之后系统做出的反应)
- withChoice:当执行一个动作,可能导致多种结果时,可以选择使用choice+guard来跳转
- withInternal:我们支持三种不同类型的转换,external,internal和local。转换时通过信号触发的,该信号是发送到状态机的事件或计时器
三、Spring状态机使用示例
Spring StateMachine 是 Spring 官方提供的状态机实现。其核心组件如下:
- StateMachine:状态机实例,可以触发事件、执行状态转换,并获取当前状态等信息;
- StateMachineStateConfigurer:用于配置状态,定义状态机中的各种状态,并指定每个状态的行为和属性;
- StateMachineTransitionConfigurer:用于配置状态之间的转换关系,定义转换的触发事件、源状态、目标状态,以及转换的条件和动作;
- StateMachineConfigurationConfigurer:状态机系统配置,用于配置状态机的全局属性和行为,包括状态机的执行模式、并发策略、监听器等;
- StateMachineListenerAdapter:事件监听器,用于简化状态机事件监听器(StateMachineEventListener)的实现
这里还是以抽奖奖励状态转换为例,奖励的状态转换图如下:
- 环境准备:
<dependency><groupId>org.springframework.statemachine</groupId><artifactId>spring-statemachine-starter</artifactId><version>2.5.0</version></dependency>
- 定义奖励状态以及事件的枚举类
public enum AwardState {INACTIVE, ACTIVE, PAUSE, FINISH
}public enum AwardEvent {AUTO_ACTIVATE, AUTO_FINISH, FINISH, PAUSE, RESUME
}
- 状态机配置类:
@Configuration
@EnableStateMachine
public class AwardStateMachineConfig extends EnumStateMachineConfigurerAdapter<AwardState, AwardEvent> {@Autowiredprivate AwardStateMachineListener awardStateMachineListener;@Overridepublic void configure(StateMachineStateConfigurer<AwardState, AwardEvent> states) throws Exception {states.withStates().initial(AwardState.INACTIVE).states(EnumSet.allOf(AwardState.class));}@Overridepublic void configure(StateMachineTransitionConfigurer<AwardState, AwardEvent> transitions) throws Exception {transitions.withExternal().source(AwardState.INACTIVE).target(AwardState.ACTIVE).event(AwardEvent.AUTO_ACTIVATE).and().withExternal().source(AwardState.INACTIVE).target(AwardState.FINISH).event(AwardEvent.AUTO_FINISH).and().withExternal().source(AwardState.ACTIVE).target(AwardState.FINISH).event(AwardEvent.FINISH).and().withExternal().source(AwardState.ACTIVE).target(AwardState.PAUSE).event(AwardEvent.PAUSE).and().withExternal().source(AwardState.PAUSE).target(AwardState.ACTIVE).event(AwardEvent.RESUME).and().withExternal().source(AwardState.PAUSE).target(AwardState.FINISH).event(AwardEvent.FINISH);}@Overridepublic void configure(StateMachineConfigurationConfigurer<AwardState, AwardEvent> config) throws Exception {config.withConfiguration().autoStartup(true).listener(awardStateMachineListener);}
}
- 状态机监听器:
@Component
public class AwardStateMachineListener extends StateMachineListenerAdapter<AwardState, AwardEvent> {@Overridepublic void transition(Transition<AwardState, AwardEvent> transition) {System.out.println("状态转移 from " + transition.getSource().getId() + " to " + transition.getTarget().getId());}/*@Overridepublic void stateChanged(State<AwardState, AwardEvent> from, State<AwardState, AwardEvent> to) {System.out.println("状态改变 from " + from.getId() + " to " + to.getId());}*/
}
- 奖励服务类
@Service
public class AwardStateMachineService {@Resourceprivate StateMachine<AwardState, AwardEvent> stateMachine;public void autoActivate() {stateMachine.sendEvent(AwardEvent.AUTO_ACTIVATE);}public void autoFinish() {stateMachine.sendEvent(AwardEvent.AUTO_FINISH);}public void finish() {stateMachine.sendEvent(AwardEvent.FINISH);}public void pause() {stateMachine.sendEvent(AwardEvent.PAUSE);}public void resume() {stateMachine.sendEvent(AwardEvent.RESUME);}}
- 测试:
@SpringBootTest
class AwardStatemachineApplicationTests {@Autowiredprivate AwardStateMachineService awardStateMachineService;@Autowiredprivate StateMachine<AwardState, AwardEvent> stateMachine;@Testvoid awardStageTest() {// 发送事件自动激活抽奖awardStateMachineService.autoActivate();// 检查状态是否变为ACTIVEassert (stateMachine.getState().getId() == AwardState.ACTIVE);// 发送事件暂停抽奖awardStateMachineService.pause();// 检查状态是否变为PAUSEassert (stateMachine.getState().getId() == AwardState.PAUSE);// 发送事件恢复抽奖awardStateMachineService.resume();// 检查状态是否变为ACTIVEassert (stateMachine.getState().getId() == AwardState.ACTIVE);// 发送事件结束抽奖awardStateMachineService.finish();// 检查状态是否变为FINISHassert (stateMachine.getState().getId() == AwardState.FINISH);}}
测试执行结果:
可以看到 Spring 状态机很好的控制了奖励状态之间的流转。
总结:本文主要介绍了Spring状态机的一些基本概念,以及状态流转的使用方式,Spring状态机一些高级的用法,如状态的持久化、状态的并行(parallel,fork,join)、子状态机等,会在后面文章更新。
参考:
https://docs.spring.io/spring-statemachine/docs/2.0.2.RELEASE/reference/htmlsingle/#glossary
相关文章:

Spring状态机简单实现
一、什么是状态机 状态机,又称有限状态自动机,是表示有限个状态以及在这些状态之间的转移和动作等行为的计算模型。状态机的概念其实可以应用的各种领域,包括电子工程、语言学、哲学、生物学、数学和逻辑学等,例如日常生活中的电…...

WebServer -- 面试题(下)
👂 夏风 - Gifty - 单曲 - 网易云音乐 目录 🌼前言 🎂面试题(下) 4)HTTP报文解析 为什么要用状态机 状态转移图画一下 https 协议为什么安全 https 的 ssl 连接过程 GET 和 POST 的区别 5)数据库注册登录 登…...

企业微信如何接入第三方应用?
1.登录企业微信管理后台:https://work.weixin.qq.com/wework_admin 2.点击创建应用; 3. 此时可以看到已经创建好的应用,并且生成应用的唯一id(agentId) 4. 第三方应用申请域名 (举例&…...
JAVA后端编码的主键字段存储为什么倾向于使用雪花算法
1.背景 最近有人问,什么是雪花算法,为什么使用雪花算法不使用数据库UUID,基于此,写一个说明。 2.简介 (1)雪花算法,英文名为snowflake,翻译过来就是是雪花,所以叫雪花…...

Rust 深度学习库 Burn
一、概述 Burn 它是一个新的综合动态深度学习框架,使用 Rust 构建的,以极高的灵活性、计算效率和可移植性作为其主要目标。 Rust Burn 是一个以灵活性、高性能和易用性为核心设计原则工具,主打就是灵活性 、高性能 及易用性。 二、Rust B…...

C语言-存储期2.0
静态存储期 在数据段中分配的变量,统统拥有静态存储期,因此也都被称为静态变量。这里静态的含义,指的是这些变量的不会因为程序的运行而发生临时性的分配和释放,它们的生命周期是恒定的,跟整个程序一致。 静态变量包含…...
计算机网络面经八股-HTTP请求报文和响应报文的格式?
请求报文格式: 请求行(请求方法URI协议版本)请求头部空行请求主体 请求行:GET /sample.jsp HTTP/1.1 表示使用 GET 方法请求 /sample.jsp 资源,并使用 HTTP/1.1 协议。请求头部:包含多个字段,…...
Ubuntu 18.04安装最新版Visual Studio Code(VS Code)报依赖库版本过低错误
Ubuntu 18.04安装最新版Visual Studio Code(VS Code)报依赖库版本过低错误 1. 问题描述2. 解决方案2.1 修复之前安装的错误2.2 安装VS Code 1.85.2 3. 原因分析 1. 问题描述 在Ubuntu 18.04系统上安装VS Code ≥ v1.86.2(测试到v1.87.1&…...

Android NDK入门:在应用中加入C和C++的力量
目录 编辑 引 NDK的设计目的 与Java/Kotlin的结合 使用场景 开发流程 设置项目以支持NDK 编写本地代码 使用JNI连接本地代码和Java/Kotlin代码 编译和运行你的应用 附 引 自诩方向是android方向的移动端开发工程师,却从来没有真正仔细了解过NDK&#…...
2024年华为OD机试真题-田忌赛马-Java-OD统一考试(C卷)
题目描述: 给定两个只包含数字的数组a,b,调整数组 a 里面数字的顺序,使得尽可能多的 a[i] >b[i]。数组 a和 b 中的数字各不相同。 输出所有可以达到最优结果的 a 数组的数量 输入描述: 输入的第一行是数组 a 中的数字,其中只包含数字,每两个数字之间相隔一个空格,a…...

C++ 网络编程学习五
C网络编程学习五 网络结构的更新单例模式懒汉单例模式饿汉单例模式懒汉式指针智能指针设计单例类 服务器优雅退出asio的多线程模型IOServiceasio多线程IOThreadPoolepoll 和 iocp的一些知识点 网络结构的更新 asio网络层,会使用io_context进行数据封装,…...

案例分析篇05:数据库设计相关28个考点(9~16)(2024年软考高级系统架构设计师冲刺知识点总结系列文章)
专栏系列文章推荐: 2024高级系统架构设计师备考资料(高频考点&真题&经验)https://blog.csdn.net/seeker1994/category_12593400.html 【历年案例分析真题考点汇总】与【专栏文章案例分析高频考点目录】(2024年软考高级系统架构设计师冲刺知识点总结-案例分析篇-…...

pip 和conda 更换镜像源介绍
1、前言 很多深度学习的项目免不了安装库文件、配置环境等等,如果利用官方提供的连接,网速很慢,而且很容易download掉。 所以配置好了虚拟环境,将pip换源属实重要 常见的国内镜像源有清华、中科大、阿里等等... 这里建议用中科…...

Git概述及安装步骤
一、Git简介 Git是一个免费的、开源的分布式版本控制系统,可以快速高效地处理从小型到大型的各种项目。Git 易于学习,占地面积小,性能极快。它具有廉价的本地库,方便的暂存区域和多个工作流分支等特性。其性能优于Subversion、CV…...

北京保险服务中心携手镜舟科技,助推新能源车险市场规范化
2022 年,一辆新能源汽车在泥泞的小路上不慎拖底,动力电池底壳受损,电池电量低。车主向保险公司报案,希望能够得到赔偿。然而,在定损过程中,保司发现这辆车的电池故障并非由拖底事件引起,而是由于…...

给女朋友的浪漫微信消息推送超详细版
1. 下载代码 地址: 链接: https://pan.baidu.com/s/1lESgRoWn8bXyE0jsSVCHqQ?pwdimr6 提取码: imr6 根据 resources/db 下sql文件创建表 修改yml文件中数据库连接 2. 进入微信测试平台 地址:微信公众平台 扫码登录获取测试号信息 修改代…...
Android开发 Activity启动模式、ViewModel与LiveData,及Kotlin Coroutines
目录 Activity启动模式 onNewIntent解释 Activity启动模式的考虑时机 Service启动模式 ContentProvider的作用 Broadcast的注册方式 AsyncTask的作用 ViewModel LiveData Kotlin Coroutines 结合使用 Activity启动模式 Android中Activity的启动模式有四种࿰…...
MQL语言实现抽象工厂模式
文章目录 一、定义抽象产品接口二、定义抽象工厂接口三、定义具体产品四、定义具体工厂五、定义工厂客户端六、客户端调用工厂客户端七、抽象工厂模式的结构 一、定义抽象产品接口 //------------------------------------------------------------------ //| participants …...

UE4开个头-简易小汽车
跟着谌嘉诚学的小Demo,记录一下 主要涉及到小白人上下车和镜头切换操作 1、动态演示效果 2、静态展示图片 3、蓝图-上下车...
Java基础入门day04
day04 包 包可以用来区分相同的类名 将相同的类放在不同包下,可以进行存储 一个目录下没有办法存在两个同名的文件 包最终在文件系统中与文件目录结构是一一对应的 在不同包下可以存放相同类名的文件 包后期还可以实现项目中模块的精确划分,controller,…...
浏览器访问 AWS ECS 上部署的 Docker 容器(监听 80 端口)
✅ 一、ECS 服务配置 Dockerfile 确保监听 80 端口 EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]或 EXPOSE 80 CMD ["python3", "-m", "http.server", "80"]任务定义(Task Definition&…...
PHP和Node.js哪个更爽?
先说结论,rust完胜。 php:laravel,swoole,webman,最开始在苏宁的时候写了几年php,当时觉得php真的是世界上最好的语言,因为当初活在舒适圈里,不愿意跳出来,就好比当初活在…...

MFC内存泄露
1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...

自然语言处理——Transformer
自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效,它能挖掘数据中的时序信息以及语义信息,但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN,但是…...
Java多线程实现之Thread类深度解析
Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...
NPOI操作EXCEL文件 ——CAD C# 二次开发
缺点:dll.版本容易加载错误。CAD加载插件时,没有加载所有类库。插件运行过程中用到某个类库,会从CAD的安装目录找,找不到就报错了。 【方案2】让CAD在加载过程中把类库加载到内存 【方案3】是发现缺少了哪个库,就用插件程序加载进…...
C语言中提供的第三方库之哈希表实现
一. 简介 前面一篇文章简单学习了C语言中第三方库(uthash库)提供对哈希表的操作,文章如下: C语言中提供的第三方库uthash常用接口-CSDN博客 本文简单学习一下第三方库 uthash库对哈希表的操作。 二. uthash库哈希表操作示例 u…...
人工智能--安全大模型训练计划:基于Fine-tuning + LLM Agent
安全大模型训练计划:基于Fine-tuning LLM Agent 1. 构建高质量安全数据集 目标:为安全大模型创建高质量、去偏、符合伦理的训练数据集,涵盖安全相关任务(如有害内容检测、隐私保护、道德推理等)。 1.1 数据收集 描…...

Rust 开发环境搭建
环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行: rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu 2、Hello World fn main() { println…...
二维FDTD算法仿真
二维FDTD算法仿真,并带完全匹配层,输入波形为高斯波、平面波 FDTD_二维/FDTD.zip , 6075 FDTD_二维/FDTD_31.m , 1029 FDTD_二维/FDTD_32.m , 2806 FDTD_二维/FDTD_33.m , 3782 FDTD_二维/FDTD_34.m , 4182 FDTD_二维/FDTD_35.m , 4793...