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.删除状态禁止直接物理删除删除状态会破坏版本透明性应该标记为废弃保留转换定义但引导至新的状态

相关新闻

AI核心概念扫盲:从大模型到Agent的一站式入门指南

AI核心概念扫盲:从大模型到Agent的一站式入门指南

AI核心概念扫盲:从大模型到Agent的一站式入门指南 掌握这条链路,让你彻底搞懂AI热词不再发懵。 学AI工具时,最让人头疼的往往不是“不会用”,而是扑面而来的各种名词:大语言模型、Token、上下文、Prompt、工具调用、MC…

2026/6/28 1:58:12阅读更多 →
用参考图引导的扩散模型提升重选封面帧画质

用参考图引导的扩散模型提升重选封面帧画质

背景在手机摄影中,Live Photo 早已成为一种常见而成熟的拍摄形式,它不再只是定格一个画面,而是记录“此时此刻”的状态与情绪,捕捉那些转瞬即逝的精彩瞬间。它由一张高清封面图和一段短视频共同组成,既保留了照片的清晰…

2026/6/28 1:58:12阅读更多 →
Java毕业设计-基于 Spring Boot 的社区生鲜团购平台的设计与实现 基于 Web 的社区生鲜团购管理系统的设计与实现(源码+LW+部署文档+全bao+远程调试+代码讲解等)

Java毕业设计-基于 Spring Boot 的社区生鲜团购平台的设计与实现 基于 Web 的社区生鲜团购管理系统的设计与实现(源码+LW+部署文档+全bao+远程调试+代码讲解等)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围:&am…

2026/6/28 1:53:12阅读更多 →
YOLO注意力机制改进- 第32篇:TripletAttention三重注意力的空间交互

YOLO注意力机制改进- 第32篇:TripletAttention三重注意力的空间交互

一、引言 注意力机制在计算机视觉领域的成功,很大程度上源于其对特征空间的自适应建模能力。从最早的通道注意力(SENet),到通道与空间相结合的注意力(CBAM、BAM),再到后来的坐标注意力(CA),注意力机制的演进始终围绕着一个核心问题:如何以尽可能低的计算成本,捕获…

2026/6/28 5:23:22阅读更多 →
好客搜智搜GEO优化系统:AI搜索时代企业品牌可见性的新引擎

好客搜智搜GEO优化系统:AI搜索时代企业品牌可见性的新引擎

智搜GEO:AI搜索时代企业品牌可见性的新引擎 随着DeepSeek、豆包、文心一言、通义千问等AI大模型的普及,用户获取信息的方式正在发生根本性变革。数据显示,2025年以来,超过40%的互联网用户开始通过AI对话平台获取日常信息&#xf…

2026/6/28 5:23:22阅读更多 →
P11942 [KTSC 2025] 重塑矩阵 题解

P11942 [KTSC 2025] 重塑矩阵 题解

Solution看到 0101 矩阵,一个经典的转化是转化成二分图:建立 nn 个行点 R0∼Rn−1R0​∼Rn−1​ 与 nn 个列点 C0∼Cn−1C0​∼Cn−1​,Ai,jAi,j​ 表示一条连接 Ri,CjRi​,Cj​ 的边。在此基础上可以想到两种建图方法:Method 1建…

2026/6/28 5:23:22阅读更多 →
影刀RPA新手教程:MySQL数据库操作完全指南——连接配置、CRUD实战与批量插入优化

影刀RPA新手教程:MySQL数据库操作完全指南——连接配置、CRUD实战与批量插入优化

影刀RPA新手教程:MySQL数据库操作完全指南——连接配置、CRUD实战与批量插入优化 本文作者:林焱 | 转载请注明出处 开篇案例:MySQL连接池耗尽,整个RPA系统停了 去年维护一个电商RPA系统,10个机器人同时跑,…

2026/6/28 5:23:22阅读更多 →
科幻作家为何钟情 WordStar?解析其优于 WordPerfect 等软件的独特优势

科幻作家为何钟情 WordStar?解析其优于 WordPerfect 等软件的独特优势

为盲打者设计的界面许多科幻作家,包括罗伯特J索耶、罗杰麦克布赖德艾伦、杰拉尔德布兰特等,都继续选择使用适用于 DOS 的 WordStar 作为写作工具。不过,多年来,他们的这一选择常常遭到无端批评,批评者大多是 WordPerfe…

2026/6/28 5:23:22阅读更多 →
预编译知识

预编译知识

只有定义了AAA下面这句话才会编译

2026/6/28 5:18:22阅读更多 →
AI Coding 六个月真实ROI账本:产品经理的血泪教训,研发的冷静忠告

AI Coding 六个月真实ROI账本:产品经理的血泪教训,研发的冷静忠告

6个月前的2025年12月,Boris Cherny 公开宣布自己卸载了 IDE。一时间,Vibe Coding 成了全行业最热的话题。6个月后,当我们回过头来拉一份真实账本,发现事情远没有"一句话生成一个App"那么浪漫。本文从产品经理和研发两个…

2026/6/28 0:08:01阅读更多 →
审计来了,数据权限全开——审计走了,怎么确保权限全部关掉?

审计来了,数据权限全开——审计走了,怎么确保权限全部关掉?

引言:审计结束三个月了,审计员的权限还没关某城商行每年按照监管要求开展至少一次数据安全审计。审计期间,内审部门需要抽样检查各类业务数据——交易流水、客户信息、员工操作日志、权限配置记录。这些数据分布在不同系统中,审计…

2026/6/28 0:08:01阅读更多 →
AI Coding 六个月真实ROI账本:产品经理的血泪教训,研发的冷静忠告

AI Coding 六个月真实ROI账本:产品经理的血泪教训,研发的冷静忠告

6个月前的2025年12月,Boris Cherny 公开宣布自己卸载了 IDE。一时间,Vibe Coding 成了全行业最热的话题。6个月后,当我们回过头来拉一份真实账本,发现事情远没有"一句话生成一个App"那么浪漫。本文从产品经理和研发两个…

2026/6/28 0:08:01阅读更多 →
审计来了,数据权限全开——审计走了,怎么确保权限全部关掉?

审计来了,数据权限全开——审计走了,怎么确保权限全部关掉?

引言:审计结束三个月了,审计员的权限还没关某城商行每年按照监管要求开展至少一次数据安全审计。审计期间,内审部门需要抽样检查各类业务数据——交易流水、客户信息、员工操作日志、权限配置记录。这些数据分布在不同系统中,审计…

2026/6/28 0:08:01阅读更多 →