业务流程场景的处理利器—Spring状态机

Spring状态机的核心在于状态变迁和事件驱动,它强调的是系统当前所处的状态,并且关注于系统如何根据接收到的外部事件或内部条件进行状态转变。

在日常开发系统中经常会涉及到一些状态管理的场景,典型的如订单场景,订单从创建到最终完成或取消,通常会经历多个状态的转换,那么如何高效地管理这些状态流转,并在系统中灵活地扩展状态和行为呢?Spring状态机可以很好的帮助解决我们的实际问题。

1、认识Spring状态机

    Spring状态机(称为Spring State Machine)是一种可以管理状态、事件之间的关系,以及他们之间的转换。这是一个专门为应用程序中的状态管理和状态转换提供支持的框架。它简化了事物对象在不同状态下,不同事件转化的代码管理,让其代码变得更加清晰明了。

    在我们的日常生活中电梯是我们非常熟悉的老伙计,电梯有停止、运行、开门、关门等状态。如果我们按了某一层按了电梯按钮(事件),电梯会进入运行状态,并且运行到目标楼层,然后停止并开门等一系列的动作。类比电梯的运行,Spring的状态机也是在有限个状态以及这些状态之间的转移和动作等行为,Spring状态机的核心概念:

(1)状态 (State):定义系统可以存在的各个状态,如电梯的不同阶段(停止、运行、开门、关门)。

(2)事件(Event):触发状态变迁的动作或条件,如用户点击“5f”按钮触发电梯的运行事件。

(3)转换(Transition):定义了在特定状态下接收到特定事件时,如何从一个状态迁移到另一个状态。

(4)动作(Action):可以在状态变迁前后执行的操作,如状态切换时更新数据库记录、发送通知邮件等。

    Spring状态机的核心在于状态变迁和事件驱动,它强调的是系统当前所处的状态,并且关注于系统如何根据接收到的外部事件或内部条件进行状态转变。

2、实战Spring状态机

    我们以典型的电商场景下的订单状态为例,介绍Spring状态机如何实现订单状态的高效管理,订单的状态流转图如下所示:

图片

项目的基础结构图如下所示:

图片

(1)添加的spring状态机依赖
<dependency>
    <groupId>org.springframework.statemachine</groupId>
    <artifactId>spring-statemachine-starter</artifactId>
    <version>2.5.1</version>
</dependenc
    (2)定义订单的状态
    @Getter
    @AllArgsConstructor
    public enum OrderStatusEnum {
        WAIT_PAY(0, "待支付"),
        WAIT_DELIVER(1, "待发货"),
        WAIT_RECEIVE(2, "待收货"),
        RECEIVED(3, "已收货");
    
    
        private Integer code;
        private String msg;
    }
    (3)定义订单的事件
    @Getter
    @AllArgsConstructor
    public enum OrderStatusEventEnum {
        ORDER(1, "用户下单"),
        PAY(2, "用户支付"),
        DELIVER(3, "仓库发货"),
        RECEIVE(4, "用户收货")
        ;
        private final Integer code;
        private final String msg;
    }
    (4)定义订单状态的流转和初始化

    @Configurable
    @EnableStateMachine(name ="orderStateMachine")    //name的作用是给状态机制命名,用于区分不同的状态机
    public class OrderStateMachineConfig extends StateMachineConfigurerAdapter<OrderStatusEnum, OrderStatusEventEnum> {
    
    
        /**
         * 初始化
         *
         */
        @Override
        public void configure(StateMachineStateConfigurer<OrderStatusEnum, OrderStatusEventEnum> states) throws Exception {
           states.withStates()
                    .initial(OrderStatusEnum.WAIT_PAY)   //初始化订单的状态
                    .states(EnumSet.allOf(OrderStatusEnum.class));    //列举订单所有的预设状态
        }
    
    
        /**
         * 配置订单状态的流转和触发事件
         *
         */
        @Override
        public void configure(StateMachineTransitionConfigurer<OrderStatusEnum, OrderStatusEventEnum> transitions) throws Exception {
            transitions
                    // 通过支付事件  将订单从待支付 -> 待发货状态(支付成功)
                    .withExternal().source(OrderStatusEnum.WAIT_PAY).target(OrderStatusEnum.WAIT_DELIVER).event(OrderStatusEventEnum.PAY)
                    .and()
                    // 通过发货事件  已支将订单从待发货 -> 待收货状态
                    .withExternal().source(OrderStatusEnum.WAIT_DELIVER).target(OrderStatusEnum.WAIT_RECEIVE).event(OrderStatusEventEnum.DELIVER)
                    .and()
                    // 通过用户接收事件 将订单从待收货 -> 已收货状态
                    .withExternal().source(OrderStatusEnum.WAIT_RECEIVE).target(OrderStatusEnum.RECEIVED).event(OrderStatusEventEnum.RECEIVE);
        }
    }

        通过上面的配置就可以实现订单状态直接的转换,主要用于说明订单的状态和事件的绑定关系,并且指明了订单状态如何流转的(原始状态是什么,目标的状态是什么 ,通过什么事件触发)。

    (5)定义监听

        订单的节点已经定义,那么每个节点是如何工作的呢?此时需要配置监听,用于实现当状态发生变化的时候需要完成什么动作。

    @Slf4j
    @Component
    @WithStateMachine(name = "orderStateMachine")
    public class OrderMachineListener {
    
    
        @OnTransition(source = "WAIT_PAY", target = "WAIT_DELIVER")
        public void pay(Message<OrderStatusEventEnum> message) {
            // 获取消息头中的order对象
            OrderEntity entity = message.getHeaders().get("order", OrderEntity.class);
            entity.setStatus(OrderStatusEnum.WAIT_DELIVER.getCode());
            System.out.println("orderMachine   WAIT_PAY----> WAIT_DELIVER");
        }
    
    
        @OnTransition(source = "WAIT_DELIVER", target = "WAIT_RECEIVE")
        public void delivery(Message<OrderStatusEventEnum> message) {
            // 获取消息头中的order对象
            OrderEntity entity = message.getHeaders().get("order", OrderEntity.class);
            entity.setStatus(OrderStatusEnum.WAIT_DELIVER.getCode());
            System.out.println("orderMachine   WAIT_DELIVER----> WAIT_RECEIVE");
        }
    
    
        @OnTransition(source = "WAIT_RECEIVE", target = "RECEIVED")
        public void received(Message<OrderStatusEventEnum> message) {
            // 获取消息头中的order对象
            OrderEntity entity = message.getHeaders().get("order", OrderEntity.class);
            entity.setStatus(OrderStatusEnum.WAIT_DELIVER.getCode());
            System.out.println("orderMachine   WAIT_RECEIVE----> RECEIVED");
        }
    }
    (6)触发状态机的工作

        订单状态机整个生命周期的状态定义完成、订单的状态发生了转换后需要执行的行为也定义完成,那么如何触发整个链路工作呢?此时就需要加入触发的入口:

    @Component
    @Slf4j
    public class OrderProcessorService {
    
    
        @Resource
        private StateMachine<OrderStatusEnum, OrderStatusEventEnum> orderStatusMachine;
    
    
        public Boolean process(OrderEntity order, OrderStatusEventEnum event) {
            orderStatusMachine.start();
            //订单的对象封装到message中
            Message<OrderStatusEventEnum> message = MessageBuilder.withPayload(event).
                    setHeader("order",order).
                    build();
            //使用状态机发送这个消息
            return this.touchEvent(message);
        }
    
    
    
    
        private boolean touchEvent(Message<OrderStatusEventEnum> message) {
            OrderEntity order = (OrderEntity)message.getHeaders().get("order");
            System.out.println("订单的信息 orderId=" + (Objects.nonNull(order) ? order.getId() : "-"));
            return orderStatusMachine.sendEvent(message);
        }
    }

        sendEvent(message)是十分关键的,因为在这里只要输入什么样的事件,那么它就按照预先设定的规则触发状态的流转,并执行监听中方法。

    (7)测试Conrtoller
    @RestController
    @RequestMapping("/order")
    public class OrderController {
        @Resource
        private OrderProcessorService orderProcessorService;
    
    
        @GetMapping("/pay")
        public String pay(Integer orderId) {
    
    
            OrderEntity order = new OrderEntity();
            order.setId(orderId);
            order.setStatus(OrderStatusEnum.WAIT_PAY.getCode());
            orderProcessorService.process(order, OrderStatusEventEnum.PAY);
            return "pay success";
        }
    
    
        @GetMapping("/deliver")
        public String deliver(Integer orderId) {
    
    
            OrderEntity order = new OrderEntity();
            order.setId(orderId);
            order.setStatus(OrderStatusEnum.WAIT_DELIVER.getCode());
            orderProcessorService.process(order, OrderStatusEventEnum.DELIVER);
            return "deliver success";
        }
    
    
        @GetMapping("/receive")
        public String receive(Integer orderId) {
    
    
            OrderEntity order = new OrderEntity();
            order.setId(orderId);
            order.setStatus(OrderStatusEnum.WAIT_RECEIVE.getCode());
            orderProcessorService.process(order, OrderStatusEventEnum.RECEIVE);
            return "receive success";
        }
    }
    (8)执行的结果
    (a)支付的状态流转

    图片

    控制台的输出结果:

    图片

    (b)发货的状态流转

    图片

    控制台的输出结果:

    图片

    (c)收货状态的流转

    图片

    控制台的输出结果:

    图片

        至此,我们实现使用Spring状态机实现了订单的状态流转和触发对应的事件。

    总结:

    (1)如果业务流程复杂,状态多,状态切换频繁,Spring状态机就非常的适用,它可以提升代码的维护性和扩展性。

    (2)Spring状态机可以直接集成到现有的服务中,适合高并发低延迟的场景,涉及到单个流程的流转业务,可以考虑使用状态机。

    AI大模型学习福利

    作为一名热心肠的互联网老兵,我决定把宝贵的AI知识分享给大家。 至于能学习到多少就看你的学习毅力和能力了 。我已将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。

    一、全套AGI大模型学习路线

    AI大模型时代的学习之旅:从基础到前沿,掌握人工智能的核心技能!

    因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获取

    二、640套AI大模型报告合集

    这套包含640份报告的合集,涵盖了AI大模型的理论研究、技术实现、行业应用等多个方面。无论您是科研人员、工程师,还是对AI大模型感兴趣的爱好者,这套报告合集都将为您提供宝贵的信息和启示。

    因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获

    三、AI大模型经典PDF籍

    随着人工智能技术的飞速发展,AI大模型已经成为了当今科技领域的一大热点。这些大型预训练模型,如GPT-3、BERT、XLNet等,以其强大的语言理解和生成能力,正在改变我们对人工智能的认识。 那以下这些PDF籍就是非常不错的学习资源。


    因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获

    四、AI大模型商业化落地方案

    因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获

    作为普通人,入局大模型时代需要持续学习和实践,不断提高自己的技能和认知水平,同时也需要有责任感和伦理意识,为人工智能的健康发展贡献力量

    评论
    成就一亿技术人!
    拼手气红包6.0元
    还能输入1000个字符
     
    红包 添加红包
    表情包 插入表情
     条评论被折叠 查看
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

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

    抵扣说明:

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

    余额充值