当前位置: 首页 > article >正文

SpringBoot状态机

Spring Boot 状态机(State Machine)是 Spring Framework 提供的一种用于实现复杂业务逻辑的状态管理工具。它基于有限状态机(Finite State Machine, FSM)的概念,允许开发者定义一组状态、事件以及它们之间的转换规则。这在处理具有多个步骤或条件的工作流时特别有用。

Spring StateMachine 组件

Spring StateMachine 是一个专门设计来帮助构建和管理状态机的库。它提供了丰富的功能来简化状态机的配置和使用。以下是几个关键概念:

  • 状态(States):表示系统可以处于的不同状况。
  • 事件(Events):触发从一个状态到另一个状态的转换。
  • 转换(Transitions):定义了在特定事件发生时如何从一个状态转移到另一个状态。
  • 动作(Actions):当进入某个状态或执行某些转换时可以执行的操作。
  • 守卫(Guards):条件检查器,用于确定是否允许特定转换发生。

如下图示例:有限的状态集是“opend”以及“closed”。如果“现态”是“opend”,当“条件”为“Close”时,执行的“动作”是“close door”,次态则为“closed”。状态机逻辑执行完毕后“closed”则变成了“现态”。

应用场景

Spring 状态机(Spring State Machine)作为一种强大的状态管理和转换工具,在多个领域有着广泛的应用场景。以下是几个典型示例的详细解释:

1. 订单生命周期管理

在电商应用中,订单状态可能经历创建、支付、发货、确认收货直至完成或取消等多个状态变化。通过 Spring State Machine,你可以清晰地定义并控制这些状态之间的合法转换过程。例如:

  • 创建订单:当用户提交订单时,状态机从初始状态 CREATED 开始。
  • 支付订单:一旦支付成功,状态机接收事件 PAYMENT_RECEIVED,从而将订单状态转移到 PAID
  • 发货:仓库系统接收到发货指令后,状态机会根据事件 SHIP_ORDER 将订单状态转移到 SHIPPED
  • 确认收货:买家确认收货后,状态机接收 DELIVERED 事件,订单进入 COMPLETED 状态。
  • 取消订单:如果在任意步骤出现问题,可以通过 CANCEL_ORDER 事件将订单状态转移到 CANCELED

同时,在每个状态变迁时,可以触发相应的操作,如发送邮件通知、更新库存等。

2. 工作流引擎

在企业级应用中,诸如请假审批流程、报销流程等工作流通常具有多个步骤和决策点。状态机可以用来描述每个步骤之间的关系及转换条件,确保流程按照预设规则进行。例如:

  • 发起申请:员工提交请假申请,状态机从 DRAFT 转移到 SUBMITTED
  • 主管审批:主管批准或拒绝申请,状态机根据结果转移到 APPROVED 或 REJECTED
  • HR 复核:对于特定类型的请假,可能需要 HR 进行复核,状态机进一步转移到 HR_REVIEW
  • 最终确定:所有审批完成后,状态机最终转移到 CONFIRMED 或直接结束流程。

3. 游戏逻辑

在游戏开发中,游戏角色、游戏关卡、战斗场景等都有各自的状态。状态机可用于实现角色的不同行动模式切换、关卡过关条件判断、战斗状态循环等复杂逻辑。例如:

  • 角色状态:玩家角色可以在 IDLEMOVINGATTACKINGDEFENDING 等状态之间切换,每种状态对应不同的行为模式。
  • 关卡状态:关卡可以从 START 到 PLAYING 再到 COMPLETED 或 FAILED,并且可以根据玩家的表现动态调整难度。
  • 战斗状态:战斗场景中的状态机可以管理回合制战斗中的 PLAYER_TURN 和 ENEMY_TURN,以及处理 VICTORY 或 DEFEAT 结果。

4. 设备状态监控

在物联网(IoT)应用中,对设备运行状态进行实时跟踪和管理时,可以根据设备接收到的各种信号或指令触发状态转变。例如:

  • 开机/待机/运行:设备根据电源开关、用户交互或其他传感器数据在 POWER_ONSTANDBY 和 RUNNING 状态间切换。
  • 故障检测:设备检测到异常情况时,状态机会转移到 ERROR 状态,并触发报警机制。
  • 维修状态:设备进入维修模式后,状态机保持在 MAINTENANCE 状态直到修复完成。

订单流程扭转简单示例

pom.xml 添加依赖

        <dependency>
            <groupId>org.springframework.statemachine</groupId>
            <artifactId>spring-statemachine-starter</artifactId>
            <version>4.0.0</version>
        </dependency>

订单模型

这里方便演示,其他的字段根据需要自行添加

package com.coderlk.state.model;

import com.coderlk.state.config.OrderStatus;
import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
public class Order {
    private OrderStatus status;

}

订单状态

package com.coderlk.state.config;

/**
 * 订单状态
 */
public enum OrderStatus {
    //待支付,待发货,待收货,订单结束
    INIT , PAYED, WAIT_DELIVERY, RECEIVED;
}

订单状态改变事件

package com.coderlk.state.config;

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

 配置状态机

package com.coderlk.state.config;

import com.coderlk.state.model.Order;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.statemachine.StateMachineContext;
import org.springframework.statemachine.StateMachinePersist;
import org.springframework.statemachine.config.EnableStateMachine;
import org.springframework.statemachine.config.StateMachineConfigurerAdapter;
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
import org.springframework.statemachine.persist.DefaultStateMachinePersister;
import org.springframework.statemachine.support.DefaultStateMachineContext;

import java.util.EnumSet;

/**
 * 订单状态机配置
 */
@Configuration
@EnableStateMachine(name = "orderStateMachine")
public class OrderStateMachineConfig extends StateMachineConfigurerAdapter<OrderStatus, OrderEvents> {

    /**
     * 配置状态
     *
     * @param states
     * @throws Exception
     */
    public void configure(StateMachineStateConfigurer<OrderStatus, OrderEvents> states) throws Exception {
        states.withStates()
                .initial(OrderStatus.INIT)
                .states(EnumSet.allOf(OrderStatus.class));
    }

    /**
     * 配置状态转换事件关系
     *
     * @param transitions
     * @throws Exception
     */
    public void configure(StateMachineTransitionConfigurer<OrderStatus, OrderEvents> transitions) throws Exception {
        transitions.withExternal().source(OrderStatus.INIT).target(OrderStatus.PAYED)
                .event(OrderEvents.PAY)
                .and()
                .withExternal().source(OrderStatus.PAYED).target(OrderStatus.WAIT_DELIVERY)
                .event(OrderEvents.DELIVERY)
                .and()
                .withExternal().source(OrderStatus.WAIT_DELIVERY).target(OrderStatus.RECEIVED)
                .event(OrderEvents.RECEIVE);
    }

    /**
     * 持久化配置
     * 在实际使用中,可以配合Redis等进行持久化操作
     *
     * @return
     */
    @Bean
    public DefaultStateMachinePersister<Object,Object,Order> persister() {
        return new DefaultStateMachinePersister<>(new StateMachinePersist<>() {
            @Override
            public void write(StateMachineContext<Object, Object> context, Order order) throws Exception {
                //todo 持久化处理
            }

            @Override
            public StateMachineContext<Object, Object> read(Order order) throws Exception {
                //此处直接获取Order中的状态,其实并没有进行持久化读取操作
                return new DefaultStateMachineContext<>(order.getStatus(), null, null, null);
            }
        });
    }
}

处理事件

package com.coderlk.state.service;

import com.coderlk.state.config.OrderStatus;
import com.coderlk.state.config.OrderEvents;
import com.coderlk.state.model.Order;
import lombok.extern.slf4j.Slf4j;
import org.springframework.messaging.Message;
import org.springframework.statemachine.annotation.OnTransition;
import org.springframework.statemachine.annotation.WithStateMachine;
import org.springframework.stereotype.Component;

@Component("orderStateListener")
@Slf4j
@WithStateMachine(name = "orderStateMachine")
public class OrderStateListener {
    @OnTransition( source = "INIT" ,target = "PAYED")
    public boolean pay(Message<OrderEvents> message) {
        Order order = (Order) message.getHeaders().get("order");
        order.setStatus(OrderStatus.PAYED);
        log.info("订单表保存数据");
        log.info("发送站内消息");
        log.info("回调支付接口");
        log.info("同步至数据仓库");
        return true;
    }

    @OnTransition( source = "PAYED" , target = "WAIT_DELIVERY")
    public boolean delivery(Message<OrderEvents> message) {
        Order order = (Order) message.getHeaders().get("order");
        order.setStatus(OrderStatus.WAIT_DELIVERY);
        log.info("创建物流订单");
        log.info("更新物流订单状态");
        log.info("同步至数据仓库");
        return true;
    }

    @OnTransition(source = "SHIPPED" , target = "RECEIVED")
    public boolean receive(Message<OrderEvents> message){
        Order order = (Order) message.getHeaders().get("order");
        order.setStatus(OrderStatus.PAYED);

        log.info("更新订单数据");
        log.info("同步至数据仓库");
        return true;
    }
}
package com.coderlk.state.service;

import com.coderlk.state.config.OrderStatus;
import com.coderlk.state.config.OrderEvents;
import com.coderlk.state.model.Order;
import jakarta.annotation.Resource;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.persist.StateMachinePersister;
import org.springframework.stereotype.Component;

@Component("orderProcessor")
@Slf4j
public class OrderProcessor {

    @Resource
    private StateMachine<OrderStatus, OrderEvents> orderStateMachine;

    @Resource
    private StateMachinePersister<OrderStatus, OrderEvents, Order> persister;

    public  Boolean process(Order order, OrderEvents event){
        Message<OrderEvents> message = MessageBuilder.withPayload(event)
                .setHeader("order", order).build();
        boolean b = sendEvent(message);
        return b;
    }

    @SneakyThrows
    private boolean sendEvent(Message<OrderEvents> message) {
        Order order = (Order) message.getHeaders().get("order");
        persister.restore(orderStateMachine, order);
        boolean result = orderStateMachine.sendEvent(message);
        return result;
    }
}

启动

package com.coderlk.state;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class StateMachineApplication {

    public static void main(String[] args) {
        SpringApplication.run(StateMachineApplication.class, args);
    }

}

测试

支付流程

2024-12-24T15:26:19.088+08:00  INFO 44471 --- [           main] c.c.state.service.OrderStateListener     : 订单表保存数据
2024-12-24T15:26:19.088+08:00  INFO 44471 --- [           main] c.c.state.service.OrderStateListener     : 发送站内消息
2024-12-24T15:26:19.088+08:00  INFO 44471 --- [           main] c.c.state.service.OrderStateListener     : 回调支付接口
2024-12-24T15:26:19.089+08:00  INFO 44471 --- [           main] c.c.state.service.OrderStateListener     : 同步至数据仓库

如果读者对其他流程感兴趣,可以自行测试


http://www.kler.cn/a/450627.html

相关文章:

  • 华为实训课笔记 2024 1223-1224
  • LabVIEW中什么和C 语言指针类似?
  • 医疗大模型威胁攻击下的医院AI安全:挑战与应对策略
  • uniapp跨平台开发---webview调用app方法
  • 微机接口课设——基于Proteus和8086的打地鼠设计(8255、8253、8259)
  • 【YashanDB知识库】jdbc查询st_geometry类型的数据时抛出YAS-00101错误
  • Xilinx 平台 drp 动态调节 mmcm
  • python基础知识(六)
  • 【day15】String常用API
  • 【论文阅读笔记】Learning to sample
  • 数据结构经典算法总复习(上卷)
  • redis延迟队列
  • 云边端一体化架构
  • pyinstaller打包资源文件和ini配置文件怎么放
  • 油漆面积(2017年蓝桥杯)
  • 在瑞芯微RK3588平台上使用RKNN部署YOLOv8Pose模型的C++实战指南
  • ABP vNext框架之EntityVersion
  • 绩效考核试题
  • 技术文档的语言表达:简洁、准确与易懂的平衡艺术
  • 嵌入式科普(24)从SPI和CAN通信重新理解“全双工”
  • 智能脂肪秤方案pcba设计研发步骤解析
  • 开发场景中Java 集合的最佳选择
  • 华为浏览器(HuaweiBrowser),简约高效上网更轻松
  • uniapp Native.js原生arr插件服务发送广播到uniapp页面中
  • leetcode 面试经典 150 题:螺旋矩阵
  • Spring基础分析13-Spring Security框架