一、状态机的使用场景分析
状态机(State Machine)是一种行为设计模式,用于描述对象在其生命周期内所经历的各种状态,以及在不同状态之间的转换规则和转换时触发的动作。上述代码实现的订单状态机,在很多业务场景中都有广泛的应用,下面列举了两个常见使用场景:
1. 订单管理系统
在电商、外卖、物流等业务中,订单会经历多个状态,如待支付、待发货、待收货、已完成等。使用状态机可以清晰地定义订单状态之间的转换规则,确保订单状态的流转符合业务逻辑。例如,只有在用户完成支付后,订单才能从待支付状态转换到待发货状态;只有在商家发货后,订单才能从待发货状态转换到待收货状态。
2. 工作流管理
在企业的业务流程中,很多任务都有明确的状态和流转规则,如请假流程、审批流程等。状态机可以帮助管理这些工作流,确保每个任务按照预定的流程进行处理。例如,员工提交请假申请后,请假申请会处于待审批状态;审批人审批通过后,请假申请会转换到已批准状态。
二、状态机使用步骤
1、定义状态枚举类
// 订单状态枚举
public enum OrderStatus {
WAIT_PAYMENT, WAIT_DELIVER, WAIT_RECEIVE, FINISH
}
// 订单状态变更事件枚举
public enum OrderStatusChangeEvent {
PAYED, DELIVERY, RECEIVED
}
- 配置状态机
创建一个配置类,继承自 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());
}
};
}
}
- 定义状态转换监听器
创建一个状态转换监听器类,使用 @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);
// 其他业务
}
}
- 使用状态机
在业务代码中,注入状态机并发送事件来触发状态转换。
@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) 方法将更新后的订单信息保存到数据库中
总结:当业务逻辑触发状态转换时,代码会依次经过业务逻辑触发事件—> 状态机匹配转换规则—> 触发状态转换监听器—> 执行监听器中的业务逻辑这几个步骤