摘要:先來一個,它的主要作用就告訴狀態機的初始狀態應該啥樣,然后把整個狀態流程都用代碼配置出來。繼承了類,表明身份,我就是來配置狀態機的初始狀態,并描繪一下狀態流程的全過程。
上一篇說了很多廢話,這一篇就不嘮叨,先跑起來
1、來個spring boot
去start.spring.io新建一個springboot的項目,雖然我對spirngboot也有不少的牢騷,但作為demo的開始,還是一個很好用的腳手架,記得選spring statemachine,為了方便,我還選了web 模塊
點擊generate project 下載到本地,用IDE打開,順便說一句,我用的是java IDE界逼格很低的eclipse,因為我一直用它,還不要錢。
2、跑起來一個廢物例子
在本地打開后我們首先看pom.xml文件,里面和我們相關的有這幾段
2.0.1.RELEASE org.springframework.statemachine spring-statemachine-starter org.springframework.statemachine spring-statemachine-bom ${spring-statemachine.version} pom import
現在就可以在springboot里面用statemachine了,然后我們就開始想辦法跑起來。 先來一個StateMachineConfig,它的主要作用就告訴狀態機的初始狀態應該啥樣,然后把整個狀態流程都用代碼配置出來。@Configuration是springboot的注解,表示這個類負責配置,@EnableStateMachine表示這個配置類是用在spring statemachine上面的。
package com.skyblue.statemachine.config; import java.util.EnumSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Configuration; import org.springframework.statemachine.config.EnableStateMachine; import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter; import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer; import org.springframework.statemachine.config.builders.StateMachineStateConfigurer; import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer; @Configuration @EnableStateMachine public class StateMachineConfig extends EnumStateMachineConfigurerAdapter{ private Logger logger = LoggerFactory.getLogger(getClass()); @Override public void configure(StateMachineStateConfigurer states) throws Exception { states.withStates().initial(OrderStates.UNPAID).states(EnumSet.allOf(OrderStates.class)); } @Override public void configure(StateMachineTransitionConfigurer transitions) throws Exception { transitions.withExternal().source(OrderStates.UNPAID).target(OrderStates.WAITING_FOR_RECEIVE).event(OrderEvents.PAY).and() .withExternal().source(OrderStates.WAITING_FOR_RECEIVE).target(OrderStates.DONE).event(OrderEvents.RECEIVE); } }
它配套需要OrderStates和OrderEvents,代碼如下:
package com.skyblue.statemachine.config; public enum OrderStates { UNPAID, // 待支付 WAITING_FOR_RECEIVE, // 待收貨 DONE // 結束 } package com.skyblue.statemachine.config; public enum OrderEvents { PAY, // 支付 RECEIVE // 收貨 }
還有個OrderSingleEventConfig
package com.skyblue.statemachine.config; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.messaging.Message; import org.springframework.statemachine.annotation.OnTransition; import org.springframework.statemachine.annotation.WithStateMachine; @WithStateMachine(name="orderSingleMachine") public class OrderSingleEventConfig { private Logger logger = LoggerFactory.getLogger(getClass()); /** * 當前狀態UNPAID */ @OnTransition(target = "UNPAID") public void create() { logger.info("---訂單創建,待支付---"); } /** * UNPAID->WAITING_FOR_RECEIVE 執行的動作 */ @OnTransition(source = "UNPAID", target = "WAITING_FOR_RECEIVE") public void pay() { logger.info("---用戶完成支付,待收貨---"); } /** * WAITING_FOR_RECEIVE->DONE 執行的動作 */ @OnTransition(source = "WAITING_FOR_RECEIVE", target = "DONE") public void receive() { logger.info("---用戶已收貨,訂單完成---"); } }
因為我本人不會用單元測試,我用了一個controller來運行,大家見諒
@RestController @RequestMapping("/statemachine") public class StateMachineController { @Autowired private StateMachine orderSingleMachine; @RequestMapping("/testSingleOrderState") public void testSingleOrderState() throws Exception { // 創建流程 orderSingleMachine.start(); // 觸發PAY事件 orderSingleMachine.sendEvent(OrderEvents.PAY); // 觸發RECEIVE事件 orderSingleMachine.sendEvent(OrderEvents.RECEIVE); // 獲取最終狀態 System.out.println("最終狀態:" + orderSingleMachine.getState().getId()); } }
訪問頁面http://localhost:port/statemachine/testSingleOrderState,頁面沒有變化,我們看console的日志
2019-04-25 19:14:11.782 INFO 17020 --- [nio-9991-exec-3] tConfig$$EnhancerBySpringCGLIB$$ab30f59f : ---訂單創建,待支付--- 2019-04-25 19:14:11.787 INFO 17020 --- [nio-9991-exec-3] o.s.s.support.LifecycleObjectSupport : started org.springframework.statemachine.support.DefaultStateMachineExecutor@2648176e 2019-04-25 19:14:11.787 INFO 17020 --- [nio-9991-exec-3] o.s.s.support.LifecycleObjectSupport : started UNPAID DONE WAITING_FOR_RECEIVE / UNPAID / uuid=93e4f752-55bc-40ef-84e4-6c00cf5a4fc5 / id=null 2019-04-25 19:14:11.797 INFO 17020 --- [nio-9991-exec-3] tConfig$$EnhancerBySpringCGLIB$$ab30f59f : ---用戶完成支付,待收貨--- 2019-04-25 19:14:11.800 INFO 17020 --- [nio-9991-exec-3] tConfig$$EnhancerBySpringCGLIB$$ab30f59f : ---用戶已收貨,訂單完成--- 最終狀態:DONE
3、我們來講一下這個例子
1)描述上圖
其實OrderStates表達的就是這張圖的狀態(state),OrderEvents表達的就是這張圖狀態間的事件(event),我們的業務代碼就是要塞到事件(event)里面去,處理在狀態轉換間要處理的事情,比如P從UNPAID到WAITING_FOR_RECEIVE中間的PAY事件(event),我們就可能需要調用支付接口,或者判斷用戶的會員等級是不是有支付優惠啥的。但state和event是指描述這個流程的三個點和兩條線,具體的流程指向要怎么描述呢,就輪到StateMachineConfig出場了。StateMachineConfig繼承了EnumStateMachineConfigurerAdapter類,表明身份,我就是來配置狀態機的初始狀態,并描繪一下狀態流程的全過程。
2)塞入業務代碼
現在我們知道狀態(state)和事件(event)了,也描繪了這個狀態機的流程和初始狀態是什么樣了,然后我們要做什么,當然是開始把業務代碼塞到事件(event)里面去,于是OrderSingleEventConfig登場了。OrderSingleEventConfig里面的create,pay和receive方法就是描繪事件觸發時需要做什么,但這三個方法名其實是可以自己隨便寫的(當然最好和event名一樣,避免一年后自己看代碼時罵當年自己為什么那么蠢,至于別人閱讀你的代碼嘛......業務代碼誰要看你的,別人會重寫的),真正和上面描繪的狀態流程對應的是@OnTransition,source代表現在的狀態,target代表目標狀態,很容易懂的。
3)運行狀態機
我們在需要的地方引入一個狀態機
@Autowired
private StateMachine orderSingleMachine;
然后運行就可以啦
// 創建流程
orderSingleMachine.start(); // 觸發PAY事件 orderSingleMachine.sendEvent(OrderEvents.PAY); // 觸發RECEIVE事件 orderSingleMachine.sendEvent(OrderEvents.RECEIVE); // 獲取最終狀態 System.out.println("最終狀態:" + orderSingleMachine.getState().getId());
至此,狀態機就跑起來了,謝謝大家,本教程到此結束,有疑問我也沒辦法。
4、我是還不想結束的番外篇
現實的世界那有這么簡單,這樣的一個例子在企業級的開發中毫無用處,大家可以想想這個例子有啥用,其實什么問題都沒有解決。大家能想到有哪些問題呢,我用我淺薄的開發經驗一眼就看到了以下幾個問題: 1)整個項目只有一種狀態機流程,我要是想在一個項目里面又有訂單流程,又有公文審批流程怎么辦,難道和老板講我的狀態機demo告訴我,狀態機的流程只能選一個? 2)整個項目只有一個狀態機流程,你沒有看眼花,這是另外一個問題。哪怕是只有一種流程,比如訂單流程,其實也是有很多訂單的流程在同時跑,而不是像這個例子,全部訂單共用一個流程,一個訂單到WAITING_FOR_RECEIVE狀態了,其他訂單就不能是UNPAY狀態了。 3)參數問題,我們做項目,不是為了看日志打出“---訂單創建,待支付---”給我們玩的,而是要處理具體業務的,拿訂單流程來說吧,訂單號怎么傳,狀態改變時間怎么回數據庫,等等問題其實都需要解決。 4)存儲問題,狀態機如果有多個,需要的時候從哪去,暫時不需要了放哪去,這都是問題,所以存儲狀態機也是需要解決的。 下一章教程,我們帶著這四個靈魂問題繼續我們的學習之旅
配套代碼地址
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/74940.html
摘要:讓我們先看下狀態機的概念。下面是狀態機模型中的個要素,即現態條件動作次態。因為訂單和審批公文都有很多的流程,每個流程都會產生狀態的變化,而且流程是這種業務的主軸,其他都是圍繞這個流程和狀態變化來考慮的,所以看起來蠻適合用狀態機來做。 1、背景在我打算學習spring statemachine的時候,我幾乎看過了所有網上的中文教程,基本上都處于淺嘗輒止的階段,有幾篇講的比較深入的,都只是...
摘要:目前為止,我們都是從狀態流程的開始階段創建一個狀態機,然后一路走下去。然后就可以愉快的在里面看怎么用了發送事件持久化恢復狀態機后的狀態為執行完保存后,大家可以自己在客戶端查看以下,是不是有內容保存進去了。 目前為止,我們都是從狀態流程的開始階段創建一個狀態機,然后一路走下去。但在實際業務中,狀態機可能需要在某個環節停留,等待其他業務的觸發,然后再繼續下面的流程。比如訂單,可能在支付環節...
摘要:在實際的企業開發中,不可能所有情況都是從頭到尾的按狀態流程來,會有很多意外,比如歷史數據,故障重啟后的遺留流程,所以這種可以任意調節狀態的才是我們需要的狀態機。 1、偽持久化和中間段的狀態機我們設想一個業務場景,就比如訂單吧,我們一般的設計都會把訂單狀態存到訂單表里面,其他的業務信息也都有表保存,而狀態機的主要作用其實是規范整個訂單業務流程的狀態和事件,所以狀態機要不要保存真的不重要,...
摘要:創建了后,狀態機就可以不只是傳一個,可以組合和數據內容一起發送給狀態機變化的處理類了。到這里為止,狀態機通過對象就和其他的業務代碼做到了數據連接。 在企業開發中,數據在不同的業務間傳輸是最常見的工作,所以雖然我們的主架構是用的狀態機,也就是從流程狀態的角度來看待這個項目,但在具體業務中,每個狀態的轉變中會牽涉到各類業務,這些業務有些需要收到狀態機變化的通知,需要把狀態值傳遞給業務類和業...
摘要:目前為止,多個狀態機和多種狀態機都可以在里面實現了,下一章我們來解決下狀態機和實際業務間的數據傳輸問題,畢竟我們不是為了讓狀態機自個獨自玩耍,和業務數據互通有無才是企業開發的正道。 在上一章的例子中,我們實現了多個狀態機并存執行,不同的訂單有各自的狀態機運行,但只有一種狀態機,這顯然不能滿足實際業務的要求,比如我就遇到了訂單流程和公文審批流程在同一個項目的情況,所以我們這一章講怎么讓多...
閱讀 3063·2023-04-26 02:27
閱讀 2774·2021-11-22 13:54
閱讀 915·2021-11-12 10:36
閱讀 3768·2021-10-09 09:44
閱讀 3188·2021-10-09 09:41
閱讀 1239·2021-09-22 10:02
閱讀 2847·2019-08-30 15:56
閱讀 3112·2019-08-30 11:02