spring statemachine-單個狀態(tài)機

1.代碼例子

先來一個StateMachineConfig,它的主要作用就告訴狀態(tài)機的初始狀態(tài)應該啥樣掌动,然后把整個狀態(tài)流程都用代碼配置出來四啰。@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<OrderStates, OrderEvents> {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Override
    public void configure(StateMachineStateConfigurer<OrderStates, OrderEvents> states) throws Exception {
        states.withStates().initial(OrderStates.UNPAID).states(EnumSet.allOf(OrderStates.class));
    }

    @Override
    public void configure(StateMachineTransitionConfigurer<OrderStates, OrderEvents> 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());

    /**
     * 當前狀態(tài)UNPAID
     */
    @OnTransition(target = "UNPAID")
    public void create() {
        logger.info("---訂單創(chuàng)建眷射,待支付---");
    }

    /**
     * UNPAID->WAITING_FOR_RECEIVE 執(zhí)行的動作
     */
    @OnTransition(source = "UNPAID", target = "WAITING_FOR_RECEIVE")
    public void pay() {
        logger.info("---用戶完成支付匙赞,待收貨---");
    }

    /**
     * WAITING_FOR_RECEIVE->DONE 執(zhí)行的動作
     */
    @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 {

        // 創(chuàng)建流程
        orderSingleMachine.start();

        // 觸發(fā)PAY事件
        orderSingleMachine.sendEvent(OrderEvents.PAY);

        // 觸發(fā)RECEIVE事件
        orderSingleMachine.sendEvent(OrderEvents.RECEIVE);

        // 獲取最終狀態(tài)
        System.out.println("最終狀態(tài):" + 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 : ---訂單創(chuàng)建坐榆,待支付---
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 : ---用戶已收貨鱼鸠,訂單完成---
最終狀態(tài):DONE

2猛拴、我們來講一下這個例子

image

1)描述上圖
?? 其實OrderStates表達的就是這張圖的狀態(tài)(state)羹铅,OrderEvents表達的就是這張圖狀態(tài)間的事件(event)蚀狰,我們的業(yè)務代碼就是要塞到事件(event)里面去,處理在狀態(tài)轉換間要處理的事情职员,比如P從UNPAID到WAITING_FOR_RECEIVE中間的PAY事件(event)麻蹋,我們就可能需要調用支付接口,或者判斷用戶的會員等級是不是有支付優(yōu)惠啥的焊切。但state和event是指描述這個流程的三個點和兩條線扮授,具體的流程指向要怎么描述呢,就輪到StateMachineConfig出場了专肪。StateMachineConfig繼承了EnumStateMachineConfigurerAdapter類刹勃,表明身份,我就是來配置狀態(tài)機的初始狀態(tài)嚎尤,并描繪一下狀態(tài)流程的全過程荔仁。

2)塞入業(yè)務代碼
?? 現(xiàn)在我們知道狀態(tài)(state)和事件(event)了,也描繪了這個狀態(tài)機的流程和初始狀態(tài)是什么樣了,然后我們要做什么乏梁,當然是開始把業(yè)務代碼塞到事件(event)里面去次洼,于是OrderSingleEventConfig登場了。OrderSingleEventConfig里面的create遇骑,pay和receive方法就是描繪事件觸發(fā)時需要做什么卖毁,但這三個方法名其實是可以自己隨便寫的(當然最好和event名一樣,避免一年后自己看代碼時罵當年自己為什么那么蠢落萎,至于別人閱讀你的代碼嘛......業(yè)務代碼誰要看你的亥啦,別人會重寫的),真正和上面描繪的狀態(tài)流程對應的是@OnTransition模暗,source代表現(xiàn)在的狀態(tài)禁悠,target代表目標狀態(tài),很容易懂的兑宇。

3)運行狀態(tài)機

?? 我們在需要的地方引入一個狀態(tài)機

    @Autowired
    private StateMachine orderSingleMachine;

然后運行就可以啦

        // 創(chuàng)建流程
        orderSingleMachine.start();
        // 觸發(fā)PAY事件
        orderSingleMachine.sendEvent(OrderEvents.PAY);
        // 觸發(fā)RECEIVE事件
        orderSingleMachine.sendEvent(OrderEvents.RECEIVE);
        // 獲取最終狀態(tài)
        System.out.println("最終狀態(tài):" + orderSingleMachine.getState().getId());

至此碍侦,狀態(tài)機就跑起來了,謝謝大家隶糕,本教程到此結束瓷产,有疑問我也沒辦法。

3枚驻、我是還不想結束的番外篇

?? 現(xiàn)實的世界那有這么簡單濒旦,這樣的一個例子在企業(yè)級的開發(fā)中毫無用處,大家可以想想這個例子有啥用再登,其實什么問題都沒有解決尔邓。大家能想到有哪些問題呢,我用我淺薄的開發(fā)經(jīng)驗一眼就看到了以下幾個問題:
1)整個項目只有一種狀態(tài)機流程锉矢,我要是想在一個項目里面又有訂單流程梯嗽,又有公文審批流程怎么辦,難道和老板講我的狀態(tài)機demo告訴我沽损,狀態(tài)機的流程只能選一個灯节?
2)整個項目只有一個狀態(tài)機流程,你沒有看眼花绵估,這是另外一個問題炎疆。哪怕是只有一種流程,比如訂單流程国裳,其實也是有很多訂單的流程在同時跑形入,而不是像這個例子,全部訂單共用一個流程缝左,一個訂單到WAITING_FOR_RECEIVE狀態(tài)了亿遂,其他訂單就不能是UNPAY狀態(tài)了螟蒸。
3)參數(shù)問題,我們做項目崩掘,不是為了看日志打出“---訂單創(chuàng)建七嫌,待支付---”給我們玩的,而是要處理具體業(yè)務的苞慢,拿訂單流程來說吧诵原,訂單號怎么傳,狀態(tài)改變時間怎么回數(shù)據(jù)庫挽放,等等問題其實都需要解決绍赛。
4)存儲問題,狀態(tài)機如果有多個辑畦,需要的時候從哪去吗蚌,暫時不需要了放哪去,這都是問題纯出,所以存儲狀態(tài)機也是需要解決的蚯妇。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市暂筝,隨后出現(xiàn)的幾起案子箩言,更是在濱河造成了極大的恐慌,老刑警劉巖焕襟,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件陨收,死亡現(xiàn)場離奇詭異,居然都是意外死亡鸵赖,警方通過查閱死者的電腦和手機务漩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來它褪,“玉大人饵骨,你說我怎么就攤上這事×惺辏” “怎么了宏悦?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵镐确,是天一觀的道長包吝。 經(jīng)常有香客問我,道長源葫,這世上最難降的妖魔是什么诗越? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮息堂,結果婚禮上嚷狞,老公的妹妹穿的比我還像新娘块促。我一直安慰自己,他們只是感情好床未,可當我...
    茶點故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布竭翠。 她就那樣靜靜地躺著,像睡著了一般薇搁。 火紅的嫁衣襯著肌膚如雪斋扰。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天啃洋,我揣著相機與錄音传货,去河邊找鬼。 笑死宏娄,一個胖子當著我的面吹牛问裕,可吹牛的內容都是我干的。 我是一名探鬼主播孵坚,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼粮宛,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了卖宠?” 一聲冷哼從身側響起窟勃,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎逗堵,沒想到半個月后秉氧,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡蜒秤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年汁咏,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片作媚。...
    茶點故事閱讀 40,127評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡攘滩,死狀恐怖,靈堂內的尸體忽然破棺而出纸泡,到底是詐尸還是另有隱情漂问,我是刑警寧澤,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布女揭,位于F島的核電站蚤假,受9級特大地震影響,放射性物質發(fā)生泄漏吧兔。R本人自食惡果不足惜磷仰,卻給世界環(huán)境...
    茶點故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望境蔼。 院中可真熱鬧灶平,春花似錦伺通、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至瞒爬,卻和暖如春笑诅,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背疮鲫。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工吆你, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人俊犯。 一個月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓妇多,卻偏偏與公主長得像,于是被迫代替她去往敵國和親燕侠。 傳聞我的和親對象是個殘疾皇子者祖,可洞房花燭夜當晚...
    茶點故事閱讀 45,066評論 2 355