Spring狀態(tài)機(jī) Statemachine使用小結(jié)
1. 狀態(tài)機(jī)簡介
狀態(tài)機(jī)(State Machine)是一種描述系統(tǒng)行為的數(shù)學(xué)模型,核心思想是將系統(tǒng)抽象為有限個(gè)狀態(tài),并通過狀態(tài)轉(zhuǎn)移規(guī)則定義系統(tǒng)如何響應(yīng)外部事件。它由一系列狀態(tài)(States)、轉(zhuǎn)換(Transitions)、事件(Events)、衛(wèi)兵(Guards)和動(dòng)作(Actions)組成。在軟件開發(fā)中,狀態(tài)機(jī)常用于控制程序的流程和狀態(tài)變化。
其核心優(yōu)勢在于: 通過清晰明確的狀態(tài)定義、自動(dòng)狀態(tài)更新,將復(fù)雜的狀態(tài)流轉(zhuǎn)邏輯結(jié)構(gòu)化。從而使得業(yè)務(wù)邏輯與狀態(tài)解耦,減少了自己維護(hù)狀態(tài)產(chǎn)生的大量if-else,顯著提升代碼可維護(hù)性和擴(kuò)展性??。
2. 核心組件介紹
2.1 流轉(zhuǎn)邏輯相關(guān):
組件? | 處理邏輯類型? |
State枚舉 | 枚舉對(duì)象所有的狀態(tài)(流程圖的節(jié)點(diǎn)) |
Events枚舉 | 枚舉所有引起兩個(gè)狀態(tài)之間切換的事件(流程圖的箭頭) |
Configurer配置類 | 組裝State和Event的轉(zhuǎn)換關(guān)系 (相當(dāng)于流程圖) |
StateMachine狀態(tài)機(jī) | 一個(gè)狀態(tài)機(jī)表征一個(gè)對(duì)象實(shí)體,它擁有狀態(tài),并通過event的驅(qū)動(dòng)來按照Configurer的配置進(jìn)行狀態(tài)變化,同時(shí)執(zhí)行配置中指定的guard、action和listener |
2.2 業(yè)務(wù)邏輯組件:
(具體業(yè)務(wù)開發(fā)在這里寫,由框架負(fù)責(zé)調(diào)度)
組件? | 處理邏輯類型? | 是否阻塞遷移? |
Guard? | 純校驗(yàn)邏輯 (金額校驗(yàn)/渠道檢查) | 是,失敗則中斷遷移 |
Action? | 核心業(yè)務(wù)操作 (支付執(zhí)行/庫存扣減) | 是,異常導(dǎo)致遷移失敗 |
Listener? | 后續(xù)操作響應(yīng) (通知/日志/監(jiān)控) | 否,異步執(zhí)行(也可同步) |
2.3 流程示例圖
3. 示例代碼
3.1 依賴引入
<!--狀態(tài)機(jī)--> <dependency> <groupId>org.springframework.statemachine</groupId> <artifactId>spring-statemachine-starter</artifactId> <version>3.2.0</version> </dependency> <!-- redis持久化狀態(tài)機(jī)--非必需組件,也可以用文件或數(shù)據(jù)庫做持久化 --> <dependency> <groupId>org.springframework.statemachine</groupId> <artifactId>spring-statemachine-redis</artifactId> <version>1.2.9.RELEASE</version> </dependency>
3.2 狀態(tài)枚舉定義
public enum OrderStatus { // 待支付,待發(fā)貨,待收貨,已完成 WAIT_PAYMENT(1, "待支付"), WAIT_DELIVER(2, "待發(fā)貨"), WAIT_RECEIVE(3, "待收貨"), FINISH(4, "已完成"); private Integer key; private String desc; OrderStatus(Integer key, String desc) { this.key = key; this.desc = desc; } public Integer getKey() { return key; } public String getDesc() { return desc; } }
3.3 事件枚舉定義
public enum OrderStatusChangeEvent { // 支付,發(fā)貨,確認(rèn)收貨 PAYED, DELIVERY, RECEIVED,UNPAY; /** * 獲取下一個(gè)事件枚舉 * * @return 下一個(gè)事件枚舉 */ public OrderStatusChangeEvent getNextEvent() { switch (this) { case UNPAY: return PAYED; case PAYED: return DELIVERY; case DELIVERY: return RECEIVED; case RECEIVED: // 如果是最后一個(gè)事件,則返回null return null; default: // 如果枚舉值不在預(yù)期范圍內(nèi),則拋出異常 throw new IllegalArgumentException("不支持的事件枚舉值"); } } }
3.4 配置類
@Configuration @EnableStateMachineFactory public class OrderStateMachineConfig extends StateMachineConfigurerAdapter<OrderStatus, OrderStatusChangeEvent> { @Autowired private OrderGuard orderGuard; @Autowired private OrderAction orderAction; /** * 配置狀態(tài) * * @param states * @throws Exception */ public void configure(StateMachineStateConfigurer<OrderStatus, OrderStatusChangeEvent> states) throws Exception { states .withStates() .initial(OrderStatus.WAIT_PAYMENT) .states(EnumSet.allOf(OrderStatus.class)); } /** * 配置狀態(tài)轉(zhuǎn)換事件關(guān)系 * * @param transitions * @throws Exception */ public void configure(StateMachineTransitionConfigurer<OrderStatus, OrderStatusChangeEvent> transitions) throws Exception { transitions //支付事件:待支付-》待發(fā)貨 .withExternal() .source(OrderStatus.WAIT_PAYMENT).target(OrderStatus.WAIT_DELIVER) .event(OrderStatusChangeEvent.PAYED) .guard(orderGuard) .action(orderAction) .and() //發(fā)貨事件:待發(fā)貨-》待收貨 .withExternal() .source(OrderStatus.WAIT_DELIVER).target(OrderStatus.WAIT_RECEIVE) .event(OrderStatusChangeEvent.DELIVERY) .guard(orderGuard) .action(orderAction) .and() //收貨事件:待收貨-》已完成 .withExternal() .source(OrderStatus.WAIT_RECEIVE).target(OrderStatus.FINISH) .event(OrderStatusChangeEvent.RECEIVED) .guard(orderGuard) .action(orderAction) .and() .withExternal() .source(OrderStatus.WAIT_DELIVER).target(OrderStatus.WAIT_PAYMENT) .event(OrderStatusChangeEvent.UNPAY) .guard(orderGuard) .action(orderAction); } }
3.5 Guard--存放業(yè)務(wù)檢查邏輯
業(yè)務(wù)開發(fā)只用關(guān)心實(shí)現(xiàn)這個(gè)Guard接口不用關(guān)心執(zhí)行調(diào)度和狀態(tài)維護(hù)。
@Component public class OrderGuard implements Guard<OrderStatus, OrderStatusChangeEvent> { @Override public boolean evaluate(StateContext<OrderStatus, OrderStatusChangeEvent> context) { Order order = context.getMessage().getHeaders().get("order", Order.class); // ...自定義業(yè)務(wù)檢查邏輯 return true; } }
3.6 Action---存放業(yè)務(wù)執(zhí)行邏輯
業(yè)務(wù)開發(fā)只用關(guān)心實(shí)現(xiàn)這個(gè)Action接口不用關(guān)心執(zhí)行調(diào)度和狀態(tài)維護(hù)
@Slf4j @Component public class OrderAction implements Action<OrderStatus,OrderStatusChangeEvent> { @Resource private OrderMapper orderMapper; @Override public void execute(StateContext<OrderStatus, OrderStatusChangeEvent> stateContext) { try { Order order = stateContext.getMessage().getHeaders().get("order", Order.class); State<OrderStatus, OrderStatusChangeEvent> target = stateContext.getTarget(); order.setStatus(target.getId().getKey()); log.info("orderAction:{}", order); // 自定義業(yè)務(wù)執(zhí)行邏輯 } catch (Exception e) { log.error("訂單狀態(tài)機(jī)執(zhí)行異常", e); stateContext.getStateMachine().setStateMachineError(new RuntimeException("自定義錯(cuò)誤")); throw e; } } }
3.7 核心控制類
對(duì)并發(fā)有要求的使用工廠方法,每個(gè)訂單都單獨(dú)使用一個(gè)狀態(tài)機(jī)表達(dá)
@Service("orderService") @Slf4j public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService { @Autowired private StateMachineFactory<OrderStatus, OrderStatusChangeEvent> stateMachineFactory; @Resource private StateMachinePersister<OrderStatus, OrderStatusChangeEvent, String> stateMachineRedisPersister; @Resource private OrderMapper orderMapper; @Autowired private RedisUtil redisUtil; @Autowired OrderStateListener orderStateListener; /** * 創(chuàng)建訂單 * * @param order * @return */ @DS("oracle-xxx") @Transactional(propagation = Propagation.REQUIRES_NEW) public Order create(Order order) { order.setStatus(OrderStatus.WAIT_PAYMENT.getKey()); orderMapper.insert(order); return order; } /** * 對(duì)訂單進(jìn)行支付 * * @param id * @return */ @DS("oracle-xxx") @Transactional(propagation = Propagation.REQUIRES_NEW) public boolean pay(Long id) { Order order = orderMapper.selectById(id); log.info("線程名稱:{},嘗試支付,訂單號(hào):{}" ,Thread.currentThread().getName() , id); return autoSendEvent(OrderStatusChangeEvent.PAYED, order); } /** * 發(fā)送訂單狀態(tài)轉(zhuǎn)換事件 * synchronized修飾保證這個(gè)方法是線程安全的 * * @param changeEvent * @param order * @return */ private boolean sendEvent(OrderStatusChangeEvent changeEvent, Order order) { boolean result = false; StateMachine<OrderStatus, OrderStatusChangeEvent> orderStateMachine = null; try { // 工廠方法生成狀態(tài)機(jī) orderStateMachine = stateMachineFactory.getStateMachine(order.getId()); orderStateMachine.addStateListener(orderStateListener); //啟動(dòng)狀態(tài)機(jī) orderStateMachine.start(); //嘗試恢復(fù)狀態(tài)機(jī)狀態(tài) stateMachineRedisPersister.restore(orderStateMachine, String.valueOf(order.getId())); Message message = MessageBuilder.withPayload(changeEvent).setHeader("order", order).build(); result = orderStateMachine.sendEvent(message); boolean hasError = orderStateMachine.hasStateMachineError(); //持久化狀態(tài)機(jī)狀態(tài) if(hasError){ return !hasError; } stateMachineRedisPersister.persist(orderStateMachine, String.valueOf(order.getId())); // 已結(jié)束的流程 給key加上過期時(shí)間 if (order.getStatus() == OrderStatus.FINISH.getKey()) { redisUtil.set(order.getId(), order.getStatus(), 60 * 60 * 24, TimeUnit.SECONDS); } } catch (Exception e) { log.error("訂單操作失敗:{}", e); } finally{ if (orderStateMachine != null) { // 釋放狀態(tài)機(jī) orderStateMachine.stop(); } } return result; } private boolean autoSendEvent(OrderStatusChangeEvent changeEvent, Order order) { boolean b = true; while (b && changeEvent != null) { b = sendEvent(changeEvent, order); changeEvent = changeEvent.getNextEvent(); } return b; }
3.8 監(jiān)聽器
用來執(zhí)行一些不影響流程主流程的 日志、通知之類的任務(wù),可異步
@Component @Slf4j public class OrderStateListener extends StateMachineListenerAdapter<OrderStatus, OrderStatusChangeEvent> { private StateMachine<OrderStatus, OrderStatusChangeEvent> stateMachine; private Message<OrderStatusChangeEvent> message; @Override public void stateContext(StateContext<OrderStatus, OrderStatusChangeEvent> context) { StateMachine<OrderStatus, OrderStatusChangeEvent> stateMachine = context.getStateMachine(); this.stateMachine = stateMachine; // 獲取當(dāng)前事件 Message<OrderStatusChangeEvent> message = context.getMessage(); this.message = message; // 獲取狀態(tài)機(jī) ID String machineId = stateMachine.getId(); // 應(yīng)用場景:訂單狀態(tài)變更記錄 if (context.getStage() == StateContext.Stage.STATE_CHANGED) { OrderStatus oldStatus = context.getSource().getId(); OrderStatus newStatus = context.getTarget().getId(); log.info("訂單 {} 狀態(tài)變更: {} → {}", machineId, oldStatus, newStatus); } if(context.getException()!=null){ log.error("狀態(tài)機(jī)異常: {}",context.getException().getMessage()); } } @DS("oracle-xxx") @Override public void stateChanged(State<OrderStatus, OrderStatusChangeEvent> from, State<OrderStatus, OrderStatusChangeEvent> to) { // 狀態(tài)變更時(shí)觸發(fā)(如 UNPAID → PAID) System.out.println("狀態(tài)變更: " + from.getId() + " → " + to.getId()); Order order = message.getHeaders().get("order", Order.class); order.setStatus(to.getId().getKey()); } @Override public void stateEntered(State<OrderStatus, OrderStatusChangeEvent> state) { // 進(jìn)入新狀態(tài)時(shí)觸發(fā) System.out.println("進(jìn)入狀態(tài): " + state.getId()); } @Override @Async public void eventNotAccepted(Message<OrderStatusChangeEvent> message) { // 事件被拒絕時(shí)觸發(fā)(如非法狀態(tài)轉(zhuǎn)換) System.err.println("事件拒絕開始: " + message.getPayload()); Order order = message.getHeaders().get("order", Order.class); try { Thread.sleep(60000); } catch (InterruptedException e) { throw new RuntimeException(e); } System.err.println("事件拒絕結(jié)束: " + message.getPayload()); } @Override @Async public void stateMachineError(StateMachine<OrderStatus, OrderStatusChangeEvent> stateMachine, Exception exception) { // 只能建ring狀態(tài)機(jī)本身在運(yùn)行中發(fā)生的異常,不能處理action拋出的異常 // 可以在此進(jìn)行重試、告警等操作 log.error("錯(cuò)誤處理: {}", exception.getMessage()); } @Override public void stateExited(State<OrderStatus, OrderStatusChangeEvent> state) { // 狀態(tài)機(jī)退出時(shí)觸發(fā) log.info("狀態(tài)退出: {}", state.getId()); } }
3.9 持久化
這里直接用的redis持久化,可自己定義成其他持久化方式
@Configuration @Slf4j public class Persist<E, S> { @Resource private RedisConnectionFactory redisConnectionFactory; /** * 持久化到redis中,在分布式系統(tǒng)中使用 * * @return */ @Bean(name = "stateMachineRedisPersister") public RedisStateMachinePersister<E, S> getRedisPersister() { RedisStateMachineContextRepository<E, S> repository = new RedisStateMachineContextRepository<>(redisConnectionFactory); RepositoryStateMachinePersist p = new RepositoryStateMachinePersist<>(repository); return new RedisStateMachinePersister<>(p); } }
4. 總結(jié)
1. 狀態(tài)轉(zhuǎn)移通過配置類維護(hù),可讀性高,新增狀態(tài)時(shí)維護(hù)方便。
2. 狀態(tài)之間轉(zhuǎn)移通過由配置強(qiáng)制指定,不容易出現(xiàn)邏輯錯(cuò)亂。
3. 加鎖方便,可以在統(tǒng)一的事件觸發(fā)入口加redis分布式鎖。
4. 業(yè)務(wù)邏輯處理存放在Guard?、Action?、監(jiān)聽器中,各司其職結(jié)構(gòu)清晰,代碼邏輯解耦。
到此這篇關(guān)于Spring狀態(tài)機(jī) Statemachine使用小結(jié)的文章就介紹到這了,更多相關(guān)Spring狀態(tài)機(jī) Statemachine內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
關(guān)于SpringBoot創(chuàng)建存儲(chǔ)令牌的媒介類和過濾器的問題
這篇文章主要介紹了SpringBoot創(chuàng)建存儲(chǔ)令牌的媒介類和過濾器的問題,需要在配置文件中,添加JWT需要的密匙,過期時(shí)間和緩存過期時(shí)間,具體實(shí)例代碼參考下本文2021-09-09在Java中PDF與TIFF格式互轉(zhuǎn)的實(shí)現(xiàn)方案
在IT行業(yè)中,處理圖像文件和文檔格式轉(zhuǎn)換是一項(xiàng)常見的任務(wù),本項(xiàng)目主要關(guān)注的是PDF與TIFF格式互轉(zhuǎn),TIFF是一種廣泛用于掃描儀和打印機(jī)的圖像文件格式,尤其適合存儲(chǔ)多頁的圖像,如傳真或文檔掃描,本文給大家介紹了Java中PDF與TIFF格式互轉(zhuǎn)的實(shí)現(xiàn)方案,需要的朋友可以參考下2025-06-06spring?mvc?AOP切面方法未執(zhí)行的一種情況的分析和處理過程
這篇文章主要介紹了spring?mvc?AOP切面方法未執(zhí)行的一種情況的分析和處理,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05Java利用策略模式實(shí)現(xiàn)條件判斷,告別if else
策略模式定義了一系列算法,并且將每個(gè)算法封裝起來,使得他們可以相互替換,而且算法的變化不會(huì)影響使用算法的客戶端。本文將通過案例講解如何利用Java的策略模式實(shí)現(xiàn)條件判斷,告別if----else條件硬編碼,需要的可以參考一下2022-02-02