1. 程式人生 > >Spring Boot 2.x實戰之StateMachine

Spring Boot 2.x實戰之StateMachine

本文首發於個人網站:Spring Boot 2.x實戰之StateMachine

Spring StateMachine是一個狀態機框架,在Spring框架專案中,開發者可以通過簡單的配置就能獲得一個業務狀態機,而不需要自己去管理狀態機的定義、初始化等過程。今天這篇文章,我們通過一個案例學習下Spring StateMachine框架的用法。

案例介紹

假設在一個業務系統中,有這樣一個物件,它有三個狀態:草稿、待發布、釋出完成,針對這三個狀態的業務動作也比較簡單,分別是:上線、釋出、回滾。該業務狀態機如下圖所示。

實戰

接下來,基於上面的業務狀態機進行Spring StateMachine的演示。

  • 建立一個基礎的Spring Boot工程,在主pom檔案中加入Spring StateMachine的依賴:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.1.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
  </parent>
  <groupId>online.javaadu</groupId>
  <artifactId>statemachinedemo</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>statemachinedemo</name>
  <description>Demo project for Spring Boot</description>

  <properties>
    <java.version>1.8</java.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
    </dependency>

    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <optional>true</optional>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
      <exclusions>
        <exclusion>
          <groupId>org.junit.vintage</groupId>
          <artifactId>junit-vintage-engine</artifactId>
        </exclusion>
      </exclusions>
    </dependency>

    <!--加入spring statemachine的依賴-->
        <dependency>
        <groupId>org.springframework.statemachine</groupId>
        <artifactId>spring-statemachine-core</artifactId>
        <version>2.1.3.RELEASE</version>
      </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>

</project>

定義狀態列舉和事件列舉,程式碼如下:

/**
* 狀態列舉
**/
public enum States {
    DRAFT,
    PUBLISH_TODO,
    PUBLISH_DONE,
}

/**
* 事件列舉
**/
public enum Events {
    ONLINE,
    PUBLISH,
    ROLLBACK
}
  • 完成狀態機的配置,包括:(1)狀態機的初始狀態和所有狀態;(2)狀態之間的轉移規則
@Configuration
@EnableStateMachine
public class StateMachineConfig extends EnumStateMachineConfigurerAdapter<States, Events> {

    @Override
    public void configure(StateMachineStateConfigurer<States, Events> states) throws Exception {
        states.withStates().initial(States.DRAFT).states(EnumSet.allOf(States.class));
    }

    @Override
    public void configure(StateMachineTransitionConfigurer<States, Events> transitions) throws Exception {
        transitions.withExternal()
            .source(States.DRAFT).target(States.PUBLISH_TODO)
            .event(Events.ONLINE)
            .and()
            .withExternal()
            .source(States.PUBLISH_TODO).target(States.PUBLISH_DONE)
            .event(Events.PUBLISH)
            .and()
            .withExternal()
            .source(States.PUBLISH_DONE).target(States.DRAFT)
            .event(Events.ROLLBACK);
    }
}
  • 定義一個測試業務物件,狀態機的狀態轉移都會反映到該業務物件的狀態變更上
@WithStateMachine
@Data
@Slf4j
public class BizBean {

    /**
     * @see States
     */
    private String status = States.DRAFT.name();

    @OnTransition(target = "PUBLISH_TODO")
    public void online() {
        log.info("操作上線,待發布. target status:{}", States.PUBLISH_TODO.name());
        setStatus(States.PUBLISH_TODO.name());
    }

    @OnTransition(target = "PUBLISH_DONE")
    public void publish() {
        log.info("操作釋出,釋出完成. target status:{}", States.PUBLISH_DONE.name());
        setStatus(States.PUBLISH_DONE.name());
    }

    @OnTransition(target = "DRAFT")
    public void rollback() {
        log.info("操作回滾,回到草稿狀態. target status:{}", States.DRAFT.name());
        setStatus(States.DRAFT.name());
    }

}
  • 編寫測試用例,這裡我們使用CommandLineRunner介面代替,定義了一個StartupRunner,在該類的run方法中啟動狀態機、傳送不同的事件,通過日誌驗證狀態機的流轉過程。
public class StartupRunner implements CommandLineRunner {

    @Resource
    StateMachine<States, Events> stateMachine;

    @Override
    public void run(String... args) throws Exception {
        stateMachine.start();
        stateMachine.sendEvent(Events.ONLINE);
        stateMachine.sendEvent(Events.PUBLISH);
        stateMachine.sendEvent(Events.ROLLBACK);
    }
}

在執行上述程式後,我們可以在控制檯中獲得如下輸出,我們執行了三個操作:上線、釋出、回滾,在下圖中也確實看到了對應的日誌。不過我還發現有一個意料之外的地方——在啟動狀態機的時候,還打印出了一個日誌——“操作回滾,回到草稿狀態. target status:DRAFT”,這裡應該是狀態機設定初始狀態的時候觸發的。

分析

如上面的實戰過程所示,使用Spring StateMachine的步驟如下:

  1. 定義狀態列舉和事件列舉
  2. 定義狀態機的初始狀態和所有狀態
  3. 定義狀態之間的轉移規則
  4. 在業務物件中使用狀態機,編寫響應狀態變化的監聽器方法

為了將狀態變更的操作都統一管理起來,我們會考慮在專案中引入狀態機,這樣其他的業務模組就和狀態轉移模組隔離開來了,其他業務模組也不會糾結於當前的狀態是什麼,應該做什麼操作。在應用狀態機實現業務需求時,關鍵是業務狀態的分析,只要狀態機設計得沒問題,具體的實現可以選擇用Spring StateMachine,也可以自己去實現一個狀態機。

使用Spring StateMachine的好處在於自己無需關心狀態機的實現細節,只需要關心業務有什麼狀態、它們之間的轉移規則是什麼、每個狀態轉移後真正要進行的業務操作。

本文完整例項參見:https://github.com/duqicauc/Spring-Boot-2.x-In-Action/tree/master/statemachinedemo

參考資料

  1. http://blog.didispace.com/spring-statemachine/
  2. https://projects.spring.io/spring-statemachine/#quick-start

本號專注於後端技術、JVM問題排查和優化、Java面試題、個人成長和自我管理等主題,為讀者提供一線開發者的工作和成長經驗,期待你能在這裡有所收穫。

相關推薦

Spring Boot 2.x實戰StateMachine

本文首發於個人網站:Spring Boot 2.x實戰之StateMachine Spring StateMachine是一個狀態機框架,在Spring框架專案中,開發者可以通過簡單的配置就能獲得一個業務狀態機,而不需要自己去管理狀態機的定義、初始化等過程。今天這篇文章,我們通過一個案例學習下Spring

[公益課程]Spring Boot 2.x 實戰入門

thymeleaf 數據 沒有 講師 對象關系映射 編程 連接池 作用域 培訓機構 【公益內容】Spring Boot 2.x 實戰入門課程內容 http://edu.51cto.com/course/13147.html 第1章Spring Boot 2.X快速入門45

spring boot 2.X 集成 Elasticsearch 5.x 實戰 增刪改查

springboot2.x Elasticsearch5.x 集成 實戰 增刪改查 其實這種博客網上一大片,為啥還要寫出來這篇博客?網上的例子都是基於elasticsearch2.x版本的,並不是5.x版本,而且還有好多是錯的,拿過來根本不能直接用來測試,還有就是spring-data沒有

Protocol Buffer Java 使用(基於Spring Boot 2.x

開發環境:使用Intellij IDEA + Maven + Spring Boot 2.x + JDK 8 1.在專案的pom.xml檔案下,引入protobuf的Jar開發包依賴;並且可以配置protobuf 的Maven外掛,對編寫的proto檔案編譯成Java檔案。

Redis 客戶端Redisson 配置使用(基於Spring Boot 2.x

開發環境:使用Intellij IDEA + Maven + Spring Boot 2.x + JDK 8 1.在專案的pom.xml檔案下,引入Redis和Redisson在Spring Boot 下的相關Jar包依賴。 <properties>

Redis 客戶端Lettuce配置使用(基於Spring Boot 2.x

開發環境:使用Intellij IDEA + Maven + Spring Boot 2.x + JDK 8 Spring Boot 從 2.0版本開始,將預設的Redis客戶端Jedis替換問Lett

spring boot 2.x 踩坑JPA

上次整合過一次,遇到這個問題沒記下來,現在又遇到了,有必要寫一下 PS:如果我總結的不對,歡迎各位大佬指正~ 環境: spring boot 版本 2.1.0 <!--spring boot 版本--> <parent>

spring boot 2.x 配置redis快取的注意兩點(pool連線池以及CacheManager)

1、在SpringBoot的application.yml配置檔案中配置redis資料庫的相關資訊,這裡改動主要有兩點,其一是時間相關的屬性,如spring.redis.timeout,在1.0中,時間

Spring Boot 2.x(十一):AOP實戰--列印介面日誌

介面日誌有啥用 在我們日常的開發過程中,我們可以通過介面日誌去檢視這個介面的一些詳細資訊。比如客戶端的IP,客戶端的型別,響應

Spring Boot 2.X(十六):應用監控 Spring Boot Actuator 使用及配置

Actuator 簡介 Actuator 是 Spring Boot 提供的對應用系統的自省和監控功能。通過 Actuator,可以使用資料化的指標去度量應用的執行情況,比如檢視伺服器的磁碟、記憶體、CPU等資訊,系統的執行緒、gc、執行狀態等等。 Actuator 通常通過使用 HTTP 和 JMX 來管理

【轉載】Vue 2.x 實戰後臺管理系統開發(二)

null element asc 其他 就會 ans 目錄 asi all 2. 常見需求 01. 父子組件通信 a. 父 -> 子(父組件傳遞數據給子組件) 使用 props,具體查看文檔 - 使用 Prop 傳遞數據(cn.vuejs.org/v2/guide

漲姿勢:Spring Boot 2.x 啟動全過程源碼分析

nlog res ces framework abs rsh 思路 get spec 上篇《Spring Boot 2.x 啟動全過程源碼分析(一)入口類剖析》我們分析了 Spring Boot 入口類 SpringApplication 的源碼,並知道了其構造原理,這篇我

spring boot 2.X上傳文件限制大小

code 限制 1.4 大小 http class and sin span Spring Boot 1.3.x multipart.maxFileSize multipart.maxRequestSize Spring Boot 1.4.x and 1.5.x s

基於spring boot 2.xspring-cloud-admin 實踐

boot enable sco import def 多功能 int @override err spring cloud admin 簡介 Spring Boot Admin 用於監控基於 Spring Boot 的應用,它是在 Spring Boot Actuator

Spring Boot 2.x 小新功能 – Spring Data Web configuration

Spring Boot 2.x 小新功能 – Spring Data Web configuration 摘要: 原創出處 www.bysocket.com 「泥瓦匠BYSocket 」歡迎轉載,保留摘要,謝謝! 不賺錢,是一個創業者的最大恥辱。先賺錢,活得好,再談發展,這才是最重要的

spring-boot-2.0.3redis快取實現,不是你想的那樣哦!

前言   開心一刻     小白問小明:“你前面有一個5米深的坑,裡面沒有水,如果你跳進去後該怎樣出來了?”小明:“躺著出來唄,還能怎麼出來?”小白:“為什麼躺著出來?”小明:“5米深的坑,還沒有水,跳下去不死就很幸運了,殘是肯定會殘的,不躺著出來,那能怎麼出來?”小白:“假設沒死也沒殘呢?”小明:“你當

spring-boot-2.0.3redis快取實現,不是你想的那樣哦

前言  開心一刻     小白問小明:“你前面有一個5米深的坑,裡面沒有水,如果你跳進去後該怎樣出來了?”小明:“躺著出來唄,還能怎麼出來?”小白:“為什麼躺著出來?”小明:“5米深的坑,還沒有水,跳下去不死就很幸運了,殘是肯定會殘的,不躺著出來,那能怎麼出來?”小白:“假設沒死也沒殘呢?”小明:“你當我超

spring boot 2.x + elasticsearch+mybatis-plus

第一步引入 maven <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XML

spring boot 2.x html中引用css和js失效

在application.properties中配置了static的預設路徑 我的static目錄結構是這樣的 index.html中這樣引用css或者js檔案,用到了th標籤 html使用th標籤需要先匯入   以上這樣配置好了之後發現網頁的c

Spring Boot 2.X 微信公眾平臺開發 環境說明

本文隨時更新 ~~~   JDK : 1.8 Spring Boot : 2.0.4 Redis 測試公眾號 資料庫暫時還沒用到 目前的pom.xml <project xmlns="http://maven.apache.org/POM/4.0.0"