Spring StateMachine 框架详解
Spring StateMachine 框架详解一、什么是 Spring StateMachineSpring StateMachine 是 Spring 官方提供的状态机框架用于在 Spring 应用中以声明式的方式定义和管理有限状态机。当前最新版本为 4.0.2基于 Spring Framework 6.x 构建。它的核心价值是将散落在 if/else 中的状态流转逻辑集中声明、统一管理让状态转换规则成为可配置、可审计、可测试的一等公民。Maven 依赖dependencygroupIdorg.springframework.statemachine/groupIdartifactIdspring-statemachine-starter/artifactIdversion4.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配置状态机ConfigurationEnableStateMachinepublicclassOrderStateMachineConfigextendsEnumStateMachineConfigurerAdapterOrderState,OrderEvent{/** * 配置所有状态. */Overridepublicvoidconfigure(StateMachineStateConfigurerOrderState,OrderEventstates)throwsException{states.withStates().initial(OrderState.UNPAID)// 初始状态.end(OrderState.COMPLETED)// 终态.end(OrderState.CANCELLED)// 终态.states(EnumSet.allOf(OrderState.class));// 所有状态}/** * 配置状态转换规则. */Overridepublicvoidconfigure(StateMachineTransitionConfigurerOrderState,OrderEventtransitions)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守卫条件BeanpublicGuardOrderState,OrderEventpayGuard(){returncontext-{// 从上下文中获取业务数据Objectamountcontext.getExtendedState().getVariables().get(amount);if(amountnull){returnfalse;}// 校验支付金额大于0return((BigDecimal)amount).compareTo(BigDecimal.ZERO)0;};}Guard 返回true转换继续执行返回false转换被拒绝状态不变不报错。步骤4定义 Action转换动作BeanpublicActionOrderState,OrderEventpayAction(){returncontext-{// 转换过程中执行的业务逻辑IntegerorderId(Integer)context.getExtendedState().getVariables().get(orderId);System.out.println(订单 orderId 支付成功发送通知...);// 可以调用其他 Service};}步骤5业务中使用状态机ServicepublicclassOrderService{AutowiredprivateStateMachineOrderState,OrderEventstateMachine;publicvoidpayOrder(IntegerorderId,BigDecimalamount){// 设置业务上下文数据stateMachine.getExtendedState().getVariables().put(orderId,orderId);stateMachine.getExtendedState().getVariables().put(amount,amount);// 发送事件状态机自动完成校验和转换booleanacceptedstateMachine.sendEvent(OrderEvent.PAY);if(accepted){// 获取转换后的状态OrderStatecurrentStatestateMachine.getState().getId();System.out.println(当前状态: currentState);// PAID}else{System.out.println(状态转换被拒绝);}}}四、监听器ListenerSpring StateMachine 支持通过监听器来观察状态机的各种行为ComponentpublicclassOrderStateMachineListenerextendsStateMachineListenerAdapterOrderState,OrderEvent{OverridepublicvoidstateChanged(StateOrderState,OrderEventfrom,StateOrderState,OrderEventto){System.out.println(状态变更: (from!null?from.getId():无) → to.getId());}Overridepublicvoidtransition(TransitionOrderState,OrderEventtransition){System.out.println(转换执行: transition.getSource().getId() → transition.getTarget().getId());}OverridepublicvoideventNotAccepted(MessageOrderEventevent){System.out.println(事件被拒绝: event.getPayload());}}也可以使用注解方式WithStateMachinepublicclassOrderStateMachineHandler{OnTransition(sourceUNPAID,targetPAID)publicvoidonPay(){System.out.println(支付完成);}OnTransition(sourcePAID,targetSHIPPED)publicvoidonShip(){System.out.println(已发货);}OnStateEntry(targetCANCELLED)publicvoidonEnterCancelled(){System.out.println(订单取消执行退款...);}}五、状态持久化问题状态机实例是内存对象默认情况下状态机的当前状态保存在内存中。应用重启后状态丢失。对于业务系统如订单状态需要持久化到数据库。方案一StateMachinePersister推荐核心思路状态机只作为规则引擎使用每次操作前从数据库恢复状态操作后将新状态写回数据库。ComponentpublicclassOrderStateMachinePersister{AutowiredprivateStateMachineOrderState,OrderEventstateMachine;AutowiredprivateStateMachinePersisterOrderState,OrderEvent,Integerpersister;/** * 发送事件并持久化. */publicbooleansendEvent(IntegerorderId,OrderEventevent){try{// 1. 从数据库恢复状态机到该订单的状态persister.restore(stateMachine,orderId);// 2. 发送事件booleanresultstateMachine.sendEvent(event);// 3. 将新状态持久化回数据库if(result){persister.persist(stateMachine,orderId);}returnresult;}catch(Exceptione){thrownewRuntimeException(状态机操作失败,e);}}}自定义持久化适配器ComponentpublicclassOrderStateMachineContextPersistimplementsStateMachinePersistOrderState,OrderEvent,Integer{AutowiredprivateOrderRepositoryorderRepository;Overridepublicvoidwrite(StateMachineContextOrderState,OrderEventcontext,IntegerorderId){// 将状态写入数据库OrderorderorderRepository.findById(orderId).orElseThrow();order.setStatus(context.getState().name());orderRepository.save(order);}OverridepublicStateMachineContextOrderState,OrderEventread(IntegerorderId){// 从数据库读取状态OrderorderorderRepository.findById(orderId).orElseThrow();OrderStatestateOrderState.valueOf(order.getStatus());returnnewDefaultStateMachineContext(state,null,null,null);}}方案二spring-statemachine-data-jpa框架提供的 JPA 持久化模块自动创建表存储状态机配置和运行状态dependencygroupIdorg.springframework.statemachine/groupIdartifactIdspring-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(StateMachineStateConfigurerOrderState,OrderEventstates)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 中读取BeanpublicGuardOrderState,OrderEventretryGuard(){returncontext-{IntegerretryCount(Integer)context.getExtendedState().getVariables().get(retryCount);returnretryCount3;// 重试不超过3次};}十、Spring StateMachine vs 隐式状态机维度Spring StateMachine隐式状态机if/else规则集中度所有转换规则在配置类中一目了然散落在各业务方法中非法转换防护框架自动拒绝未定义的转换依赖开发者手动校验动作绑定Action 与转换声明式绑定业务逻辑和状态修改混在一起可视化可以导出状态图UML需要人工画图学习成本需要学习框架API和概念零学习成本运行时开销有框架对象创建和管理开销几乎无开销并发安全框架内置处理需要自行加锁适用场景状态多(5)、转换复杂、需要审计状态少(5)、流转简单直接持久化需要额外配置持久化策略天然就是数据库字段调试通过监听器追踪完整转换链路打断点跟踪散落的代码十一、实际项目中的选型建议适合用 Spring StateMachine 的场景状态数量多7个以上转换路径复杂需要层次状态或并行区域状态转换需要严格的审计日志多人协作需要规则集中可读状态机需要动态修改从数据库加载配置适合用隐式状态机的场景状态少3-5个流转路径线性转换规则稳定不常变化项目已经用隐式方式写了大量代码迁移成本高性能敏感不想引入框架开销十二、总结Spring StateMachine 本质上提供的是声明式规则定义— 状态和转换关系集中配置自动校验— 未定义的转换自动拒绝生命周期钩子— Entry/Exit/Transition Action 和 Guard可观察性— Listener 监听所有状态变更事件持久化支持— 状态可以保存到数据库并恢复高级特性— 层次状态、并行区域、定时触发器核心思想是把隐含在代码逻辑中的状态规则显式化使其成为可配置、可审计、可测试的独立关注点。