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,…...
洛雪音乐音源配置完全指南:免费搭建个人音乐库的终极方案
洛雪音乐音源配置完全指南:免费搭建个人音乐库的终极方案 【免费下载链接】lxmusic- lxmusic(洛雪音乐)全网最新最全音源 项目地址: https://gitcode.com/gh_mirrors/lx/lxmusic- 洛雪音乐作为一款强大的音乐播放工具,提供了全网最新最全的音源资…...
跨平台Unity游戏资源编辑利器:UABEA深度解析
跨平台Unity游戏资源编辑利器:UABEA深度解析 【免费下载链接】UABEA c# uabe for newer versions of unity 项目地址: https://gitcode.com/gh_mirrors/ua/UABEA 在游戏开发与模组制作领域,Unity引擎的资源文件编辑一直是个技术门槛较高的任务。传…...
UVa 275 Expanding Fractions
题目分析 本题要求计算两个正整数的除法的小数展开形式,其中分子小于分母,分母小于 100010001000。输入以 0 0 结束。 对于每个分数,需要输出其小数部分(从小数点开始),并且: 如果小数是有限的&…...
企业内训系统集成Taotoken实现多模型AI助教与可控的交互成本
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 企业内训系统集成Taotoken实现多模型AI助教与可控的交互成本 对于现代企业而言,构建一个高效、智能的内训系统是提升员…...
2022年AI工程实战指南:从H100到Chinchilla的十大关键技术落地
1. 这不是一份“新闻简报”,而是一份2022年4月AI技术演进的实操解剖报告 如果你在2022年春天打开过任何一家AI实验室的内部通讯、技术周会纪要,或者翻过几篇刚上线的arXiv论文,你大概率会看到一连串让人头皮发麻的名词:H100、PaLM…...
Linux服务器网络断了别慌!手把手教你用nmcli命令快速诊断与恢复连接(实战排错指南)
Linux服务器网络故障急救指南:nmcli命令实战排错全解析 凌晨三点,服务器监控突然告警,SSH连接中断,业务系统全面瘫痪——这是每位运维工程师都经历过的噩梦时刻。当远程连接彻底断开,仅剩控制台可用时,掌握…...
Docker编译镜像实战:为嵌入式Linux开发打造标准化环境
1. 项目概述:为什么我们需要一个专属的Docker编译镜像?如果你是一名嵌入式Linux开发者,或者正在学习诸如全志Tina Linux这样的开源嵌入式系统,那么“编译环境”这个词对你来说一定不陌生。它就像是一个厨师的后厨,锅碗…...
2026 SSH工具推荐:不装传统面板,还有什么更适合管理 Linux 服务器?
这几年很多人选 SSH 工具,已经不只是为了“远程登上服务器敲命令”。 真正常见的需求其实是:连上服务器之后,还要继续完成文件管理、服务部署、HTTPS 配置、站点检查,甚至多台 VPS 的统一管理。 也正因为这样,到了 202…...
Android动态调试实战:密钥提取四步法与JEB深度用法
1. 这不是“破解游戏”,而是一场对Android应用安全边界的系统性测绘你有没有遇到过这样的情况:一个内部工具APK,文档里写着“密钥已硬编码在so中”,但反编译Java层完全找不到明文;或者某SDK的初始化方法里反复调用getS…...
G-Helper:释放华硕笔记本性能的免费开源轻量控制神器
G-Helper:释放华硕笔记本性能的免费开源轻量控制神器 【免费下载链接】g-helper Lightweight Armoury Crate alternative for Asus laptops with nearly the same functionality. Works with ROG Zephyrus, Flow, TUF, Strix, Scar, ProArt, Vivobook, Zenbook, Exp…...
