Spring StateMachine嵌套狀態(tài)流轉(zhuǎn)
1、問(wèn)題概述
在 Spring StateMachine 中,嵌套狀態(tài)(Substates / Hierarchical States)是指在一個(gè)父狀態(tài)(Parent State)內(nèi)部,包含了一組子狀態(tài)(Child States)。這種設(shè)計(jì)非常適合用來(lái)表達(dá)“狀態(tài)中的狀態(tài)”,能夠?qū)?fù)雜的業(yè)務(wù)邏輯按層級(jí)進(jìn)行拆分。
在嵌套狀態(tài)中,狀態(tài)流轉(zhuǎn)遵循一個(gè)核心原則:如果狀態(tài)機(jī)當(dāng)前處于某個(gè)子狀態(tài),那么它必然也同時(shí)處于該子狀態(tài)的父狀態(tài)。
假設(shè)訂單業(yè)務(wù)中有如下狀態(tài):
- PENDING_PAY,頂層狀態(tài):待支付(初始狀態(tài))
- IN_PAYMENT,父狀態(tài):支付中
PAY_PREPARE,子狀態(tài):支付準(zhǔn)備中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):支付準(zhǔn)備中
PAY_PROCESSING, // 子狀態(tài):扣款中
PENDING_DELIVER // 結(jié)束狀態(tài):待發(fā)貨
}//訂單事件枚舉
public enum OrderEvent {
GO_PAY, // 外部事件:去支付(待支付 -> 支付中)
BANK_CONNECT, // 內(nèi)部事件:連接渠道(支付準(zhǔn)備中 -> 扣款中)
PAY_SUCCESS, // 業(yè)務(wù)結(jié)果事件:扣款成功(從扣款中跳出 -> 待發(fā)貨)
PAY_FAIL // 業(yè)務(wù)結(jié)果事件:扣款失?。◤目劭钪刑?-> 回到待支付)
}3、配置層級(jí)狀態(tài)節(jié)點(diǎn)
在 configure(StateMachineStateConfigurer) 中,我們利用 .parent() 語(yǔ)法來(lái)編織狀態(tài)之間的樹狀層級(jí)關(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()
//注冊(cè)最外層的頂層獨(dú)立狀態(tài)
.initial(OrderState.PENDING_PAY) //頂層起點(diǎn):待支付
.end(OrderState.PENDING_DELIVER) //頂層終點(diǎn):待發(fā)貨
.state(OrderState.IN_PAYMENT) //宏觀負(fù)狀態(tài):支付中
.and()
//嵌套子狀態(tài)
.withStates()
.parent(OrderState.IN_PAYMENT) //指明父節(jié)點(diǎn)
.initial(OrderState.PAY_PREPARE) //子狀態(tài)的第一個(gè)狀態(tài)
.state(OrderState.PAY_PROCESSING);//扣款中
}
}4、配置層級(jí)流轉(zhuǎn)路由 rules
在配置流轉(zhuǎn)時(shí),Spring StateMachine 的嵌套路由支持兩種經(jīng)典的流轉(zhuǎn)模式:
- 外部流轉(zhuǎn)(withExternal):導(dǎo)致狀態(tài)發(fā)生宏觀層級(jí)跨越的跳轉(zhuǎn)(比如跳入/跳出父狀態(tài))。
- 局部流轉(zhuǎn)(withLocal):在父狀態(tài)的樹內(nèi)部進(jìn)行安全的子狀態(tài)切換,不會(huì)觸發(fā)父狀態(tài)的 onExit 或 onEntry 動(dòng)作。
@Override
public void configure(StateMachineTransitionConfigurer<OrderState, OrderEvent> transitions) throws Exception {
transitions
// 1. 待支付 -> 跳入父狀態(tài)(支付中)
// 此時(shí)狀態(tài)機(jī)激活集合變?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):支付準(zhǔn)備中 -> 扣款中
// 此時(shí)狀態(tài)機(jī)激活集合變?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)機(jī)將徹底銷毀內(nèi)部的所有子狀態(tài)指針
.withExternal()
.source(OrderState.IN_PAYMENT).target(OrderState.PENDING_DELIVER)
.event(OrderEvent.PAY_SUCCESS)
.and()
// 4. 【核心流轉(zhuǎn)二】:扣款失敗,打破父狀態(tài),無(wú)條件滾回到頂層的“待支付”
// 狀態(tài)機(jī)同樣會(huì)銷毀內(nèi)部指針,讓訂單恢復(fù)到最初可以重新發(fā)起支付的狀態(tài)
.withExternal()
.source(OrderState.IN_PAYMENT).target(OrderState.PENDING_PAY)
.event(OrderEvent.PAY_FAIL);
}5、測(cè)試調(diào)用與狀態(tài)打印
我們?cè)跍y(cè)試中連續(xù)投遞事件,看看狀態(tài)機(jī)的內(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("當(dāng)前激活狀態(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("當(dāng)前激活狀態(tài): " + sm.getState().getIds());
// ?? 預(yù)期輸出: [IN_PAYMENT, PAY_PREPARE] (父子同時(shí)激活)
// 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("當(dāng)前激活狀態(tài): " + sm.getState().getIds());
// ?? 預(yù)期輸出: [IN_PAYMENT, PAY_PROCESSING] (父狀態(tài)不變,子狀態(tài)向前)
// 4. 支付成功 -> 強(qiáng)行打破嵌套,融合成頂層終態(tài)
sm.sendEvent(Mono.just(MessageBuilder.withPayload(OrderEvent.PAY_SUCCESS).build())).blockLast();
System.out.println("--- 4. 發(fā)送 PAY_SUCCESS 后 ---");
System.out.println("當(dāng)前激活狀態(tài): " + sm.getState().getIds());
// ?? 預(yù)期輸出: [PENDING_DELIVER] (子狀態(tài)全部銷毀,回歸單頂層狀態(tài))
}--- 初始狀態(tài) --- [PENDING_PAY] --- 1. 點(diǎn)擊去支付后 --- [IN_PAYMENT, PAY_PREPARE] --- 2. 進(jìn)入銀行扣款中后 --- [IN_PAYMENT, PAY_PROCESSING] --- 3. 收到扣款失敗回調(diào)后 --- [PENDING_PAY] --- 4. 第二次嘗試,重新進(jìn)入扣款中 --- [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)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
mongo分布式鎖Java實(shí)現(xiàn)方法(推薦)
下面小編就為大家?guī)?lái)一篇mongo分布式鎖Java實(shí)現(xiàn)方法(推薦)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-07-07
探討Java 將Markdown文件轉(zhuǎn)換為Word和PDF文檔
這篇文章主要介紹了Java 將Markdown文件轉(zhuǎn)換為Word和PDF文檔,本文通過(guò)分步指南及代碼示例展示了如何將 Markdown 文件轉(zhuǎn)換為 Word 文檔和 PDF 文件,需要的朋友可以參考下2024-07-07
關(guān)于Java中Object類的幾個(gè)方法示例
這篇文章主要給大家介紹了關(guān)于Java中Object類的幾個(gè)方法的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-05-05
SpringBoot中的統(tǒng)一異常處理詳細(xì)解析
自定義SpringBoot的白標(biāo)錯(cuò)誤頁(yè)面的操作方法
java中LinkedBlockingQueue與ArrayBlockingQueue的異同
利用SpringBoot和LiteFlow解鎖復(fù)雜流程

