当前位置: 首页 > news >正文

Springboot 集成 SpringState 状态机

Springboot 集成 SpringState 状态机

  • 1.SpringState 简介
  • 2.状态机示例
    • 2.1 项目结构和依赖包
    • 2.2 定义事件类和状态类
    • 2.3 Spring 事件监听器
    • 2.4 状态机持久化类
      • 2.4.1 Redis 状态机持久化容器
      • 2.4.2 Redis 配置
      • 2.4.3 状态机监听器
    • 2.5 装机器容器
    • 2.6 状态机事件发送器
    • 2.7 状态机配置
    • 2.8 接口类
    • 2.9 实现类
    • 2.10 状态机上下文
    • 2.11 配置文件
  • 3.状态机测试
    • 3.1创建订单
    • 3.2持久化结果
    • 3.3 支付订单
    • 3.4 发货
    • 3.5 确认收货

1.SpringState 简介

状态机核心概念​​

项目说明
状态(State)​​对象生命周期中的特定条件(如订单的待支付、已发货)
事件(Event)​​触发状态转换的动作(如支付成功、取消订单)
转换(Transition)​​定义事件如何驱动状态迁移(如待支付 → 支付事件 → 待发货)
守卫(Guard)​​条件检查,决定是否允许转换(如“仅未超时订单可支付”)
​​动作(Action)​​条件检查,决定是否允许转换(如“仅未超时订单可支付”)

应用场景

  • 订单生命周期管理​​
    管理订单从创建到完成的完整流程(如待支付 → 待发货 → 已完成)

  • 工作流引擎​​
    审批流程的状态控制(如提交 → 审核中 → 已批准)

  • ​​游戏状态流转​​
    角色状态切换(如空闲 → 战斗 → 死亡)

  • 物联网设备监控​​
    设备状态跟踪(如离线 → 在线 → 故障)

2.状态机示例

2.1 项目结构和依赖包

在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.example</groupId><artifactId>spring-state-m</artifactId><version>1.0-SNAPSHOT</version><properties><maven.compiler.source>21</maven.compiler.source><maven.compiler.target>21</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><spring-boot.version>3.5.3</spring-boot.version></properties><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!-- Spring State Machine --><dependency><groupId>org.springframework.statemachine</groupId><artifactId>spring-statemachine-starter</artifactId><version>4.0.0</version></dependency><!-- Spring State Machine Redis Persistence --><dependency><groupId>org.springframework.statemachine</groupId><artifactId>spring-statemachine-data-redis</artifactId><version>4.0.0</version></dependency></dependencies>
</project>

启动类

package org.example;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;@EnableAsync
@SpringBootApplication
public class SpringStateMachine {public static void main(String[] args) {SpringApplication.run(SpringStateMachine.class, args);}
}

2.2 定义事件类和状态类

事件用于驱动状态转移,状态用于记录事件进度

事件类

package org.example.common;/*** @Author zhx && moon* @Since 21* @Date 2025-06-18 PM 6:38*/
public enum OrderEvent {PAY,            // 支付操作SHIP,           // 发货操作CONFIRM,        // 确认收货CANCEL          // 取消订单
}

状态类

package org.example.common;/*** @Author zhx && moon* @Since 21* @Date 2025-06-18 PM 6:37*/
public enum OrderState {UNPAID,         // 待支付PAID,           // 已支付SHIPPED,        // 已发货CONFIRMED,      // 已确认收货CANCELLED       // 已取消
}

2.3 Spring 事件监听器

Spring 事件监听器,用于异步处理事件流,当状态机结束时,推送当前状态机到监听器,监听器则从持久化中删除该状态机

package org.example.config;import org.example.entity.OrderSMContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;/*** @Author zhx && moon* @Since 21* @Date 2025-06-20 AM 10:18*/
@Component
public class AsyncEventListener {Logger logger = LoggerFactory.getLogger(this.getClass());@AutowiredSMContainer smContainer;@Async@EventListenerpublic void handleAsyncEvent(OrderSMContext context) {logger.info("order id {} has delete {}", context.getOrderId(), smContainer.delete(context.getOrderId()));}}

2.4 状态机持久化类

利用 Redis 做状态机的持久化存储

2.4.1 Redis 状态机持久化容器

package org.example.config;import org.example.common.OrderEvent;
import org.example.common.OrderState;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.statemachine.data.redis.RedisStateMachineContextRepository;
import org.springframework.statemachine.persist.RepositoryStateMachinePersist;
import org.springframework.stereotype.Component;/*** @Author zhx && moon* @Since 21* @Date 2025-06-18 PM 6:54*/
@Component
public class MYRedisPerSisterConfig {@Autowiredprivate RedisConnectionFactory factory;/*** 创建 RedisStateMachineRepository 实例*/@Bean(name = "redisStateMachineContextRepository")public MYRedisStateMachinePer<OrderState, OrderEvent> getRedisPerSister() {// 创建 RedisStateMachineRepository 实例RedisStateMachineContextRepository<OrderState, OrderEvent> repository = new RedisStateMachineContextRepository<>(factory);// 持久化RepositoryStateMachinePersist perSister = new RepositoryStateMachinePersist(repository);// 获取 Redis StateMachinePerSister 实例MYRedisStateMachinePer machine = new MYRedisStateMachinePer<>(perSister);RedisTemplate<String, byte[]>  redisTemplate = createDefaultTemplate(factory);machine.setRedisTemplate(redisTemplate);// 返回return machine;}/*** 与 RedisStateMachineContextRepository 使用相同的序列化配置* @param connectionFactory* @return*/private static RedisTemplate<String, byte[]> createDefaultTemplate(RedisConnectionFactory connectionFactory) {RedisTemplate<String, byte[]> template = new RedisTemplate();template.setKeySerializer(new StringRedisSerializer());template.setHashKeySerializer(new StringRedisSerializer());template.setConnectionFactory(connectionFactory);template.afterPropertiesSet();return template;}
}

2.4.2 Redis 配置

package org.example.config;import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.statemachine.StateMachinePersist;
import org.springframework.statemachine.data.redis.RedisStateMachinePersister;/*** @Author zhx && moon* @Since 21* @Date 2025-06-19 PM 5:21*/
public class MYRedisStateMachinePer<S, E> extends RedisStateMachinePersister<S, E> {RedisTemplate<String, byte[]> redisTemplate;public MYRedisStateMachinePer(StateMachinePersist<S, E, String> stateMachinePersist) {super(stateMachinePersist);}public void setRedisTemplate(RedisTemplate<String, byte[]> redisTemplate){this.redisTemplate = redisTemplate;}/*** 检查 Redis 中是否存在指定的 key* @param key* @return*/public boolean isUnExist(String key){return !this.redisTemplate.hasKey(key);}/*** 删除 Redis 中指定的 key* @param key* @return*/public boolean deleteKey(String key){return this.redisTemplate.delete(key);}}

2.4.3 状态机监听器

用于监听状态机状态变化

package org.example.config;import org.example.common.OrderEvent;
import org.example.common.OrderState;
import org.example.entity.OrderSMContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.messaging.Message;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.listener.StateMachineListenerAdapter;
import org.springframework.statemachine.state.State;
import org.springframework.stereotype.Component;/*** @Author zhx && moon* @Since 21* @Date 2025-06-19 PM 4:58*/
@Component
public class RedStateMachineListener extends StateMachineListenerAdapter<OrderState, OrderEvent> {Logger logger = LoggerFactory.getLogger(this.getClass());@Autowiredprivate ApplicationEventPublisher publisher;/*** 状态变化* @param from* @param to*/@Overridepublic void stateChanged(State<OrderState, OrderEvent> from, State<OrderState, OrderEvent> to) {// 状态变更时的处理if (null == from) {logger.info("state machine init, from Init to {}", to.getId());} else {logger.info("state machine change, from {} to {}", from.getId(), to.getId());}}/*** 状态机启动成功时的回调* @param sm*/@Overridepublic void stateMachineStarted(StateMachine<OrderState, OrderEvent> sm) {logger.info("state machine {} start success.", sm.getId());}/*** 状态机结束的回调* @param sm*/@Overridepublic void stateMachineStopped(StateMachine<OrderState, OrderEvent> sm) {logger.info("state machine {} stop success.", sm.getId());publisher.publishEvent(new OrderSMContext(sm.getId()));}@Overridepublic void eventNotAccepted(Message<OrderEvent> event) {logger.error("Event not accepted: {}", event.getPayload());}}

2.5 装机器容器

用于管理状态机的创建,本地化缓存与持久化存储

package org.example.config;import org.example.common.OrderEvent;
import org.example.common.OrderState;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.config.StateMachineFactory;
import org.springframework.stereotype.Component;import java.util.HashMap;
import java.util.Map;
import java.util.Objects;/*** @Author zhx && moon* @Since 21* @Date 2025-06-19 PM 4:30*/
@Component
public class SMContainer {Logger logger = LoggerFactory.getLogger(this.getClass());@Autowiredprivate StateMachineFactory<OrderState, OrderEvent> factory;@Autowiredprivate MYRedisStateMachinePer<OrderState, OrderEvent> myRedisStateMachinePer;private Map<String, StateMachine<OrderState, OrderEvent>> map = new HashMap<>(16);/*** 获取状态机* @param orderId 订单ID* @return 状态机实例*/public synchronized StateMachine<OrderState, OrderEvent> getStateMachine(String orderId) {String key = getKey(orderId);try {// 取缓存StateMachine<OrderState, OrderEvent> sm = map.get(orderId);// 校验if (Objects.isNull(sm)) {// 获取状态机实例sm = factory.getStateMachine(orderId);// 校验是否存在if (myRedisStateMachinePer.isUnExist(key)) {sm.startReactively().subscribe();myRedisStateMachinePer.persist(sm, key);} else {// 恢复状态myRedisStateMachinePer.restore(sm, key);}// 缓存状态机map.put(orderId, sm);}return sm;} catch (Exception e) {logger.error("get state machine error: {}", e.getMessage(), e);return null;}}/*** 保存状态机* @param orderId* @param stateMachine*/public synchronized boolean save(StateMachine<OrderState, OrderEvent> stateMachine, String orderId){try {String key = getKey(orderId);myRedisStateMachinePer.persist(stateMachine, key);return true;} catch (Exception e) {logger.error("save state machine error: {}", e.getMessage(), e);return false;}}/*** 删除状态机* @param orderId* @return*/public synchronized boolean delete(String orderId){return myRedisStateMachinePer.deleteKey(getKey(orderId));}/*** 获取 KEY* @param orderId* @return*/private String getKey(String orderId){return "STATE-MACHINE:" +orderId;}}

2.6 状态机事件发送器

用于统一发送状态机事件,管理事件发送过程

package org.example.config;import org.example.common.OrderEvent;
import org.example.common.OrderState;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.StateMachineEventResult;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;import java.util.concurrent.atomic.AtomicBoolean;/*** @Author zhx && moon* @Since 21* @Date 2025-06-20 PM 2:22*/
@Component
public class SMEventSender {Logger logger = LoggerFactory.getLogger(this.getClass());@AutowiredSMContainer smContainer;/*** 初始化订单状态机* @param orderId*/public void initOrderStateMachine(String orderId) {smContainer.getStateMachine(orderId);}/*** 发送事件* @param orderId* @param event* @return*/public boolean send(String orderId, OrderEvent event) {// 获取状态StateMachine<OrderState, OrderEvent> sm = smContainer.getStateMachine(orderId);// 构建事件消息Message<OrderEvent> message = MessageBuilder.withPayload(event).setHeader("orderId", orderId) // 订单对象关联状态机.build();// 发送事件AtomicBoolean result = new AtomicBoolean(false);sm.sendEvent(Mono.just(message)).subscribe(r->{if (r.getResultType() == StateMachineEventResult.ResultType.ACCEPTED) {// 成功result.set(true);// 在未完成时持久化if (!OrderEvent.CONFIRM.equals(event)) {smContainer.save(sm, orderId);}} else {result.set(false);}});// 输出logger.info("send event: {}, orderId: {}, result: {}", event, orderId, result.get());// 返回return result.get();}}

2.7 状态机配置

状态机配置,定义事件和状态关系,以及守卫和动作

package org.example.config;import org.example.common.OrderEvent;
import org.example.common.OrderState;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.statemachine.config.EnableStateMachineFactory;
import org.springframework.statemachine.config.StateMachineConfigurerAdapter;
import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer;
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;import java.util.EnumSet;/*** @Author zhx && moon* @Since 21* @Date 2025-06-18 PM 6:38*/
@Configuration
@EnableStateMachineFactory
public class StateMachineConfig extends StateMachineConfigurerAdapter<OrderState, OrderEvent> {Logger logger = LoggerFactory.getLogger(this.getClass());@AutowiredRedStateMachineListener listener;@Overridepublic void configure(StateMachineConfigurationConfigurer<OrderState, OrderEvent> config) throws Exception {config.withConfiguration()// 注册监听器.listener(listener);}/*** 状态机初始化* @param states* @throws Exception*/@Overridepublic void configure(StateMachineStateConfigurer<OrderState, OrderEvent> states) throws Exception {states.withStates().initial(OrderState.UNPAID).states(EnumSet.allOf(OrderState.class)).end(OrderState.CONFIRMED).end(OrderState.CANCELLED);}/*** 状态转移逻辑* @param transitions* @throws Exception*/@Overridepublic void configure(StateMachineTransitionConfigurer<OrderState, OrderEvent> transitions) throws Exception {transitions// 支付:UNPAID -> PAID.withExternal().source(OrderState.UNPAID).target(OrderState.PAID).guard(context -> {// (前置)守卫条件,校验支付结果logger.info("check order {} pay result ...", context.getMessageHeader("orderId"));return true;}).action(context -> {// (后置)触发动作,通知仓库备货logger.info("order {} pay success, notify warehouse to prepare goods ...", context.getMessageHeader("orderId"));}).event(OrderEvent.PAY).and()// 发货:PAID -> SHIPPED.withExternal().source(OrderState.PAID).target(OrderState.SHIPPED).event(OrderEvent.SHIP).and()// 确认收货:SHIPPED -> CONFIRMED.withExternal().source(OrderState.SHIPPED).target(OrderState.CONFIRMED).event(OrderEvent.CONFIRM).and()// 取消订单(仅在待支付可取消).withExternal().source(OrderState.UNPAID).target(OrderState.CANCELLED).event(OrderEvent.CANCEL);}}

2.8 接口类

用于模拟订单操作

package org.example.controller;import org.example.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** @Author zhx && moon* @Since 21* @Date 2025-06-18 PM 6:41*/
@RestController
@RequestMapping("/orders")
public class OrderController {@Autowiredprivate OrderService orderService;@GetMapping("/create")public String create() {return orderService.createOrder();}@GetMapping("/pay/{orderId}")public String pay(@PathVariable("orderId") String orderId) {return orderService.payOrder(orderId);}@GetMapping("/shipped/{orderId}")public String shipped(@PathVariable("orderId") String orderId) {return orderService.shipped(orderId);}@GetMapping("/confirm/{orderId}")public String confirm(@PathVariable("orderId") String orderId) {return orderService.confirm(orderId);}@GetMapping("/{orderId}/status")public String status(@PathVariable String orderId) {return "当前状态: " + orderService.getOrderState(orderId);}}

2.9 实现类

package org.example.service;import org.example.common.OrderEvent;
import org.example.common.OrderState;
import org.example.config.SMEventSender;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;/*** @Author zhx && moon* @Since 21* @Date 2025-06-18 PM 6:40*/
@Service
public class OrderService {@AutowiredSMEventSender smEventSender;/*** 创建订单并初始化状态机* @return*/public String createOrder() {try {// 使用时间戳作为订单IDString orderId = String.valueOf(System.currentTimeMillis());// 初始化smEventSender.initOrderStateMachine(orderId);// 返回订单IDreturn orderId;} catch (Exception e) {return e.getMessage();}}/*** 支付* @param orderId* @return*/public String payOrder(String orderId) {try {boolean result = smEventSender.send(orderId, OrderEvent.PAY);return "success:" + result;} catch (Exception e) {return e.getMessage();}}/*** 发货* @param orderId* @return*/public String shipped(String orderId) {try {boolean result = smEventSender.send(orderId, OrderEvent.SHIP);return "success:" + result;} catch (Exception e) {return e.getMessage();}}/*** 确认收货* @param orderId* @return*/public String confirm(String orderId) {try {boolean result = smEventSender.send(orderId, OrderEvent.CONFIRM);return "success:" + result;} catch (Exception e) {return e.getMessage();}}public OrderState getOrderState(String orderId) {return OrderState.UNPAID;}
}

2.10 状态机上下文

用于管理状态机信息

package org.example.entity;import org.example.common.OrderState;import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;/*** @Author zhx && moon* @Since 21* @Date 2025-06-18 PM 6:54*/
public class OrderSMContext {public OrderSMContext(String orderId){this.orderId = orderId;}private String orderId;private OrderState currentState;private Map<String, Object> extendedState = new HashMap<>();private LocalDateTime createdAt;private LocalDateTime lastModifiedAt;public String getOrderId() {return orderId;}public void setOrderId(String orderId) {this.orderId = orderId;}
}

2.11 配置文件

spring:data:redis:database: 0host: 192.168.1.103port: 6379password: 123456timeout: 5000

3.状态机测试

3.1创建订单

在这里插入图片描述

3.2持久化结果

初始化时自动将数据持久化到 Redis

在这里插入图片描述

3.3 支付订单

在这里插入图片描述

3.4 发货

在这里插入图片描述

3.5 确认收货

在这里插入图片描述

后台日志

在这里插入图片描述

http://www.lryc.cn/news/576957.html

相关文章:

  • Linux下的调试器-gdb(16)
  • Tcpdump 网络抓包工具使用
  • ali PaddleNLP docker
  • Vivado关联Vscode
  • BUCK电感电流检测电路current sense-20250603
  • 逆向工程恢复信息的方法
  • JVM中的垃圾收集(GC)
  • 【个人纪录】vscode配置clangd
  • 节点小宝:告别公网IP,重塑你的远程连接体验
  • Vue列表渲染与数据监测原理
  • word换行居中以后 前面的下划线不显示
  • Python中的序列化和反序列化
  • 2个任务同时提交到YARN后2个都卡住(CDH)
  • CNN, RNN, LSTM
  • 四大WordPress模板资源网站
  • 【QT】信号和槽(1) 使用 || 定义
  • 数据结构复习4
  • stm32之测量周期
  • GPT,GPT-2,GPT-3 论文精读笔记
  • 各种常用的串口助手工具分享
  • vue-30(理解 Nuxt.js 目录结构)
  • Java 大视界 -- 基于 Java 的大数据分布式存储在科研大数据归档与长期保存中的应用(328)
  • TCP/UDP协议深度解析(三):TCP流量控制的魔法—滑动窗口、拥塞控制与ACK的智慧
  • 【AGI】Qwen VLo:多模态AI的范式重构与AGI演进关键里程碑
  • 数据可视化 - 单子图
  • LeetCode 第80题 删除有序数组中的重复项Ⅱ
  • 【如何实现分布式压测中间件】
  • Conda 环境配置之 -- Mamba安装(causal-conv1d、mamba_ssm 最简单配置方法)-- 不需要重新配置CDUA
  • MCPA2APPT 智能化演示文稿系统:A2A、MCP、ADK 三大架构全流程自动化
  • stm32之普通定时器