彻底搞懂Spring状态机原理,实现订单与物流解耦

订单有限状态机架构设计 有限状态机 有限状态机(Finite-state machine, FSM),是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。主要用来是描述对象在它的生命周期内所经历的状态序列,以及如何响应来自外界的各种事件。 状态机可归纳为4个要素,即现态、条件、动作、新态。其中“现态”和“条件”是因,“动作”和“次态”是果。 **现态:**是指当前所处的状态。 **事件:**又称为“条件”。当一个事件发生,将会触发一个动作,或者执行一次状态的迁移。 **动作:**条件满足后执行的动作。动作执 阅读详情

1 状态模式的UML类图#

状态模式的UML类图如下图所示。

file

2 使用状态模式实现登录状态自由切换#

当我们在社区阅读文章时,如果觉得文章写得很好,我们就会评论、收藏两连发。如果处于登录情况下,则可以直接做评论、收藏这些行为。否则,跳转到登录界面,登录后再继续执行先前的动作。这里涉及的状态有两种:登录与未登录;行为有两种:评论和收藏。下面使用状态模式来实现这个逻辑,代码如下。
首先创建抽象状态角色UserState类。


public abstract class UserState {
    protected AppContext context;

    public void setContext(AppContext context) {
        this.context = context;
    }

    public abstract void favorite();

    public abstract void comment(String comment);
}

然后创建登录状态LogInState类。


public class LoginInState extends UserState {
    @Override
    public void favorite() {
        System.out.println("收藏成功!");
    }

    @Override
    public void comment(String comment) {
        System.out.println(comment);
    }
}

创建未登录状态UnloginState类。


public class UnLoginState extends UserState {
    @Override
    public void favorite() {
        this.switch2Login();
        this.context.getState().favorite();
    }

    @Override
    public void comment(String comment) {
        this.switch2Login();
        this.context.getState().comment(comment);
    }

    private void switch2Login() {
        System.out.println("跳转到登录页面!");
        this.context.setState(this.context.STATE_LOGIN);
    }
}

创建上下文角色AppContext类。


public class AppContext {
    public static final UserState STATE_LOGIN = new LoginInState();
    public static final UserState STATE_UNLOGIN = new UnLoginState();
    private UserState currentState = STATE_UNLOGIN;
    {
        STATE_LOGIN.setContext(this);
        STATE_UNLOGIN.setContext(this);
    }

    public void setState(UserState state) {
        this.currentState = state;
        this.currentState.setContext(this);
    }

    public UserState getState() {
        return this.currentState;
    }

    public void favorite() {
        this.currentState.favorite();
    }

    public void comment(String comment) {
        this.currentState.comment(comment);
    }
}

最后编写客户端测试代码。


public static void main(String[] args) {
        AppContext context = new AppContext();
        context.favorite();
        context.comment("评论: 好文章,360个赞!");
}

运行结果如下图所示。

file

3 使用状态机实现订单状态流转控制#

状态机是状态模式的一种应用,相当于上下文角色的一个升级版。在工作流或游戏等各种系统中有大量使用,如各种工作流引擎,它几乎是状态机的子集和实现,封装状态的变化规则。Spring也提供了一个很好的解决方案。Spring中的组件名称就叫作状态机(StateMachine)。状态机帮助开发者简化状态控制的开发过程,让状态机结构更加层次化。下面用Spring状态机模拟一个订单状态流转的过程。

3.1 添加依赖。#


<dependency>
    <groupId>org.springframework.statemachine</groupId>
    <artifactId>spring-statemachine-core</artifactId>
    <version>2.0.1.RELEASE</version>
</dependency>

3.2 创建订单实体Order类。#


public class Order {
    private int id;
    private OrderStatus status;
    public void setStatus(OrderStatus status) {
        this.status = status;
    }

    public OrderStatus getStatus() {
        return status;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }

    @Override
    public String toString() {
        return "订单号:" + id + ", 订单状态:" + status;
    }
}

3.3 创建订单状态枚举类和状态转换枚举类。#


/**
 * 订单状态
 */
public enum OrderStatus {
    //待支付,待发货,待收货,订单结束
    WAIT_PAYMENT, WAIT_DELIVER, WAIT_RECEIVE, FINISH;
}

/**
 * 订单状态改变事件
 */
public enum OrderStatusChangeEvent {
    //支付,发货,确认收货
    PAYED, DELIVERY, RECEIVED;
}

3.4 添加状态流转配置。#


/**
 * 订单状态机配置
 */
@Configuration
@EnableStateMachine(name = "orderStateMachine")
public class OrderStateMachineConfig extends StateMachineConfigurerAdapter<OrderStatus, OrderStatusChangeEvent> {
 
    /**
     * 配置状态
     * @param states
     * @throws Exception
     */
    public void configure(StateMachineStateConfigurer<OrderStatus, OrderStatusChangeEvent> 	states) throws Exception {
        states
                .withStates()
                .initial(OrderStatus.WAIT_PAYMENT)
                .states(EnumSet.allOf(OrderStatus.class));
    }
 
    /**
     * 配置状态转换事件关系
     * @param transitions
     * @throws Exception
     */
    public void configure(StateMachineTransitionConfigurer<OrderStatus, OrderStatusChangeEvent> 	transitions) throws Exception {
        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
     */
    @Bean
    public DefaultStateMachinePersister persister(){
        return new DefaultStateMachinePersister<>(new StateMachinePersist<Object, Object, 			Order>() {
            @Override
            public void write(StateMachineContext<Object, Object> context, Order order) throws 			Exception {
                //此处并没有进行持久化操作
            }
 
            @Override
            public StateMachineContext<Object, Object> read(Order order) throws Exception {
                //此处直接获取Order中的状态,其实并没有进行持久化读取操作
                return new DefaultStateMachineContext(order.getStatus(), null, null, null);
            }
        });
    }
}

3.5 添加订单状态监听器。#


@Component("orderStateListener")
@WithStateMachine(name = "orderStateMachine")
public class OrderStateListenerImpl{
 
    @OnTransition(source = "WAIT_PAYMENT", target = "WAIT_DELIVER")
    public boolean payTransition(Message<OrderStatusChangeEvent> message) {
        Order order = (Order) message.getHeaders().get("order");
        order.setStatus(OrderStatus.WAIT_DELIVER);
        System.out.println("支付,状态机反馈信息:" + message.getHeaders().toString());
        return true;
    }
 
    @OnTransition(source = "WAIT_DELIVER", target = "WAIT_RECEIVE")
    public boolean deliverTransition(Message<OrderStatusChangeEvent> message) {
        Order order = (Order) message.getHeaders().get("order");
        order.setStatus(OrderStatus.WAIT_RECEIVE);
        System.out.println("发货,状态机反馈信息:" + message.getHeaders().toString());
        return true;
    }
 
    @OnTransition(source = "WAIT_RECEIVE", target = "FINISH")
    public boolean receiveTransition(Message<OrderStatusChangeEvent> message){
        Order order = (Order) message.getHeaders().get("order");
        order.setStatus(OrderStatus.FINISH);
        System.out.println("收货,状态机反馈信息:" + message.getHeaders().toString());
        return true;
    }
}

3.6 创建IOrderService接口。#


public interface IOrderService {
    //创建新订单
    Order create();
    //发起支付
    Order pay(int id);
    //订单发货
    Order deliver(int id);
    //订单收货
    Order receive(int id);
    //获取所有订单信息
    Map<Integer, Order> getOrders();
}

3.7 在Service业务逻辑中应用。#


@Service("orderService")
public class OrderServiceImpl implements IOrderService {

    @Autowired
    private StateMachine<OrderStatus, OrderStatusChangeEvent> orderStateMachine;
 
    @Autowired
    private StateMachinePersister<OrderStatus, OrderStatusChangeEvent, Order> persister;
 
    private int id = 1;
    private Map<Integer, Order> orders = new HashMap<>();

    public Order create() {
        Order order = new Order();
        order.setStatus(OrderStatus.WAIT_PAYMENT);
        order.setId(id++);
        orders.put(order.getId(), order);
        return order;
    }

    public Order pay(int id) {
        Order order = orders.get(id);
        System.out.println("线程名称:" + Thread.currentThread().getName() + " 尝试支付,订单号:" + id);
        Message message = MessageBuilder.withPayload(OrderStatusChangeEvent.PAYED).
setHeader("order", order).build();
        if (!sendEvent(message, order)) {
            System.out.println("线程名称:" + Thread.currentThread().getName() + " 支付失败, 状态异常,订单号:" + id);
        }
        return orders.get(id);
    }

    public Order deliver(int id) {
        Order order = orders.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);
        }
        return orders.get(id);
    }

    public Order receive(int id) {
        Order order = orders.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);
        }
        return orders.get(id);
    }
 

    public Map<Integer, Order> getOrders() {
        return orders;
    }
 
 
    /**
     * 发送订单状态转换事件
     *
     * @param message
     * @param order
     * @return
     */
    private synchronized boolean sendEvent(Message<OrderStatusChangeEvent> message, Order order) {
        boolean result = false;
        try {
            orderStateMachine.start();
            //尝试恢复状态机状态
            persister.restore(orderStateMachine, order);
            //添加延迟用于线程安全测试
            Thread.sleep(1000);
            result = orderStateMachine.sendEvent(message);
            //持久化状态机状态
            persister.persist(orderStateMachine, order);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            orderStateMachine.stop();
        }
        return result;
    }
}

3.8 编写客户端测试代码。#



@SpringBootApplication
public class Test {
    public static void main(String[] args) {

        Thread.currentThread().setName("主线程");

        ConfigurableApplicationContext context = SpringApplication.run(Test.class,args);

        IOrderService orderService = (IOrderService)context.getBean("orderService");

        orderService.create();
        orderService.create();

        orderService.pay(1);

        new Thread("客户线程"){
            @Override
            public void run() {
                orderService.deliver(1);
                orderService.receive(1);
            }
        }.start();

        orderService.pay(2);
        orderService.deliver(2);
        orderService.receive(2);

        System.out.println("全部订单状态:" + orderService.getOrders());

    }
}

通过这个真实的业务案例,相信小伙伴们已经对状态模式有了一个非常深刻的理解。

状态机方案解决订单状态扭转 在拦截器的 preStateChange 方法中,我们可以根据状态转移情况执行自定义的业务逻辑,比如更新订单状态、记录状态转移日志等。好的,我们可以在拦截器的 preStateChange 方法中,根据状态转移目标状态更新订单状态,并记录状态转移日志。在实际应用中,我们应该根据具体的业务场景选择合适的状态机库,并结合拦截器、事件等机制,以实现状态机的灵活性和可扩展性。我们在拦截器的 preStateChange 方法中,根据状态转移目标状态更新订单状态,并记录状态转移日志。箭头上标注的是触发转移的事件。 阅读详情

相关推荐

Spring-statemachine实现订单状态机

StateMachine:状态机模型state:S-状态,一般定义为一个枚举类,如创建、待风控审核、待支付等状态event:E-事件,同样定义成一个枚举类,如订单创建、订单审核、支付等,代表一个动作。一个状态机的定义就由这两个主要的元素组成,状态及对对应的事件(动作)。状态机的相关概念Transition: 节点,是组成状态机引擎的核心source:节点的当前状态target:节点的目标状态event:触发节点从当前状态到目标状态的动作。

子悠のziyou 2127

spring-statemachine状态机梳理

spring-statemachine状态机梳理

我的E家 9710

Spring StateMachine在电商订单状态管理中的实践

状态机是管理复杂业务状态流转的数学模型,通过定义状态、事件和转移规则实现有序控制。在电商系统中,订单状态管理涉及多节点转换,传统硬编码方式难以维护。Spring StateMachine框架与Spring生态无缝集成,支持状态持久化和分布式事务,通过解耦状态流转与业务逻辑提升系统可维护性。典型应用场景包括订单生命周期管理、物流状态协同等,结合Saga模式可处理分布式事务。本文以电商订单为例,详解状态机设计模式、异常处理及性能优化方案,为复杂业务系统提供状态管理最佳实践。

weixin_33462167的博客 311

Spring之状态机讲解

先来解释什么是状态( State )。现实事物是有不同状态的,例如一个自动门,就有open和closed两种状态。我们通常所说的状态机是有限状态机,也就是被描述的事物的状态的数量是有限个,例如自动门的状态就是两个open和closed。状态机,也就是,不是指一台实际机器,而是指一个数学模型。说白了,一般就是指一张状态转换图。状态机的全称是有限状态自动机,自动两个字也是包含重要含义的。给定一个状态机,同时给定它的当前状态以及输入,那么输出状态时可以明确的运算出来的。

上善若泪 4447

计算机图形学入门30:动画与模拟介绍

计算机图形学入门,动画与模拟,物理模拟,质点弹簧系统,运动学,粒子系统,动作捕捉。

YanisWu的博客 1484

SQL Server2008详细安装步骤(超详细步骤)

https://pan.baidu.com/s/1yvolqfU-AbiuGrIzblA7pA 提取码:lzw81.下载好安装包后解压到除C盘以外的磁盘,避免系统卡顿2.使用管理员身份打开安装程序注意:使用管理员身份打开时,此时如果显示以下画面属于正常现象,下载安装即可。因为.net现在有很多软件都需要,包括SQLSERVER2008,它里面有visual studio都要有net,电脑系统没有的话安装一下好了。3.选择全新安装或向现有安装添加新功能4.点击确定5.出现以下画面之后,输入产品密钥即可(密钥在

m0_67402774的博客 4万+

《设计模式就该这样学》之彻底搞懂Spring状态机原理,实现订单与物流解耦

本文节选自《设计模式就该这样学》1 状态模式的UML类图状态模式的UML类图如下图所示。2 使用状态模式实现登录状态自由切换否则,跳转到登录界面,登录后再继续执行先前的动作。这里涉及的状态有两种:登录与未登录;下面使用状态模式来实现这个逻辑,代码如下。首先创建抽象状态角色UserState类。然后创建登录状态LogInState类。@Override创建未登录状态UnloginState类。

2401_85763569的博客 943

如何选择StandardScaler、MinMaxScaler和RobustScaler做标准化的对比解释和示例代码

选择合适的标准化方法取决于数据的特点和需求。如果数据分布接近正态分布且无异常值,可以使用。如果需要将数据映射到特定范围内并保持相对关系,可以使用。如果数据中存在异常值或离群点,可以使用进行鲁棒标准化。在分类、聚类算法中,需要使用距离来度量相似性的时候、或者使用PCA技术进行降维的时候,StandardScaler表现更好(避免不同量纲对方差、协方差计算的影响);在不涉及距离度量、协方差、数据不符合正态分布、异常值较少的时候,可使用MinMaxScaler。

qq_36001281的博客 4939

彻底搞懂Spring状态机原理,实现订单与物流解耦,2024年最新java面试算法题

本文节选自《设计模式就该这样学》1 状态模式的UML类图状态模式的UML类图如下图所示。2 使用状态模式实现登录状态自由切换否则,跳转到登录界面,登录后再继续执行先前的动作。这里涉及的状态有两种:登录与未登录;下面使用状态模式来实现这个逻辑,代码如下。首先创建抽象状态角色UserState类。然后创建登录状态LogInState类。@Override创建未登录状态UnloginState类。

m0_61499908的博客 1026

眼动数据分析基础_AOI分析动态刺激物

如果您正在研究人们对如视频、动画等动态刺激物的视觉行为,动态AOI工具可为您提供类似静态AOI工具对于图片及其他静止刺激物分析的同样功能。创建和编辑动态AOI的过程和静态AOI相似。 关于动态AOI工具的使用,有几点非常重要: 多个看到相同线性动态刺激物的用户的眼动数据叠加是有价值的; 动态AOI不能用于非线性动态记录的分析; 动态AOI不能用于静态刺激物,如图片的分析; 动态AOI可以被开启、关闭、移动、调整大小及跟踪相关分析对象 首先,我们来定义两种不同的动态刺激物:线性动态刺激物(LDS),是指固

Kobe123brant的博客 3501

未连接互联网的银河麒麟v10安装g++,各种报错,如何处理?

🏆本文收录于《全栈Bug调优(实战版)》专栏,主要记录项目实战过程中所遇到的Bug或因后果及提供真实有效的解决方案,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!! 备注:部分问题/疑难杂症搜集于互联网。

**My Coding Family** 1803

Java实战篇02-电商订单系统(Spring Cloud+RabbitMQ)

本文介绍了如何使用Spring Cloud Alibaba和RabbitMQ构建一个电商订单系统。文章从技术选型入手,采用Nacos作为服务注册中心,OpenFeign实现服务调用,RabbitMQ进行异步解耦,MySQL存储核心数据,Redis缓存热点数据。系统架构采用微服务+消息队列模式,包含订单服务、库存服务、用户服务等模块,通过API网关统一入口。重点讲解了用户下单的核心业务流程,采用"同步校验+异步执行"的设计思想,通过流程图展示了从库存校验到订单创建的完整链路。文章还提供了详

千淘万漉虽辛苦,吹尽狂沙始到金 2万+

3、基于视觉的智能分拣机:原理、算法与应用

本文介绍了一种基于视觉的智能分拣系统,利用树莓派、相机模块和多种检测算法实现低成本、高精度的物体分拣。系统支持颜色、形状和尺寸三种检测方式,准确率达99%,广泛适用于制造业、农业和物流等领域。文章详细阐述了各检测算法原理、测试结果及系统优化方向,并探讨了其实际应用案例与未来发展前景。

mqtt6iot的博客 236

订单状态机设计:硬编码与程序化实现的深度对比与实践

在软件工程中,状态机是管理复杂业务实体(如订单、工单)生命周期的核心设计模式。其原理在于将状态、事件、动作和转移规则抽象为模型,实现业务逻辑与流程控制的解耦。这种设计模式的技术价值在于显著提升了系统的可维护性、可扩展性和可测试性,尤其适用于规则频繁变更或流程复杂的应用场景。通过将硬编码的if-else逻辑转化为声明式的配置,状态机使得业务规则可视化、动态化成为可能。本文以电商订单状态流转为例,深入探讨了硬编码与基于Spring Statemachine的程序化实现两种方案,并分析了其各自的适用场景与工程实践

weixin_33806300的博客 380

LeRobot — 基于 PyTorch 的真实世界机器人学习框架(一)- 核心框架模块解析

LeRobot 是一个开源的机器人学习框架,由 Hugging Face 开发,旨在降低机器人学的准入门槛。该框架提供完整的机器学习流程支持,包括数据采集、训练、评估和部署。核心模块涵盖数据集标准化、策略算法、硬件抽象和强化学习组件,采用模块化设计实现灵活扩展。通过集成 Hugging Face Hub,用户可以轻松共享数据集和预训练模型。LeRobot 特别注重真实世界的机器人应用,支持多种前沿算法如模仿学习、视觉语言动作模型等,并提供统一的硬件接口和仿真环境适配。

熊猫小妖的AI世界 170

RK3588 平台imx334 驱动调试记录

rk3588调试imx334

xiaoning132的专栏 1789

Spring StateMachine在电商订单管理中的实践与优化

状态机是管理复杂业务状态流转的核心设计模式,通过明确定义状态集合和转换规则,实现业务逻辑与状态变更的解耦。在电商系统中,订单状态管理涉及支付、发货、退款等多个环节,传统if-else硬编码方式难以应对业务扩展。Spring StateMachine框架提供了优雅的解决方案,支持状态持久化、分布式锁和可视化监控。结合JPA持久化和Redis分布式锁,可以构建高可用的订单状态管理系统,显著提升代码维护性和系统稳定性。本文通过电商订单状态机实战,详解状态定义、转换规则和性能优化方案。

weixin_33966095的博客 296

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

电商系统实战:Spring AI在订单模块的应用

我们采用了预扣减库存的方式,在创建订单时先锁定库存,避免超卖。特别要注意的是支付超时处理,我们设置了30分钟的支付有效期,超时未支付的订单会自动取消并释放库存。用户取消订单和申请退款是高频操作。取消订单要处理几种情况:未支付的直接取消,已支付但未发货的要退款,已发货的要走退货流程。最方便的是部署功能,完成开发后一键就能把项目发布到线上环境,自动配置好数据库和消息队列的连接,这对快速验证业务逻辑特别有帮助。这个模块虽然看起来简单,但实际开发中要考虑的细节特别多,特别是涉及到库存、支付和状态流转这些关键环节。

GoldenleafRaven13的博客 793

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

上一篇: 如何正确的找BUG
下一篇: 大数据——配置并启动集群/开启历史服务器和日志聚集
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值