一、狀態(tài)機的使用場景分析
狀態(tài)機(State Machine)是一種行為設計模式,用于描述對象在其生命周期內(nèi)所經(jīng)歷的各種狀態(tài),以及在不同狀態(tài)之間的轉換規(guī)則和轉換時觸發(fā)的動作。上述代碼實現(xiàn)的訂單狀態(tài)機,在很多業(yè)務場景中都有廣泛的應用,下面列舉了兩個常見使用場景:
1. 訂單管理系統(tǒng)
在電商、外賣、物流等業(yè)務中,訂單會經(jīng)歷多個狀態(tài),如待支付、待發(fā)貨、待收貨、已完成等。使用狀態(tài)機可以清晰地定義訂單狀態(tài)之間的轉換規(guī)則,確保訂單狀態(tài)的流轉符合業(yè)務邏輯。例如,只有在用戶完成支付后,訂單才能從待支付狀態(tài)轉換到待發(fā)貨狀態(tài);只有在商家發(fā)貨后,訂單才能從待發(fā)貨狀態(tài)轉換到待收貨狀態(tài)。
2. 工作流管理
在企業(yè)的業(yè)務流程中,很多任務都有明確的狀態(tài)和流轉規(guī)則,如請假流程、審批流程等。狀態(tài)機可以幫助管理這些工作流,確保每個任務按照預定的流程進行處理。例如,員工提交請假申請后,請假申請會處于待審批狀態(tài);審批人審批通過后,請假申請會轉換到已批準狀態(tài)。
二、狀態(tài)機使用步驟
1、定義狀態(tài)枚舉類
// 訂單狀態(tài)枚舉
public enum OrderStatus {
WAIT_PAYMENT, WAIT_DELIVER, WAIT_RECEIVE, FINISH
}
// 訂單狀態(tài)變更事件枚舉
public enum OrderStatusChangeEvent {
PAYED, DELIVERY, RECEIVED
}
- 配置狀態(tài)機
創(chuàng)建一個配置類,繼承自 EnumStateMachineConfigurerAdapter,并使用 @EnableStateMachine 注解啟用狀態(tài)機。在配置類中,需要完成以下幾個方面的配置:
狀態(tài)機配置:設置狀態(tài)機的自動啟動和監(jiān)聽器。
狀態(tài)配置:定義狀態(tài)機的初始狀態(tài)和所有可能的狀態(tài)。
轉換配置:定義狀態(tài)之間的轉換規(guī)則,包括源狀態(tài)、目標狀態(tài)和觸發(fā)事件。
@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());
}
};
}
}
- 定義狀態(tài)轉換監(jiān)聽器
創(chuàng)建一個狀態(tài)轉換監(jiān)聽器類,使用 @WithStateMachine 注解關聯(lián)狀態(tài)機,并使用 @OnTransition 注解定義狀態(tài)轉換時的處理邏輯。
@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("支付,狀態(tài)機反饋信息:{}", message.getHeaders());
// 更新訂單
order.waitDeliver();
// orderMapper.updateById(order);
// 其他業(yè)務
}
@OnTransition(source = "WAIT_DELIVER", target = "WAIT_RECEIVE")
public void deliverTransition(@Nonnull Message<OrderStatusChangeEvent> message) {
OrderEntity order = (OrderEntity) message.getHeaders().get("order");
log.info("發(fā)貨,狀態(tài)機反饋信息:{}", message.getHeaders());
// 更新訂單
order.waitReceive();
// orderMapper.updateById(order);
// 其他業(yè)務
}
@OnTransition(source = "WAIT_RECEIVE", target = "FINISH")
public void receiveTransition(@Nonnull Message<OrderStatusChangeEvent> message) {
OrderEntity order = (OrderEntity) message.getHeaders().get("order");
log.info("確認收貨,狀態(tài)機反饋信息:{}", message.getHeaders());
// 更新訂單
order.finish();
// orderMapper.updateById(order);
// 其他業(yè)務
}
}
- 使用狀態(tài)機
在業(yè)務代碼中,注入狀態(tài)機并發(fā)送事件來觸發(fā)狀態(tài)轉換。
@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());
}
}
以一種狀態(tài)轉移情況為例,說明代碼調(diào)用過程:
步驟 1:業(yè)務邏輯觸發(fā)事件
例如在 OrderService 類中的 payOrder 方法:首先調(diào)用 orderStateMachine.start() 啟動狀態(tài)機,使其進入初始狀態(tài) WAIT_PAYMENT。然后使用 MessageBuilder 構建一個包含 PAYED 事件和訂單信息的消息,并通過 orderStateMachine.sendEvent 方法將消息發(fā)送給狀態(tài)機
步驟 2:狀態(tài)機接收事件并匹配轉換規(guī)則
狀態(tài)機接收到發(fā)送的事件消息后,會根據(jù)之前在配置類中定義的狀態(tài)轉換規(guī)則進行匹配。在 OrderStateMachineConfig 類中,定義了從 WAIT_PAYMENT 到 WAIT_DELIVER 由 PAYED 事件觸發(fā)的轉換規(guī)則
步驟 3:觸發(fā)狀態(tài)轉換監(jiān)聽器
狀態(tài)機完成狀態(tài)轉換后,會檢查是否存在與該狀態(tài)轉換匹配的狀態(tài)轉換監(jiān)聽器。在 OrderStateListener 類中,使用 @OnTransition 注解標記了在從 WAIT_PAYMENT 到 WAIT_DELIVER 狀態(tài)轉換時需要執(zhí)行的方法
步驟 4:執(zhí)行監(jiān)聽器中的業(yè)務邏輯
在 payTransition 方法內(nèi)部,會執(zhí)行具體的業(yè)務邏輯。首先從消息的頭部信息中獲取 OrderEntity 對象,然后記錄支付狀態(tài)轉換的日志,接著調(diào)用 order.waitDeliver() 方法更新訂單狀態(tài),還可以調(diào)用 orderMapper.updateById(order) 方法將更新后的訂單信息保存到數(shù)據(jù)庫中
總結:當業(yè)務邏輯觸發(fā)狀態(tài)轉換時,代碼會依次經(jīng)過業(yè)務邏輯觸發(fā)事件—> 狀態(tài)機匹配轉換規(guī)則—> 觸發(fā)狀態(tài)轉換監(jiān)聽器—> 執(zhí)行監(jiān)聽器中的業(yè)務邏輯這幾個步驟