Spring StateMachine嵌套狀態(tài)流轉(zhuǎn)
1、問題概述
在 Spring StateMachine 中,嵌套狀態(tài)(Substates / Hierarchical States)是指在一個父狀態(tài)(Parent State)內(nèi)部,包含了一組子狀態(tài)(Child States)。這種設(shè)計非常適合用來表達“狀態(tài)中的狀態(tài)”,能夠?qū)碗s的業(yè)務邏輯按層級進行拆分。
在嵌套狀態(tài)中,狀態(tài)流轉(zhuǎn)遵循一個核心原則:如果狀態(tài)機當前處于某個子狀態(tài),那么它必然也同時處于該子狀態(tài)的父狀態(tài)。
假設(shè)訂單業(yè)務中有如下狀態(tài):
- PENDING_PAY,頂層狀態(tài):待支付(初始狀態(tài))
- IN_PAYMENT,父狀態(tài):支付中
PAY_PREPARE,子狀態(tài):支付準備中PAY_PROCESSING,子狀態(tài):扣款中
- PENDING_DELIVER,結(jié)束狀態(tài):待發(fā)貨
2、定義狀態(tài)與事件枚舉
//訂單狀態(tài)枚舉
public enum OrderState {
PENDING_PAY, // 頂層狀態(tài):待支付(初始狀態(tài))
IN_PAYMENT, // 父狀態(tài):支付中
// 以下為 IN_PAYMENT 的內(nèi)部子狀態(tài)
PAY_PREPARE, // 子狀態(tài):支付準備中
PAY_PROCESSING, // 子狀態(tài):扣款中
PENDING_DELIVER // 結(jié)束狀態(tài):待發(fā)貨
}//訂單事件枚舉
public enum OrderEvent {
GO_PAY, // 外部事件:去支付(待支付 -> 支付中)
BANK_CONNECT, // 內(nèi)部事件:連接渠道(支付準備中 -> 扣款中)
PAY_SUCCESS, // 業(yè)務結(jié)果事件:扣款成功(從扣款中跳出 -> 待發(fā)貨)
PAY_FAIL // 業(yè)務結(jié)果事件:扣款失?。◤目劭钪刑?-> 回到待支付)
}3、配置層級狀態(tài)節(jié)點
在 configure(StateMachineStateConfigurer) 中,我們利用 .parent() 語法來編織狀態(tài)之間的樹狀層級關(guān)系。
@Configuration
@EnableStateMachineFactory(name = "paymentStateMachineFactory")
public class PaymentStateMachineConfig extends EnumStateMachineConfigurerAdapter<OrderState, OrderEvent> {
@Override
public void configure(StateMachineStateConfigurer<OrderState, OrderEvent> states) throws Exception {
states
.withStates()
//注冊最外層的頂層獨立狀態(tài)
.initial(OrderState.PENDING_PAY) //頂層起點:待支付
.end(OrderState.PENDING_DELIVER) //頂層終點:待發(fā)貨
.state(OrderState.IN_PAYMENT) //宏觀負狀態(tài):支付中
.and()
//嵌套子狀態(tài)
.withStates()
.parent(OrderState.IN_PAYMENT) //指明父節(jié)點
.initial(OrderState.PAY_PREPARE) //子狀態(tài)的第一個狀態(tài)
.state(OrderState.PAY_PROCESSING);//扣款中
}
}4、配置層級流轉(zhuǎn)路由 rules
在配置流轉(zhuǎn)時,Spring StateMachine 的嵌套路由支持兩種經(jīng)典的流轉(zhuǎn)模式:
- 外部流轉(zhuǎn)(withExternal):導致狀態(tài)發(fā)生宏觀層級跨越的跳轉(zhuǎn)(比如跳入/跳出父狀態(tài))。
- 局部流轉(zhuǎn)(withLocal):在父狀態(tài)的樹內(nèi)部進行安全的子狀態(tài)切換,不會觸發(fā)父狀態(tài)的 onExit 或 onEntry 動作。
@Override
public void configure(StateMachineTransitionConfigurer<OrderState, OrderEvent> transitions) throws Exception {
transitions
// 1. 待支付 -> 跳入父狀態(tài)(支付中)
// 此時狀態(tài)機激活集合變?yōu)椋篬IN_PAYMENT, PAY_PREPARE]
.withExternal()
.source(OrderState.PENDING_PAY).target(OrderState.IN_PAYMENT)
.event(OrderEvent.GO_PAY)
.and()
// 2. 父狀態(tài)內(nèi)部流轉(zhuǎn):支付準備中 -> 扣款中
// 此時狀態(tài)機激活集合變?yōu)椋篬IN_PAYMENT, PAY_PROCESSING]
.withExternal()
.source(OrderState.PAY_PREPARE).target(OrderState.PAY_PROCESSING)
.event(OrderEvent.BANK_CONNECT)
.and()
// 3. 【核心流轉(zhuǎn)一】:扣款成功,打破父狀態(tài),流轉(zhuǎn)到頂層的“待發(fā)貨”
// 狀態(tài)機將徹底銷毀內(nèi)部的所有子狀態(tài)指針
.withExternal()
.source(OrderState.IN_PAYMENT).target(OrderState.PENDING_DELIVER)
.event(OrderEvent.PAY_SUCCESS)
.and()
// 4. 【核心流轉(zhuǎn)二】:扣款失敗,打破父狀態(tài),無條件滾回到頂層的“待支付”
// 狀態(tài)機同樣會銷毀內(nèi)部指針,讓訂單恢復到最初可以重新發(fā)起支付的狀態(tài)
.withExternal()
.source(OrderState.IN_PAYMENT).target(OrderState.PENDING_PAY)
.event(OrderEvent.PAY_FAIL);
}5、測試調(diào)用與狀態(tài)打印
我們在測試中連續(xù)投遞事件,看看狀態(tài)機的內(nèi)存激活狀態(tài)集合(sm.getState().getIds())是如何展現(xiàn)嵌套層次的。
public void testHierarchicalStates() {
StateMachine<OrderState, OrderEvent> sm = factory.getStateMachine("order_999");
sm.startReactively().block();
System.out.println("--- 1. 初始化 ---");
System.out.println("當前激活狀態(tài): " + sm.getState().getIds()); // [PENDING_ORDER]
// 2. 去支付 -> 激活嵌套
sm.sendEvent(Mono.just(MessageBuilder.withPayload(OrderEvent.GO_PAY).build())).blockLast();
System.out.println("--- 2. 發(fā)送 GO_PAY 后 ---");
System.out.println("當前激活狀態(tài): " + sm.getState().getIds());
// ?? 預期輸出: [IN_PAYMENT, PAY_PREPARE] (父子同時激活)
// 3. 連接銀行 -> 子狀態(tài)流轉(zhuǎn)
sm.sendEvent(Mono.just(MessageBuilder.withPayload(OrderEvent.BANK_CONNECT).build())).blockLast();
System.out.println("--- 3. 發(fā)送 BANK_CONNECT 后 ---");
System.out.println("當前激活狀態(tài): " + sm.getState().getIds());
// ?? 預期輸出: [IN_PAYMENT, PAY_PROCESSING] (父狀態(tài)不變,子狀態(tài)向前)
// 4. 支付成功 -> 強行打破嵌套,融合成頂層終態(tài)
sm.sendEvent(Mono.just(MessageBuilder.withPayload(OrderEvent.PAY_SUCCESS).build())).blockLast();
System.out.println("--- 4. 發(fā)送 PAY_SUCCESS 后 ---");
System.out.println("當前激活狀態(tài): " + sm.getState().getIds());
// ?? 預期輸出: [PENDING_DELIVER] (子狀態(tài)全部銷毀,回歸單頂層狀態(tài))
}--- 初始狀態(tài) --- [PENDING_PAY] --- 1. 點擊去支付后 --- [IN_PAYMENT, PAY_PREPARE] --- 2. 進入銀行扣款中后 --- [IN_PAYMENT, PAY_PROCESSING] --- 3. 收到扣款失敗回調(diào)后 --- [PENDING_PAY] --- 4. 第二次嘗試,重新進入扣款中 --- [IN_PAYMENT, PAY_PROCESSING] --- 5. 收到扣款成功回調(diào)后 --- [PENDING_DELIVER]
到此這篇關(guān)于Spring StateMachine嵌套狀態(tài)流轉(zhuǎn)的文章就介紹到這了,更多相關(guān)Spring StateMachine嵌套狀態(tài)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
探討Java 將Markdown文件轉(zhuǎn)換為Word和PDF文檔
這篇文章主要介紹了Java 將Markdown文件轉(zhuǎn)換為Word和PDF文檔,本文通過分步指南及代碼示例展示了如何將 Markdown 文件轉(zhuǎn)換為 Word 文檔和 PDF 文件,需要的朋友可以參考下2024-07-07
java中LinkedBlockingQueue與ArrayBlockingQueue的異同
這篇文章主要介紹了java中LinkedBlockingQueue與ArrayBlockingQueue的異同,需要的朋友可以參考下2016-08-08

