状态转换机(StateMachine)

一、状态机的使用场景分析
状态机(State Machine)是一种行为设计模式,用于描述对象在其生命周期内所经历的各种状态,以及在不同状态之间的转换规则和转换时触发的动作。上述代码实现的订单状态机,在很多业务场景中都有广泛的应用,下面列举了两个常见使用场景:
1. 订单管理系统
在电商、外卖、物流等业务中,订单会经历多个状态,如待支付、待发货、待收货、已完成等。使用状态机可以清晰地定义订单状态之间的转换规则,确保订单状态的流转符合业务逻辑。例如,只有在用户完成支付后,订单才能从待支付状态转换到待发货状态;只有在商家发货后,订单才能从待发货状态转换到待收货状态。
2. 工作流管理
在企业的业务流程中,很多任务都有明确的状态和流转规则,如请假流程、审批流程等。状态机可以帮助管理这些工作流,确保每个任务按照预定的流程进行处理。例如,员工提交请假申请后,请假申请会处于待审批状态;审批人审批通过后,请假申请会转换到已批准状态。


二、状态机使用步骤
1、定义状态枚举类

// 订单状态枚举
public enum OrderStatus {
    WAIT_PAYMENT, WAIT_DELIVER, WAIT_RECEIVE, FINISH
}

// 订单状态变更事件枚举
public enum OrderStatusChangeEvent {
    PAYED, DELIVERY, RECEIVED
}
  1. 配置状态机
    创建一个配置类,继承自 EnumStateMachineConfigurerAdapter,并使用 @EnableStateMachine 注解启用状态机。在配置类中,需要完成以下几个方面的配置:
    状态机配置:设置状态机的自动启动和监听器。
    状态配置:定义状态机的初始状态和所有可能的状态。
    转换配置:定义状态之间的转换规则,包括源状态、目标状态和触发事件。
@Configuration
@EnableStateMachine(name = "orderStateMachine")
public class OrderStateMachineConfig extends EnumStateMachineConfigurerAdapter<OrderStatus, OrderStatusChangeEvent> {

    @Override
    public void configure(StateMachineConfigurationConfigurer<OrderStatus, OrderStatusChangeEvent> config) throws Exception {
        config.withConfiguration()
                .autoStartup(true)
                .listener(listener());
    }

    @Override
    public void configure(StateMachineStateConfigurer<OrderStatus, OrderStatusChangeEvent> states) throws Exception {
        states.withStates()
                .initial(OrderStatus.WAIT_PAYMENT)
                .states(EnumSet.allOf(OrderStatus.class));
    }

    @Override
    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);
    }

    @Bean
    public StateMachineListener<OrderStatus, OrderStatusChangeEvent> listener() {
        return new StateMachineListenerAdapter<>() {
            @Override
            public void stateChanged(State<OrderStatus, OrderStatusChangeEvent> from, State<OrderStatus, OrderStatusChangeEvent> to) {
                System.out.println("State change to " + to.getId());
            }
        };
    }
}
  1. 定义状态转换监听器
    创建一个状态转换监听器类,使用 @WithStateMachine 注解关联状态机,并使用 @OnTransition 注解定义状态转换时的处理逻辑。
@Component("orderStateListener")
@WithStateMachine(name = "orderStateMachine")
public class OrderStateListener {
    private static final Logger log = LoggerFactory.getLogger(OrderStateListener.class);

    @OnTransition(source = "WAIT_PAYMENT", target = "WAIT_DELIVER")
    public void payTransition(@Nonnull Message<OrderStatusChangeEvent> message) {
        OrderEntity order = message.getHeaders().get("order", OrderEntity.class);
        log.info("支付,状态机反馈信息:{}", message.getHeaders());
        // 更新订单
        order.waitDeliver();
        // orderMapper.updateById(order);
        // 其他业务
    }

    @OnTransition(source = "WAIT_DELIVER", target = "WAIT_RECEIVE")
    public void deliverTransition(@Nonnull Message<OrderStatusChangeEvent> message) {
        OrderEntity order = (OrderEntity) message.getHeaders().get("order");
        log.info("发货,状态机反馈信息:{}", message.getHeaders());
        // 更新订单
        order.waitReceive();
        // orderMapper.updateById(order);
        // 其他业务
    }

    @OnTransition(source = "WAIT_RECEIVE", target = "FINISH")
    public void receiveTransition(@Nonnull Message<OrderStatusChangeEvent> message) {
        OrderEntity order = (OrderEntity) message.getHeaders().get("order");
        log.info("确认收货,状态机反馈信息:{}", message.getHeaders());
        // 更新订单
        order.finish();
        // orderMapper.updateById(order);
        // 其他业务
    }
}
  1. 使用状态机
    在业务代码中,注入状态机并发送事件来触发状态转换。
@Service
public class OrderService {

    @Autowired
    private StateMachine<OrderStatus, OrderStatusChangeEvent> orderStateMachine;

    public void payOrder(OrderEntity order) {
        orderStateMachine.start();
        orderStateMachine.sendEvent(MessageBuilder.withPayload(OrderStatusChangeEvent.PAYED)
                .setHeader("order", order)
                .build());
    }

    public void deliverOrder(OrderEntity order) {
        orderStateMachine.sendEvent(MessageBuilder.withPayload(OrderStatusChangeEvent.DELIVERY)
                .setHeader("order", order)
                .build());
    }

    public void receiveOrder(OrderEntity order) {
        orderStateMachine.sendEvent(MessageBuilder.withPayload(OrderStatusChangeEvent.RECEIVED)
                .setHeader("order", order)
                .build());
    }
}

以一种状态转移情况为例,说明代码调用过程:

步骤 1:业务逻辑触发事件
例如在 OrderService 类中的 payOrder 方法:首先调用 orderStateMachine.start() 启动状态机,使其进入初始状态 WAIT_PAYMENT。然后使用 MessageBuilder 构建一个包含 PAYED 事件和订单信息的消息,并通过 orderStateMachine.sendEvent 方法将消息发送给状态机

步骤 2:状态机接收事件并匹配转换规则
状态机接收到发送的事件消息后,会根据之前在配置类中定义的状态转换规则进行匹配。在 OrderStateMachineConfig 类中,定义了从 WAIT_PAYMENT 到 WAIT_DELIVER 由 PAYED 事件触发的转换规则

步骤 3:触发状态转换监听器
状态机完成状态转换后,会检查是否存在与该状态转换匹配的状态转换监听器。在 OrderStateListener 类中,使用 @OnTransition 注解标记了在从 WAIT_PAYMENT 到 WAIT_DELIVER 状态转换时需要执行的方法

步骤 4:执行监听器中的业务逻辑
在 payTransition 方法内部,会执行具体的业务逻辑。首先从消息的头部信息中获取 OrderEntity 对象,然后记录支付状态转换的日志,接着调用 order.waitDeliver() 方法更新订单状态,还可以调用 orderMapper.updateById(order) 方法将更新后的订单信息保存到数据库中

总结:当业务逻辑触发状态转换时,代码会依次经过业务逻辑触发事件—> 状态机匹配转换规则—> 触发状态转换监听器—> 执行监听器中的业务逻辑这几个步骤

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容