java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Spring状态机 Statemachine

Spring状态机 Statemachine使用小结

作者:桂老七

状态机通过状态、事件、配置管理流程,分离业务逻辑与状态控制,实现结构化流转,本文主要介绍了Spring状态机Statemachine使用小结,感兴趣的可以了解一下

1. 状态机简介

状态机(State Machine)是一种描述系统行为的数学模型,核心思想是将系统抽象为有限个状态,并通过状态转移规则定义系统如何响应外部事件。它由一系列状态(States)、转换(Transitions)、事件(Events)、卫兵(Guards)和动作(Actions)组成。在软件开发中,状态机常用于控制程序的流程和状态变化。

其核心优势在于: 通过清晰明确的状态定义、自动状态更新,将复杂的状态流转逻辑结构化从而使得业务逻辑与状态解耦,减少了自己维护状态产生的大量if-else,显著提升代码可维护性和扩展性​​。

2. 核心组件介绍

2.1 流转逻辑相关:

组件​

处理逻辑类型​

State枚举

枚举对象所有的状态(流程图的节点)

Events枚举

枚举所有引起两个状态之间切换的事件(流程图的箭头)

Configurer配置类

组装State和Event的转换关系 (相当于流程图)

StateMachine状态机一个状态机表征一个对象实体,它拥有状态,并通过event的驱动来按照Configurer的配置进行状态变化,同时执行配置中指定的guard、action和listener

2.2 业务逻辑组件:

(具体业务开发在这里写,由框架负责调度)

组件​

处理逻辑类型​

是否阻塞迁移​

Guard

纯校验逻辑 (金额校验/渠道检查)

是,失败则中断迁移

Action

核心业务操作 (支付执行/库存扣减)

是,异常导致迁移失败

Listener

后续操作响应 (通知/日志/监控)

否,异步执行(也可同步)

2.3 流程示例图

3. 示例代码

3.1 依赖引入

<!--状态机-->
<dependency>
	<groupId>org.springframework.statemachine</groupId>
	<artifactId>spring-statemachine-starter</artifactId>
	<version>3.2.0</version>
</dependency>

<!-- redis持久化状态机--非必需组件,也可以用文件或数据库做持久化 -->
<dependency>
	<groupId>org.springframework.statemachine</groupId>
	<artifactId>spring-statemachine-redis</artifactId>
	<version>1.2.9.RELEASE</version>
</dependency>

3.2 状态枚举定义

public enum OrderStatus {
    // 待支付,待发货,待收货,已完成
    WAIT_PAYMENT(1, "待支付"),
    WAIT_DELIVER(2, "待发货"),
    WAIT_RECEIVE(3, "待收货"),
    FINISH(4, "已完成");
    private Integer key;
    private String desc;
    OrderStatus(Integer key, String desc) {
        this.key = key;
        this.desc = desc;
    }
    public Integer getKey() {
        return key;
    }
    public String getDesc() {
        return desc;
    }
}

3.3 事件枚举定义

public enum OrderStatusChangeEvent {
    // 支付,发货,确认收货
    PAYED, DELIVERY, RECEIVED,UNPAY;
    /**
     * 获取下一个事件枚举
     *
     * @return 下一个事件枚举
     */
    public OrderStatusChangeEvent getNextEvent() {
        switch (this) {
            case UNPAY:
                return PAYED;
            case PAYED:
                return DELIVERY;
            case DELIVERY:
                return RECEIVED;
            case RECEIVED:
                // 如果是最后一个事件,则返回null
                return null;
            default:
                // 如果枚举值不在预期范围内,则抛出异常
                throw new IllegalArgumentException("不支持的事件枚举值");
        }
    }
}

3.4 配置类

@Configuration
@EnableStateMachineFactory
public class OrderStateMachineConfig extends StateMachineConfigurerAdapter<OrderStatus, OrderStatusChangeEvent> {

    @Autowired
    private OrderGuard orderGuard;

    @Autowired
    private OrderAction orderAction;


    /**
     * 配置状态
     *
     * @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)
                .guard(orderGuard)
                .action(orderAction)
                .and()
                //发货事件:待发货-》待收货
                .withExternal()
                .source(OrderStatus.WAIT_DELIVER).target(OrderStatus.WAIT_RECEIVE)
                .event(OrderStatusChangeEvent.DELIVERY)
                .guard(orderGuard)
                .action(orderAction)
                .and()
                //收货事件:待收货-》已完成
                .withExternal()
                .source(OrderStatus.WAIT_RECEIVE).target(OrderStatus.FINISH)
                .event(OrderStatusChangeEvent.RECEIVED)
                .guard(orderGuard)
                .action(orderAction)
                .and()
                .withExternal()
                .source(OrderStatus.WAIT_DELIVER).target(OrderStatus.WAIT_PAYMENT)
                .event(OrderStatusChangeEvent.UNPAY)
                .guard(orderGuard)
                .action(orderAction);
    }
}

3.5 Guard--存放业务检查逻辑

业务开发只用关心实现这个Guard接口不用关心执行调度和状态维护。

@Component
public class OrderGuard implements Guard<OrderStatus, OrderStatusChangeEvent> {
    @Override
    public boolean evaluate(StateContext<OrderStatus, OrderStatusChangeEvent> context) {
        Order order = context.getMessage().getHeaders().get("order", Order.class);
        // ...自定义业务检查逻辑        
        return true;

    }
}

3.6 Action---存放业务执行逻辑

业务开发只用关心实现这个Action接口不用关心执行调度和状态维护

@Slf4j
@Component
public class OrderAction implements Action<OrderStatus,OrderStatusChangeEvent> {

    @Resource
    private OrderMapper orderMapper;
    
    @Override
    public void execute(StateContext<OrderStatus, OrderStatusChangeEvent> stateContext) {
        try {
            Order order = stateContext.getMessage().getHeaders().get("order", Order.class);
            State<OrderStatus, OrderStatusChangeEvent> target = stateContext.getTarget();
            order.setStatus(target.getId().getKey());
            log.info("orderAction:{}", order);
            // 自定义业务执行逻辑
            
        } catch (Exception e) {
            log.error("订单状态机执行异常", e);
            stateContext.getStateMachine().setStateMachineError(new RuntimeException("自定义错误"));
            throw e;
        }

    }
}

3.7 核心控制类

对并发有要求的使用工厂方法,每个订单都单独使用一个状态机表达

@Service("orderService")
@Slf4j
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {

    @Autowired
    private StateMachineFactory<OrderStatus, OrderStatusChangeEvent> stateMachineFactory;

    @Resource
    private StateMachinePersister<OrderStatus, OrderStatusChangeEvent, String> stateMachineRedisPersister;

    @Resource
    private OrderMapper orderMapper;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    OrderStateListener orderStateListener;

    /**
     * 创建订单
     *
     * @param order
     * @return
     */
    @DS("oracle-xxx")
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public Order create(Order order) {
        order.setStatus(OrderStatus.WAIT_PAYMENT.getKey());
        orderMapper.insert(order);
        return order;
    }
    /**
     * 对订单进行支付
     *
     * @param id
     * @return
     */
    @DS("oracle-xxx")
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public boolean pay(Long id) {
        Order order = orderMapper.selectById(id);
        log.info("线程名称:{},尝试支付,订单号:{}" ,Thread.currentThread().getName() , id);
        return autoSendEvent(OrderStatusChangeEvent.PAYED, order);
    }
    

    /**
     * 发送订单状态转换事件
     * synchronized修饰保证这个方法是线程安全的
     *
     * @param changeEvent
     * @param order
     * @return
     */
    private boolean sendEvent(OrderStatusChangeEvent changeEvent, Order order) {
        boolean result = false;
        StateMachine<OrderStatus, OrderStatusChangeEvent> orderStateMachine = null;
        try {
            // 工厂方法生成状态机
            orderStateMachine = stateMachineFactory.getStateMachine(order.getId());
            orderStateMachine.addStateListener(orderStateListener);
            //启动状态机
            orderStateMachine.start();
            //尝试恢复状态机状态
            stateMachineRedisPersister.restore(orderStateMachine, String.valueOf(order.getId()));
            Message message = MessageBuilder.withPayload(changeEvent).setHeader("order", order).build();
            result = orderStateMachine.sendEvent(message);
            boolean hasError = orderStateMachine.hasStateMachineError();
            //持久化状态机状态
            if(hasError){
                return !hasError;
            }
            stateMachineRedisPersister.persist(orderStateMachine, String.valueOf(order.getId()));
            // 已结束的流程 给key加上过期时间
            if (order.getStatus() == OrderStatus.FINISH.getKey()) {
                redisUtil.set(order.getId(), order.getStatus(), 60 * 60 * 24, TimeUnit.SECONDS);
            }
        } catch (Exception e) {
            log.error("订单操作失败:{}", e);
        } finally{
            if (orderStateMachine != null) {
                // 释放状态机
                orderStateMachine.stop();
            }
        }
        return result;
    }

    private boolean autoSendEvent(OrderStatusChangeEvent changeEvent, Order order) {
        boolean b = true;
        while (b && changeEvent != null) {
            b = sendEvent(changeEvent, order);
            changeEvent = changeEvent.getNextEvent();
        }
        return b;
    }
   

3.8 监听器

用来执行一些不影响流程主流程的 日志、通知之类的任务,可异步

@Component
@Slf4j
public class OrderStateListener extends StateMachineListenerAdapter<OrderStatus, OrderStatusChangeEvent> {


    private StateMachine<OrderStatus, OrderStatusChangeEvent> stateMachine;
    private Message<OrderStatusChangeEvent> message;


    @Override
    public void stateContext(StateContext<OrderStatus, OrderStatusChangeEvent> context) {
        
        StateMachine<OrderStatus, OrderStatusChangeEvent> stateMachine = context.getStateMachine();
        this.stateMachine = stateMachine;
        // 获取当前事件
        Message<OrderStatusChangeEvent> message = context.getMessage();
        this.message = message;

        // 获取状态机 ID
        String machineId = stateMachine.getId();

        // 应用场景:订单状态变更记录
        if (context.getStage() == StateContext.Stage.STATE_CHANGED) {
            OrderStatus oldStatus = context.getSource().getId();
            OrderStatus newStatus = context.getTarget().getId();
            log.info("订单 {} 状态变更: {} → {}", machineId, oldStatus, newStatus);
        }

        if(context.getException()!=null){
            log.error("状态机异常: {}",context.getException().getMessage());
        }
    }

    @DS("oracle-xxx")
    @Override
    public void stateChanged(State<OrderStatus, OrderStatusChangeEvent> from, State<OrderStatus, OrderStatusChangeEvent> to) {
        // 状态变更时触发(如 UNPAID → PAID)
        System.out.println("状态变更: " + from.getId() + " → " + to.getId());
        Order order = message.getHeaders().get("order", Order.class);

        order.setStatus(to.getId().getKey());


    }

    @Override
    public void stateEntered(State<OrderStatus, OrderStatusChangeEvent> state) {
        // 进入新状态时触发
        System.out.println("进入状态: " + state.getId());
    }

    @Override
    @Async
    public void eventNotAccepted(Message<OrderStatusChangeEvent> message) {
        // 事件被拒绝时触发(如非法状态转换)
        System.err.println("事件拒绝开始: " + message.getPayload());
        Order order = message.getHeaders().get("order", Order.class);
        try {
            Thread.sleep(60000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.err.println("事件拒绝结束: " + message.getPayload());
    }

    @Override
    @Async
    public void stateMachineError(StateMachine<OrderStatus, OrderStatusChangeEvent> stateMachine, Exception exception) {
        // 只能建ring状态机本身在运行中发生的异常,不能处理action抛出的异常
        // 可以在此进行重试、告警等操作
        log.error("错误处理: {}", exception.getMessage());
    }

    @Override
    public void stateExited(State<OrderStatus, OrderStatusChangeEvent> state) {
        // 状态机退出时触发
        log.info("状态退出: {}", state.getId());
    }


}

3.9 持久化

这里直接用的redis持久化,可自己定义成其他持久化方式

@Configuration
@Slf4j
public class Persist<E, S> {

    @Resource
    private RedisConnectionFactory redisConnectionFactory;
    /**
     * 持久化到redis中,在分布式系统中使用
     *
     * @return
     */
    @Bean(name = "stateMachineRedisPersister")
    public RedisStateMachinePersister<E, S> getRedisPersister() {
        RedisStateMachineContextRepository<E, S> repository = new RedisStateMachineContextRepository<>(redisConnectionFactory);
        RepositoryStateMachinePersist p = new RepositoryStateMachinePersist<>(repository);
        return new RedisStateMachinePersister<>(p);
    }
}

4. 总结

1. 状态转移通过配置类维护,可读性高,新增状态时维护方便。

2. 状态之间转移通过由配置强制指定,不容易出现逻辑错乱。

3. 加锁方便,可以在统一的事件触发入口加redis分布式锁。

4. 业务逻辑处理存放在Guard​、Action​、监听器中,各司其职结构清晰,代码逻辑解耦。

到此这篇关于Spring状态机 Statemachine使用小结的文章就介绍到这了,更多相关Spring状态机 Statemachine内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文