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

Spring StateMachine 框架详解

Spring StateMachine 框架详解

一、什么是 Spring StateMachine

Spring StateMachine 是 Spring 官方提供的状态机框架,用于在 Spring 应用中以声明式的方式定义和管理有限状态机。当前最新版本为 4.0.2,基于 Spring Framework 6.x 构建。

它的核心价值是将散落在 if/else 中的状态流转逻辑集中声明、统一管理,让状态转换规则成为可配置、可审计、可测试的一等公民。

Maven 依赖:

<dependency><groupId>org.springframework.statemachine</groupId><artifactId>spring-statemachine-starter</artifactId><version>4.0.2</version></dependency>

注:

博客:

https://blog.csdn.net/badao_liumang_qizhi

二、核心概念

概念说明类比
State(状态)对象当前所处的情形交通灯:红、黄、绿
Event(事件)触发状态变化的外部输入定时器到期、按钮按下
Transition(转换)从一个状态到另一个状态的路径红→绿
Guard(守卫条件)转换前的条件校验,返回 true 才允许转换库存 > 0 才能发货
Action(动作)转换过程中执行的业务逻辑发货时扣减库存
Entry Action进入某个状态时自动执行进入"已支付"状态时发短信
Exit Action离开某个状态时自动执行离开"待支付"状态时释放预占库存
Region(区域)并行状态的容器,一个状态机可以有多个区域同时运转订单同时有支付状态和物流状态
Hierarchical State(层次状态)状态可以嵌套,子状态继承父状态的转换“处理中"包含"拣货”、"打包"子状态

三、基本使用步骤

步骤1:定义状态和事件枚举

// 状态枚举publicenumOrderState{UNPAID,// 待支付PAID,// 已支付SHIPPED,// 已发货COMPLETED,// 已完成CANCELLED// 已取消}// 事件枚举publicenumOrderEvent{PAY,// 支付SHIP,// 发货CONFIRM,// 确认收货CANCEL// 取消}

步骤2:配置状态机

@Configuration@EnableStateMachinepublicclassOrderStateMachineConfigextendsEnumStateMachineConfigurerAdapter<OrderState,OrderEvent>{/** * 配置所有状态. */@Overridepublicvoidconfigure(StateMachineStateConfigurer<OrderState,OrderEvent>states)throwsException{states.withStates().initial(OrderState.UNPAID)// 初始状态.end(OrderState.COMPLETED)// 终态.end(OrderState.CANCELLED)// 终态.states(EnumSet.allOf(OrderState.class));// 所有状态}/** * 配置状态转换规则. */@Overridepublicvoidconfigure(StateMachineTransitionConfigurer<OrderState,OrderEvent>transitions)throwsException{transitions// 待支付 → 已支付(事件:PAY).withExternal().source(OrderState.UNPAID).target(OrderState.PAID).event(OrderEvent.PAY).action(payAction())// 转换时执行的动作.guard(payGuard())// 转换前的校验.and()// 已支付 → 已发货(事件:SHIP).withExternal().source(OrderState.PAID).target(OrderState.SHIPPED).event(OrderEvent.SHIP).and()// 已发货 → 已完成(事件:CONFIRM).withExternal().source(OrderState.SHIPPED).target(OrderState.COMPLETED).event(OrderEvent.CONFIRM).and()// 待支付 → 已取消.withExternal().source(OrderState.UNPAID).target(OrderState.CANCELLED).event(OrderEvent.CANCEL).and()// 已支付 → 已取消.withExternal().source(OrderState.PAID).target(OrderState.CANCELLED).event(OrderEvent.CANCEL);}}

步骤3:定义 Guard(守卫条件)

@BeanpublicGuard<OrderState,OrderEvent>payGuard(){returncontext->{// 从上下文中获取业务数据Objectamount=context.getExtendedState().getVariables().get("amount");if(amount==null){returnfalse;}// 校验支付金额大于0return((BigDecimal)amount).compareTo(BigDecimal.ZERO)>0;};}

Guard 返回true转换继续执行,返回false转换被拒绝(状态不变,不报错)。

步骤4:定义 Action(转换动作)

@BeanpublicAction<OrderState,OrderEvent>payAction(){returncontext->{// 转换过程中执行的业务逻辑IntegerorderId=(Integer)context.getExtendedState().getVariables().get("orderId");System.out.println("订单 "+orderId+" 支付成功,发送通知...");// 可以调用其他 Service};}

步骤5:业务中使用状态机

@ServicepublicclassOrderService{@AutowiredprivateStateMachine<OrderState,OrderEvent>stateMachine;publicvoidpayOrder(IntegerorderId,BigDecimalamount){// 设置业务上下文数据stateMachine.getExtendedState().getVariables().put("orderId",orderId);stateMachine.getExtendedState().getVariables().put("amount",amount);// 发送事件,状态机自动完成校验和转换booleanaccepted=stateMachine.sendEvent(OrderEvent.PAY);if(accepted){// 获取转换后的状态OrderStatecurrentState=stateMachine.getState().getId();System.out.println("当前状态: "+currentState);// PAID}else{System.out.println("状态转换被拒绝");}}}

四、监听器(Listener)

Spring StateMachine 支持通过监听器来观察状态机的各种行为:

@ComponentpublicclassOrderStateMachineListenerextendsStateMachineListenerAdapter<OrderState,OrderEvent>{@OverridepublicvoidstateChanged(State<OrderState,OrderEvent>from,State<OrderState,OrderEvent>to){System.out.println("状态变更: "+(from!=null?from.getId():"无")+" → "+to.getId());}@Overridepublicvoidtransition(Transition<OrderState,OrderEvent>transition){System.out.println("转换执行: "+transition.getSource().getId()+" → "+transition.getTarget().getId());}@OverridepublicvoideventNotAccepted(Message<OrderEvent>event){System.out.println("事件被拒绝: "+event.getPayload());}}

也可以使用注解方式:

@WithStateMachinepublicclassOrderStateMachineHandler{@OnTransition(source="UNPAID",target="PAID")publicvoidonPay(){System.out.println("支付完成");}@OnTransition(source="PAID",target="SHIPPED")publicvoidonShip(){System.out.println("已发货");}@OnStateEntry(target="CANCELLED")publicvoidonEnterCancelled(){System.out.println("订单取消,执行退款...");}}

五、状态持久化

问题:状态机实例是内存对象

默认情况下,状态机的当前状态保存在内存中。应用重启后状态丢失。对于业务系统(如订单),状态需要持久化到数据库。

方案一:StateMachinePersister(推荐)

核心思路:状态机只作为规则引擎使用,每次操作前从数据库恢复状态,操作后将新状态写回数据库。

@ComponentpublicclassOrderStateMachinePersister{@AutowiredprivateStateMachine<OrderState,OrderEvent>stateMachine;@AutowiredprivateStateMachinePersister<OrderState,OrderEvent,Integer>persister;/** * 发送事件并持久化. */publicbooleansendEvent(IntegerorderId,OrderEventevent){try{// 1. 从数据库恢复状态机到该订单的状态persister.restore(stateMachine,orderId);// 2. 发送事件booleanresult=stateMachine.sendEvent(event);// 3. 将新状态持久化回数据库if(result){persister.persist(stateMachine,orderId);}returnresult;}catch(Exceptione){thrownewRuntimeException("状态机操作失败",e);}}}

自定义持久化适配器

@ComponentpublicclassOrderStateMachineContextPersistimplementsStateMachinePersist<OrderState,OrderEvent,Integer>{@AutowiredprivateOrderRepositoryorderRepository;@Overridepublicvoidwrite(StateMachineContext<OrderState,OrderEvent>context,IntegerorderId){// 将状态写入数据库Orderorder=orderRepository.findById(orderId).orElseThrow();order.setStatus(context.getState().name());orderRepository.save(order);}@OverridepublicStateMachineContext<OrderState,OrderEvent>read(IntegerorderId){// 从数据库读取状态Orderorder=orderRepository.findById(orderId).orElseThrow();OrderStatestate=OrderState.valueOf(order.getStatus());returnnewDefaultStateMachineContext<>(state,null,null,null);}}

方案二:spring-statemachine-data-jpa

框架提供的 JPA 持久化模块,自动创建表存储状态机配置和运行状态:

<dependency><groupId>org.springframework.statemachine</groupId><artifactId>spring-statemachine-data-jpa</artifactId></dependency>

自动创建的表:

  • STATE_MACHINE:状态机实例
  • STATE:状态定义
  • TRANSITION:转换定义
  • ACTION:动作定义
  • GUARD:守卫定义

六、转换类型

Spring StateMachine 支持三种转换类型:

External(外部转换)

最常用,状态发生实际变化:

.withExternal().source(OrderState.UNPAID).target(OrderState.PAID).event(OrderEvent.PAY)

Internal(内部转换)

状态不变,但执行动作(用于"在当前状态下响应某个事件但不离开"):

.withInternal().source(OrderState.UNPAID).event(OrderEvent.REMIND)// 催付提醒.action(sendRemindAction())// 发送提醒但状态不变

Local(本地转换)

用于层次状态中,子状态之间的转换不触发父状态的 exit/entry:

.withLocal().source(OrderState.PROCESSING_PICK).target(OrderState.PROCESSING_PACK).event(OrderEvent.PICK_DONE)

七、层次状态(Hierarchical States)

状态可以嵌套,用于表达复杂流程中的子阶段:

@Overridepublicvoidconfigure(StateMachineStateConfigurer<OrderState,OrderEvent>states)throwsException{states.withStates().initial(OrderState.UNPAID).state(OrderState.PROCESSING)// 父状态.end(OrderState.COMPLETED).and().withStates().parent(OrderState.PROCESSING)// 嵌套在 PROCESSING 下.initial(OrderState.PICKING)// 子状态初始:拣货.state(OrderState.PACKING)// 子状态:打包.end(OrderState.READY);// 子状态终态:就绪}

状态图:

UNPAID → PROCESSING → COMPLETED │ ├── PICKING → PACKING → READY │ (子状态)

八、并行区域(Regions)

一个对象同时有多个独立的状态维度:

states.withStates().initial(OrderState.NEW).fork(OrderState.FORK).join(OrderState.JOIN).state(OrderState.DONE).and()// 区域1:支付流程.withStates().parent(OrderState.FORK).initial(OrderState.PAY_PENDING).end(OrderState.PAY_DONE).and()// 区域2:库存流程.withStates().parent(OrderState.FORK).initial(OrderState.STOCK_PENDING).end(OrderState.STOCK_DONE);

Fork/Join 用于并行区域的分叉和汇合,两个区域都到达终态后才合并继续。


九、Extended State(扩展状态)

除了有限的状态枚举外,状态机还可以携带业务数据上下文

// 写入扩展状态stateMachine.getExtendedState().getVariables().put("orderId",12345);stateMachine.getExtendedState().getVariables().put("retryCount",0);// 在 Guard 或 Action 中读取@BeanpublicGuard<OrderState,OrderEvent>retryGuard(){returncontext->{IntegerretryCount=(Integer)context.getExtendedState().getVariables().get("retryCount");returnretryCount<3;// 重试不超过3次};}

十、Spring StateMachine vs 隐式状态机

维度Spring StateMachine隐式状态机(if/else)
规则集中度所有转换规则在配置类中一目了然散落在各业务方法中
非法转换防护框架自动拒绝未定义的转换依赖开发者手动校验
动作绑定Action 与转换声明式绑定业务逻辑和状态修改混在一起
可视化可以导出状态图(UML)需要人工画图
学习成本需要学习框架API和概念零学习成本
运行时开销有框架对象创建和管理开销几乎无开销
并发安全框架内置处理需要自行加锁
适用场景状态多(>5)、转换复杂、需要审计状态少(<5)、流转简单直接
持久化需要额外配置持久化策略天然就是数据库字段
调试通过监听器追踪完整转换链路打断点跟踪散落的代码

十一、实际项目中的选型建议

适合用 Spring StateMachine 的场景

  • 状态数量多(7个以上),转换路径复杂
  • 需要层次状态或并行区域
  • 状态转换需要严格的审计日志
  • 多人协作,需要规则集中可读
  • 状态机需要动态修改(从数据库加载配置)

适合用隐式状态机的场景

  • 状态少(3-5个),流转路径线性
  • 转换规则稳定不常变化
  • 项目已经用隐式方式写了大量代码,迁移成本高
  • 性能敏感,不想引入框架开销

十二、总结

Spring StateMachine 本质上提供的是:

  1. 声明式规则定义— 状态和转换关系集中配置
  2. 自动校验— 未定义的转换自动拒绝
  3. 生命周期钩子— Entry/Exit/Transition Action 和 Guard
  4. 可观察性— Listener 监听所有状态变更事件
  5. 持久化支持— 状态可以保存到数据库并恢复
  6. 高级特性— 层次状态、并行区域、定时触发器

核心思想是把隐含在代码逻辑中的状态规则显式化,使其成为可配置、可审计、可测试的独立关注点。

http://www.jsqmd.com/news/1293033/

相关文章:

  • MediaPipe多模态机器学习框架:架构解析与跨平台实践
  • 家长拿着网上热门推荐的配置单来问价
  • 2026靠谱的论文发表快速见刊哪里好
  • 2026年物流大件收费标准是什么?寄大件怎么最省钱? - 快递物流资讯
  • 投标用的性能测试报告加急上门能做吗
  • 2026老婆饼哪个品牌口感不腻:专业选型盘点指南 靠谱服务商避坑攻略及适配参考 - 产业观察报
  • 位运算技巧在算法优化中的潜在价值挖掘
  • 大模型全球第一,居然是小米
  • 告别多开OBS的烦恼:obs-multi-rtmp插件实现一键多平台直播
  • 璟安黄金回收:巴中市七家贵金属回收推荐 - 新芸鼎珠宝首饰
  • 政府主导的医疗健康技术交易平台,如何确保成果评估的客观性和公平性?
  • Xbox 服务中断致光盘游戏无法玩,即将更新修复授权信息使用问题
  • DeepSeek模型升级解析:128k上下文与北大实习生技术突破
  • 2026年螺母输送机厂家评测:代表性品牌解析与选购指南 - 汇聚至此
  • 为什么团队每天很忙,项目却没有推进?
  • LiveBindings Designer或Wizard,编程创建绑定。 . 使用Fluent LiveBindings简化绑定代码 ...
  • 从零实现平台无关的FFT算法:原理、代码与嵌入式移植指南
  • 旧黄金翻新和回收区别!成都锦江逸程精准估价守护收益 - 融媒生活
  • 2026焊接预埋件厂家推荐排行榜从产能到质量,这些企业值得 - 栈上春秋
  • C++20核心特性解析:Ranges、Concepts、Coroutines与Modules实战指南
  • 从零搭建可商用AI转场工作流:CUDA 12.4 + PyTorch 2.3 + FFmpeg NVENC硬编全流程实测(吞吐提升3.8倍)
  • 多场景适用:2026高精三维地图定位系统推荐 - 品牌深度评测
  • GetQzonehistory:终极QQ空间历史数据备份工具完全指南
  • 云县家具怎么选?本地家具门店盘点+选购避坑全攻略 - 国麟测评
  • CefFlashBrowser:终极Flash浏览器,简单三步重温经典Flash游戏
  • 债权转让公告登报线上办理指南,稿件审核快,当日可安排 - 叮咚办真方便
  • 2026年多品牌CRM综合实力研判,企业数字化优选参考 - 毛毛鱼的夏天
  • FPGA高速收发器GTX核心原理、硬件设计与调试实战指南
  • SaaS数据范式演进:从工具到智能的核心能力解析
  • 2026早餐零食品牌排行大全:正规合规实力强口碑佳的品牌详解与避坑FAQ - 行业观察网