Spring状态机简单实现

news2024/12/24 11:35:32

一、什么是状态机

状态机,又称有限状态自动机,是表示有限个状态以及在这些状态之间的转移和动作等行为的计算模型。状态机的概念其实可以应用的各种领域,包括电子工程、语言学、哲学、生物学、数学和逻辑学等,例如日常生活中的电梯、风扇、门闸机等,都会涉及到多种状态,随着动作的执行会进行状态的转移,而在软件编程领域,采用状态机的思路同样可以简化我们的设计流程,会使代码的可读性和可维护性得到增加。

目前用的比较多的开源状态机如: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)的实现

这里还是以抽奖奖励状态转换为例,奖励的状态转换图如下:

在这里插入图片描述

  1. 环境准备:
<dependency>
     <groupId>org.springframework.statemachine</groupId>
     <artifactId>spring-statemachine-starter</artifactId>
     <version>2.5.0</version>
 </dependency>
  1. 定义奖励状态以及事件的枚举类

public enum AwardState {
    INACTIVE, 
    ACTIVE, 
    PAUSE, 
    FINISH
}

public enum AwardEvent {
    AUTO_ACTIVATE, 
    AUTO_FINISH, 
    FINISH, 
    PAUSE, 
    RESUME
}
  1. 状态机配置类:
@Configuration
@EnableStateMachine
public class AwardStateMachineConfig extends EnumStateMachineConfigurerAdapter<AwardState, AwardEvent> {
    @Autowired
    private AwardStateMachineListener awardStateMachineListener;

    @Override
    public void configure(StateMachineStateConfigurer<AwardState, AwardEvent> states) throws Exception {
        states
        .withStates()
        .initial(AwardState.INACTIVE)
        .states(EnumSet.allOf(AwardState.class));
    }

    @Override
    public 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);
    }

    @Override
    public void configure(StateMachineConfigurationConfigurer<AwardState, AwardEvent> config) throws Exception {
        config.withConfiguration().autoStartup(true).listener(awardStateMachineListener);
    }
}
  1. 状态机监听器:
@Component
public class AwardStateMachineListener extends StateMachineListenerAdapter<AwardState, AwardEvent> {
    @Override
    public void transition(Transition<AwardState, AwardEvent> transition) {
        System.out.println("状态转移 from " + transition.getSource().getId() + " to " + transition.getTarget().getId());
    }

    /*@Override
    public void stateChanged(State<AwardState, AwardEvent> from, State<AwardState, AwardEvent> to) {
        System.out.println("状态改变 from " + from.getId() + " to " + to.getId());
    }*/
}
  1. 奖励服务类
@Service
public class AwardStateMachineService {

    @Resource
    private 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);
    }
    
}
  1. 测试:
@SpringBootTest
class AwardStatemachineApplicationTests {

    @Autowired
    private AwardStateMachineService awardStateMachineService;

    @Autowired
    private StateMachine<AwardState, AwardEvent> stateMachine;

    @Test
    void awardStageTest() {
        // 发送事件自动激活抽奖
        awardStateMachineService.autoActivate();
        // 检查状态是否变为ACTIVE
        assert (stateMachine.getState().getId() == AwardState.ACTIVE);

        // 发送事件暂停抽奖
        awardStateMachineService.pause();
        // 检查状态是否变为PAUSE
        assert (stateMachine.getState().getId() == AwardState.PAUSE);

        // 发送事件恢复抽奖
        awardStateMachineService.resume();
        // 检查状态是否变为ACTIVE
        assert (stateMachine.getState().getId() == AwardState.ACTIVE);

        // 发送事件结束抽奖
        awardStateMachineService.finish();
        // 检查状态是否变为FINISH
        assert (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

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

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

相关文章

SpringBoot之Bean扫描、Bean注册

目录 Bean扫描 Bean注册 Bean lmport 自定义注解 注册条件 Bean扫描 Bean扫描有两种方式 1、标签:<context:component-scan base-package"com.mybatis"/> 2、注解: ComponentScan(basePackages "com.mybatis") springboot启动类注解可以自…

Android 异常重启--踩坑归来--干货篇

如果你未对自己的app进行过处理&#xff0c;那么线上各种偶发莫名其妙的闪退、白屏、数据丢失&#xff0c;请检查一下是否因此而引发的。 起因 异常重建指的是非配置变更情况下导致的 Activity 重新创建。 常见场景大多是因为内存等资源不足&#xff0c;从而导致后台应用被系…

python统计分析——单变量分布的特征描述之分布中心

参考资料&#xff1a;python统计分析【托马斯】 也可查看&#xff1a;python统计分析——单变量描述统计-CSDN博客 当我们有一个来自分布的数据样本时&#xff0c;我们可以用不同的参数来描述分布中心。因此&#xff0c;数据可以用两种方式来评估&#xff1a; &#xff08;1&a…

Xcode15.3 -Library ‘iconv2.4.0‘ not found

今天升级了一下Mac mini 和Xcode15.3&#xff0c;运行项目就报 Library ‘iconv2.4.0’ not found的错误 xcode升级到&#xff1a;15.3(15A240d) 项目在旧版本下&#xff0c;是能通过编译 并且能运行的。 解决方法&#xff1a; 方案1&#xff1a;在Build Phases --> Link…

SSL VPN基础原理

目录 SSL ---安全传输协议&#xff08;安全套接层&#xff09;---TLS ----传输层安全协议 SSL的工作原理 SSL会话建立的过程 ​编辑 数据传输过程中的封装示意图 无客户端认证的过程 有客户端认证的过程 SSL VPN的核心技术---虚拟网关技术 服务器验证的点&#xff1a; 资源…

Gitlab光速发起Merge Request

前言 在我们日常开发过程中需要经常使用到Merge Request&#xff0c;在使用过程中我们需要来回在开发工具和UI界面之前来回切换&#xff0c;十分麻烦。那有没有一种办法可以时间直接开发开工具中直接发起Merge Request呢&#xff1f; 答案是有的。 使用 Git 命令方式创建 Me…

npm、nodejs和vue之间关系和区别介绍

本文讲解npm、Node.js和Vue.js这三者之间的关系和区别&#xff0c;以及它们各自的特点。 首先&#xff0c;让我们来了解一下Node.js。 **Node.js** 是一个开源的服务器端运行环境&#xff0c;它允许开发者使用JavaScript来编写服务器端的代码。在传统的Web开发中&#…

免费 Copilot 用户可以访问 OpenAI 的 GPT-4 Turbo;面向 3D 虚拟环境的多面手 AI 代理

&#x1f989; AI新闻 &#x1f680; 免费 Copilot 用户可以访问 OpenAI 的 GPT-4 Turbo 摘要&#xff1a;微软宣布免费版Copilot已升级到GPT-4 Turbo模型&#xff0c;所有用户都可以免费使用。此外&#xff0c;Copilot Pro新增了GPT Builder工具&#xff0c;订阅者可创建自定…

网上商城购物系统|基于springboot框架+ Mysql+Java+B/S架构的网上商城购物系统设计与实现(可运行源码+数据库+设计文档+部署说明)

推荐阅读100套最新项目 最新ssmjava项目文档视频演示可运行源码分享 最新jspjava项目文档视频演示可运行源码分享 最新Spring Boot项目文档视频演示可运行源码分享 目录 前台功能效果图 管理员功能登录前台功能效果图 用户功能模块 系统功能设计 数据库E-R图设计 lunwen参…

【数据结构】哈希表(哈希函数+负载因子+解决冲突方法)

文章目录 五、哈希表1.概念2.哈希函数1.设计哈希函数&#xff1a;2.常见的哈希函数1.直接定址法&#xff08;常用&#xff09;&#xff1a;2.除留余数法&#xff08;常用&#xff09; 3.负载因子4.解决冲突1.闭散列法&#xff08;开放地址法&#xff09;1.线性探测法&#xff1…

docker容器启动rabbitmq

docker容器启动rabbitmq 一、RabbitMQ部署1.1.在线拉取mq镜像1.2.运行mq容器1.3.访问mq 二、RabbitMQ的集群2.1.集群分类2.1.设置 /etc/hosts文件 endl 一、RabbitMQ部署 1.1.在线拉取mq镜像 # 在线拉取 docker pull rabbitmq:3-management1.2.运行mq容器 docker run \ -e R…

python爬虫 Appium+mitmdump 京东商品

爬虫系列&#xff1a;http://t.csdnimg.cn/WfCSx 前言 我们知道通过Charles进行抓包可以发现其参数相当复杂&#xff0c;Form 表单有很多加密参数。如果我们只用 Charles 探测到这个接口链接和参数&#xff0c;还是无法直接构造请求的参数&#xff0c;构造的过程涉及一些加密…

Kafka-生产者报错javax.management.InstanceAlreadyExistsException

生产者发送消息到 kafka 中,然后控制台报错 然后根据日志查看 kafka 的源码发现了问题原因 说的是MBean已经注册了,然后报异常了,这样就会导致生产者的kafka注册失败, 原因是项目上生产者没有配置clientId,默认都是空导致的, 多个生产者(项目)注册到kafka集群中的 id 都相同。 …

水泵房远程监控物联网系统

随着物联网技术的快速发展&#xff0c;越来越多的行业开始利用物联网技术实现设备的远程监控与管理。水泵房作为城市供水系统的重要组成部分&#xff0c;其运行状态的监控与管理至关重要。HiWoo Cloud作为专业的物联网云服务平台&#xff0c;为水泵房远程监控提供了高效、稳定、…

SpringCloud(22)之Sentinel实战应用

一、Sentinel核心库 sentinel主页&#xff1a;主页 alibaba/Sentinel Wiki GitHub 1.1 Sentinel介绍 随着微服务的流行&#xff0c;服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式、多语言异构化服务架构的流量治理组件&#xff0c;主要以流量为切入点&…

有趣的前端知识(三)

推荐阅读 有趣的前端知识&#xff08;一&#xff09; 有趣的前端知识&#xff08;二&#xff09; 文章目录 推荐阅读JS内置对象JS外部对象BOM模型history对象screen对象navigator对象 DOM&#xff08;文档对象模型&#xff09;DOM的方法&#xff08;对于节点的操作&#xff09…

Rudolf and the Ball Game

传送门 题意 思路 暴力枚举每一个妆台的转换条件 code #include<iostream> #include<cstdio> #include<stack> #include<vector> #include<algorithm> #include<cmath> #include<queue> #include<cstring> #include<ma…

ChatGPT 插件Plugin集合

ChatGPT的插件功能推出一段时间了&#xff0c;陆陆续续的上架了得有200了。 但是其中大部分都不是很好用&#xff0c;并且找起来也复杂。 推荐一个不知名热心人做的导航页。 ChatGPT Plugins Overview 基本上集合了所有的插件&#xff0c;并且还在实时更新中。 需要升级4.0&a…

el-input设置max、min无效的解决方案

目录 一、方式1&#xff1a;type“number” 二、方式2&#xff1a;oninput&#xff08;推荐&#xff09; 三、计算属性 如下表所示&#xff0c;下面为官方关于max&#xff0c;min的介绍&#xff1a; el-input&#xff1a; max原生属性&#xff0c;设置最大值min原生属性&a…

<Senior High School Math>: inequality question

( 1 ) . o m i t (1). omit (1).omit ( 2 ) . ( a 2 − b 2 ) ( x 2 a 2 − y 2 b 2 ) ( x 2 y 2 ) − ( a 2 y 2 b 2 b 2 x 2 a 2 ) ≤ x 2 y 2 − 2 x y ( x − y ) 2 (2). (a^2-b^2)(\frac{x^2}{a^2} - \frac{y^2}{b^2})(x^2y^2)-(\frac{a^2y^2}{b^2}\frac{b^2x^2}{a^…