Spring Statemachine(状态机)

📅 2026/6/28 1:57:39
Spring Statemachine(状态机)
什么是状态机状态机是状态模式的一种应用相当于上下文角色的一个升级版。在工作流 或游戏等各种系统中有大量使用如各种工作流引擎它几乎是状态机的子集和实现封装状态的变化规则。状态机可以帮助开发者简化状态控制的开发过程让状态机结构更加层次化。状态机的构成要件把状态机的要素分为4个要素即现态、条件、动作、次态。“现态”和“条件”是因“动作”和“次态”是果。1现态是指当前所处状态2条件又称为“事件”。当条件被满足时将会触发一个动作或者执行一次状态的迁移。3动作条件满足后执行的动作。动作不是必须的当条件满足后也可以不执行任何动作直接迁移到新状态。4次态条件满足后要迁移往的新状态。“次态”是相对于“现态”而言的“次态”一旦被激活就转变成新的“现态”了。以电商场景中订单状态流转为例在电商平台中一个订单会有多种状态临时单、已下单、待支付、已支付、待发货、待收货、已完成等等。每一种状态都和变化前的状态以及执行的操作有关。比如用户将商品加入购物车后后台会生成一个所谓的“临时单”。因为用户还没有点击下单所以这个订单实际上还没有生成。只有当用户下单后这个“临时单”才会转化为一个“待支付的订单”。以上过程中只有将一个处于“临时单”状态的订单执行下单操作才能得到一个状态为“待支付”的订单。 即一个前置状态一个恰当的操作才能流转订单的状态。在这个过程中如果使用硬编码我们就需要一系列的 if-else 语句来检查订单的当前状态、可执行操作以及这两个组合得到的下一个应该被流转的状态值。如果订单的状态流转很复杂代码逻辑就会很复杂可读性低后期维护困难。处理以上问题我们可以使用状态设计模式 来处理。对应到实践就是状态机。代码实现Spring 提供了一个很好的解决方案Spring Statemachine状态机是应用程序开发人员在 Spring 应用程序中使用状态机概念的框架。1.添加依赖dependencygroupIdorg.springframework.statemachine/groupIdartifactIdspring-statemachine-core/artifactIdversion2.0.1.RELEASE/version/dependency2.创建订单实体类packagenet.biancheng.c.entity;publicclassOrder{privateintid;privateOrderStatusstatus;publicvoidsetStatus(OrderStatusstatus){this.statusstatus;}publicOrderStatusgetStatus(){returnstatus;}publicvoidsetId(intid){this.idid;}publicintgetId(){returnid;}OverridepublicStringtoString(){return订单号id, 订单状态status;}}3.创建订单状态枚举类和状态转换枚举类packagenet.biancheng.c.utils;/** * 订单状态 */publicenumOrderStatus{// 待支付待发货待收货已完成WAIT_PAYMENT,WAIT_DELIVER,WAIT_RECEIVE,FINISH;}packagenet.biancheng.c.utils;/** * 订单状态改变事件 */publicenumOrderStatusChangeEvent{// 支付发货确认收货PAYED,DELIVERY,RECEIVED;}4…添加状态流配置packagenet.biancheng.c.utils;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.statemachine.StateMachineContext;importorg.springframework.statemachine.StateMachinePersist;importorg.springframework.statemachine.config.EnableStateMachine;importorg.springframework.statemachine.config.StateMachineConfigurerAdapter;importorg.springframework.statemachine.config.builders.StateMachineStateConfigurer;importorg.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;importorg.springframework.statemachine.persist.DefaultStateMachinePersister;importorg.springframework.statemachine.support.DefaultStateMachineContext;importjava.util.EnumSet;/** * 订单状态机配置 */ConfigurationEnableStateMachine(nameorderStateMachine)publicclassOrderStateMachineConfigextendsStateMachineConfigurerAdapterOrderStatus,OrderStatusChangeEvent{/** * 配置状态 * * param states * throws Exception */publicvoidconfigure(StateMachineStateConfigurerOrderStatus,OrderStatusChangeEventstates)throwsException{states.withStates().initial(OrderStatus.WAIT_PAYMENT).states(EnumSet.allOf(OrderStatus.class));}/** * 配置状态转换事件关系 * * param transitions * throws Exception */publicvoidconfigure(StateMachineTransitionConfigurerOrderStatus,OrderStatusChangeEventtransitions)throwsException{transitions.withExternal().source(OrderStatus.WAIT_PAYMENT).target(OrderStatus.WAIT_DELIVER).event(OrderStatusChangeEvent.PAYED).and().withExternal().source(OrderStatus.WAIT_DELIVER).target(OrderStatus.WAIT_RECEIVE).event(OrderStatusChangeEvent.DELIVERY).and().withExternal().source(OrderStatus.WAIT_RECEIVE).target(OrderStatus.FINISH).event(OrderStatusChangeEvent.RECEIVED);}/** * 持久化配置 * 实际使用中可以配合redis等进行持久化操作 * * return */BeanpublicDefaultStateMachinePersisterpersister(){returnnewDefaultStateMachinePersister(newStateMachinePersistObject,Object,Order(){Overridepublicvoidwrite(StateMachineContextObject,Objectcontext,Orderorder)throwsException{//此处并没有进行持久化操作//此处可调用更新状态流转接口(如更新时间状态不需要在此进行调用}OverridepublicStateMachineContextObject,Objectread(Orderorder)throwsException{//此处直接获取order中的状态其实并没有进行持久化读取操作returnnewDefaultStateMachineContext(order.getStatus(),null,null,null);}});}}5.添加状态监听器packagenet.biancheng.c.utils;importorg.springframework.messaging.Message;importorg.springframework.statemachine.annotation.OnTransition;importorg.springframework.statemachine.annotation.WithStateMachine;importorg.springframework.stereotype.Component;Component(orderStateListener)WithStateMachine(nameorderStateMachine)publicclassOrderStateListenerImpl{OnTransition(sourceWAIT_PAYMENT,targetWAIT_DELIVER)publicbooleanpayTransition(MessageOrderStatusChangeEventmessage){Orderorder(Order)message.getHeaders().get(order);order.setStatus(OrderStatus.WAIT_DELIVER);System.out.println(支付状态机反馈信息message.getHeaders().toString());returntrue;}OnTransition(sourceWAIT_DELIVER,targetWAIT_RECEIVE)publicbooleandeliverTransition(MessageOrderStatusChangeEventmessage){Orderorder(Order)message.getHeaders().get(order);order.setStatus(OrderStatus.WAIT_RECEIVE);System.out.println(发货状态机反馈信息message.getHeaders().toString());returntrue;}OnTransition(sourceWAIT_RECEIVE,targetFINISH)publicbooleanreceiveTransition(MessageOrderStatusChangeEventmessage){Orderorder(Order)message.getHeaders().get(order);order.setStatus(OrderStatus.FINISH);System.out.println(收货状态机反馈信息message.getHeaders().toString());returntrue;}}6.创建 OrderService 接口 可用可不用根据现实情况分析packagenet.biancheng.c.service;importnet.biancheng.c.entity.Order;importjava.util.Map;publicinterfaceOrderService{//创建订单Ordercreate();//发起支付Orderpay(intid);//订单发货Orderdeliver(intid);//订单收货Orderreceive(intid);//获取所有订单信息MapInteger,OrdergetOrders();}7.在 Service 业务逻辑 中应用packagenet.biancheng.c.serviceImpl;importnet.biancheng.c.entity.Order;importnet.biancheng.c.service.OrderService;importnet.biancheng.c.utils.OrderStatus;importnet.biancheng.c.utils.OrderStatusChangeEvent;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.messaging.Message;importorg.springframework.messaging.support.MessageBuilder;importorg.springframework.statemachine.StateMachine;importorg.springframework.statemachine.persist.StateMachinePersister;importorg.springframework.stereotype.Service;importjava.util.HashMap;importjava.util.Map;Service(orderService)publicclassOrderServiceImplimplementsOrderService{AutowiredprivateStateMachineOrderStatus,OrderStatusChangeEventorderStateMachine;AutowiredprivateStateMachinePersisterOrderStatus,OrderStatusChangeEvent,Orderpersister;privateintid1;privateMapInteger,OrderordersnewHashMap();publicOrdercreate(){OrderordernewOrder();order.setStatus(OrderStatus.WAIT_PAYMENT);order.setId(id);orders.put(order.getId(),order);returnorder;}publicOrderpay(intid){Orderorderorders.get(id);System.out.println(线程名称Thread.currentThread().getName() 尝试支付订单号id);MessagemessageMessageBuilder.withPayload(OrderStatusChangeEvent.PAYED).setHeader(order,order).build();if(!sendEvent(message,order)){System.out.println(线程名称Thread.currentThread().getName() 支付失败, 状态异常订单号id);}returnorders.get(id);}publicOrderdeliver(intid){Orderorderorders.get(id);System.out.println(线程名称Thread.currentThread().getName() 尝试发货订单号id);if(!sendEvent(MessageBuilder.withPayload(OrderStatusChangeEvent.DELIVERY).setHeader(order,order).build(),orders.get(id))){System.out.println(线程名称Thread.currentThread().getName() 发货失败状态异常订单号id);}returnorders.get(id);}publicOrderreceive(intid){Orderorderorders.get(id);System.out.println(线程名称Thread.currentThread().getName() 尝试收货订单号id);if(!sendEvent(MessageBuilder.withPayload(OrderStatusChangeEvent.RECEIVED).setHeader(order,order).build(),orders.get(id))){System.out.println(线程名称Thread.currentThread().getName() 收货失败状态异常订单号id);}returnorders.get(id);}publicMapInteger,OrdergetOrders(){returnorders;}/** * 发送订单状态转换事件 * * param message * param order * return */privatesynchronizedbooleansendEvent(MessageOrderStatusChangeEventmessage,Orderorder){booleanresultfalse;try{orderStateMachine.start();//尝试恢复状态机状态persister.restore(orderStateMachine,order);//添加延迟用于线程安全测试Thread.sleep(1000);resultorderStateMachine.sendEvent(message);//持久化状态机状态persister.persist(orderStateMachine,order);}catch(Exceptione){e.printStackTrace();}finally{orderStateMachine.stop();}returnresult;}}思考在Java状态机中增删状态要保证线上不出问题核心是版本化与兼容性设计1.数据携带版本号在持久化数据中显示记录状态机版本处理时根据版本路由到对应逻辑2.新增状态时尽量作为原有状态的扩展而非替换旧数据应能按原有路径正常流转3.删除状态禁止直接物理删除删除状态会破坏版本透明性应该标记为废弃保留转换定义但引导至新的状态