Spring Statemachine 是 Spring 框架下的一個模塊,用于簡化狀態機的創建和管理,它允許開發者使用 Spring 的特性(如依賴注入、AOP 等)來構建復雜的狀態機應用。以下是關于 Spring Statemachine 的詳細介紹:
主要特性
- 豐富的狀態機模型支持:支持多種狀態機模型,如簡單狀態機、層次狀態機和并行狀態機。層次狀態機允許狀態嵌套,并行狀態機可以同時處理多個獨立的狀態流。
- 靈活的配置方式:可以使用 Java 配置、XML 配置或注解來定義狀態機的狀態、轉移、事件等。
- 與 Spring 生態集成:無縫集成 Spring 框架的其他模塊,如 Spring Boot、Spring MVC 等,方便構建企業級應用。
- 事件驅動機制:通過事件觸發狀態轉移,易于與外部系統進行交互。
- 狀態監聽器:允許開發者在狀態轉移前后執行自定義邏輯,如日志記錄、業務處理等。
快速入門
1. 添加依賴
如果你使用 Maven,在?pom.xml
?中添加以下依賴:
xml
<dependency><groupId>org.springframework.statemachine</groupId><artifactId>spring-statemachine-core</artifactId><version>3.2.1</version>
</dependency>
2. 定義狀態和事件
java
// 定義狀態枚舉
public enum States {STATE1, STATE2, STATE3
}// 定義事件枚舉
public enum Events {EVENT1, EVENT2
}
3. 配置狀態機
使用 Java 配置方式:
java
import org.springframework.context.annotation.Bean;
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;import java.util.EnumSet;@Configuration
@EnableStateMachine
public class StateMachineConfig extends EnumStateMachineConfigurerAdapter<States, Events> {@Overridepublic void configure(StateMachineConfigurationConfigurer<States, Events> config) throws Exception {config.withConfiguration().autoStartup(true);}@Overridepublic void configure(StateMachineStateConfigurer<States, Events> states) throws Exception {states.withStates().initial(States.STATE1).states(EnumSet.allOf(States.class));}@Overridepublic void configure(StateMachineTransitionConfigurer<States, Events> transitions) throws Exception {transitions.withExternal().source(States.STATE1).target(States.STATE2).event(Events.EVENT1).and().withExternal().source(States.STATE2).target(States.STATE3).event(Events.EVENT2);}
}
4. 使用狀態機
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.statemachine.StateMachine;@SpringBootApplication
public class StateMachineApp implements CommandLineRunner {@Autowiredprivate StateMachine<States, Events> stateMachine;public static void main(String[] args) {SpringApplication.run(StateMachineApp.class, args);}@Overridepublic void run(String... args) throws Exception {stateMachine.start();System.out.println("當前狀態: " + stateMachine.getState().getId());stateMachine.sendEvent(Events.EVENT1);System.out.println("觸發 EVENT1 后狀態: " + stateMachine.getState().getId());stateMachine.sendEvent(Events.EVENT2);System.out.println("觸發 EVENT2 后狀態: " + stateMachine.getState().getId());}
}
代碼解釋
- 定義狀態和事件:使用枚舉類型定義狀態機的狀態和事件,方便管理和使用。
@Configuration
:表明這是一個配置類。@EnableStateMachine
:啟用狀態機功能。- 配置狀態機:
configure(StateMachineConfigurationConfigurer)
:配置狀態機的基本屬性,如自動啟動。configure(StateMachineStateConfigurer)
:定義狀態機的狀態,指定初始狀態STATE_A
和所有可能的狀態。configure(StateMachineTransitionConfigurer)
:定義狀態之間的轉移規則,包括源狀態、目標狀態和觸發事件。EVENT_1
?事件觸發從?STATE_A
?到?STATE_B
?的轉移,EVENT_2
?事件觸發從?STATE_B
?到?STATE_C
?的轉移。
- 使用狀態機:在?
CommandLineRunner
?中注入狀態機實例,啟動狀態機并發送事件,觀察狀態的變化。
應用場景
- 工作流管理:如訂單處理流程、審批流程等,通過狀態機可以清晰地管理每個步驟的狀態轉換。
- 游戲開發:管理游戲角色的狀態,如站立、行走、攻擊等,根據用戶輸入和游戲邏輯進行狀態轉移。
- 設備控制:控制物聯網設備的狀態,如智能家電的開關、模式切換等。
Spring Statemachine 提供了強大而靈活的功能,幫助開發者更高效地實現狀態機應用。
-----------------------------------------------DEMO------------------------------------------------------------------
以下為你提供一個較為完整的 Spring Boot 集成 Spring Statemachine 的示例代碼,這個示例模擬了一個簡單的訂單狀態機,包含待支付、已支付、已發貨、已完成幾種狀態。
1. 創建 Spring Boot 項目并添加依賴
可以使用 Spring Initializr 或者 IDE 自帶的 Spring Boot 項目創建功能,添加以下依賴:
xml
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.statemachine</groupId><artifactId>spring-statemachine-core</artifactId><version>3.2.1</version></dependency>
</dependencies>
2. 定義狀態和事件枚舉
java
// 定義訂單狀態枚舉
public enum OrderState {PENDING_PAYMENT, PAID, SHIPPED, COMPLETED
}// 定義訂單事件枚舉
public enum OrderEvent {PAY, SHIP, DELIVER
}
3. 配置狀態機
java
import org.springframework.context.annotation.Bean;
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;import java.util.EnumSet;@Configuration
@EnableStateMachine
public class OrderStateMachineConfig extends EnumStateMachineConfigurerAdapter<OrderState, OrderEvent> {@Overridepublic void configure(StateMachineConfigurationConfigurer<OrderState, OrderEvent> config) throws Exception {config.withConfiguration().autoStartup(true);}@Overridepublic void configure(StateMachineStateConfigurer<OrderState, OrderEvent> states) throws Exception {states.withStates().initial(OrderState.PENDING_PAYMENT).states(EnumSet.allOf(OrderState.class));}@Overridepublic void configure(StateMachineTransitionConfigurer<OrderState, OrderEvent> transitions) throws Exception {transitions.withExternal().source(OrderState.PENDING_PAYMENT).target(OrderState.PAID).event(OrderEvent.PAY).and().withExternal().source(OrderState.PAID).target(OrderState.SHIPPED).event(OrderEvent.SHIP).and().withExternal().source(OrderState.SHIPPED).target(OrderState.COMPLETED).event(OrderEvent.DELIVER);}
}
4. 創建狀態機服務類
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.statemachine.StateMachine;
import org.springframework.stereotype.Service;@Service
public class OrderStateMachineService {@Autowiredprivate StateMachine<OrderState, OrderEvent> stateMachine;public boolean sendEvent(OrderEvent event) {return stateMachine.sendEvent(event);}public OrderState getCurrentState() {return stateMachine.getState().getId();}
}
5. 創建控制器類
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;@RestController
@RequestMapping("/order")
public class OrderController {@Autowiredprivate OrderStateMachineService orderStateMachineService;@GetMapping("/currentState")public OrderState getCurrentState() {return orderStateMachineService.getCurrentState();}@PostMapping("/sendEvent/{event}")public String sendEvent(@PathVariable OrderEvent event) {boolean result = orderStateMachineService.sendEvent(event);if (result) {return "事件發送成功,當前狀態: " + orderStateMachineService.getCurrentState();} else {return "事件發送失敗,當前狀態: " + orderStateMachineService.getCurrentState();}}
}
6. 啟動 Spring Boot 應用
java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class SpringStatemachineDemoApplication {public static void main(String[] args) {SpringApplication.run(SpringStatemachineDemoApplication.class, args);}
}
代碼解釋
- 狀態和事件枚舉:
OrderState
?定義了訂單可能的狀態,OrderEvent
?定義了觸發狀態轉移的事件。 - 狀態機配置:
OrderStateMachineConfig
?類使用?@EnableStateMachine
?注解啟用狀態機,通過重寫三個配置方法分別配置狀態機的基本屬性、狀態和轉移規則。 - 狀態機服務類:
OrderStateMachineService
?封裝了狀態機的操作,包括發送事件和獲取當前狀態。 - 控制器類:
OrderController
?提供了兩個接口,一個用于獲取當前訂單狀態,另一個用于發送事件觸發狀態轉移。 - 啟動類:
SpringStatemachineDemoApplication
?是 Spring Boot 應用的啟動類。
測試
啟動應用后,可以使用以下方式進行測試:
- 獲取當前狀態:訪問?
http://localhost:8080/order/currentState
- 發送事件:訪問?
http://localhost:8080/order/sendEvent/PAY
?觸發支付事件,根據狀態機配置,訂單狀態將從?PENDING_PAYMENT
?轉移到?PAID
。
------------------------------------------------------------------------------------------------
transitions.withExternal().source(OrderState.PENDING_PAYMENT).target(OrderState.PAID).event(OrderEvent.PAY).and().withExternal().source(OrderState.PAID).target(OrderState.SHIPPED).event(OrderEvent.SHIP).and().withExternal().source(OrderState.SHIPPED).target(OrderState.COMPLETED).event(OrderEvent.DELIVER);
這段代碼的主要作用是定義狀態機中不同狀態之間的轉移規則。在一個訂單處理的狀態機場景里,它明確了訂單在不同狀態(如待支付、已支付、已發貨、已完成)之間如何根據特定事件(如支付、發貨、交付)進行轉換。
代碼結構分析
整體上,這段代碼通過多次調用?withExternal()
?方法來定義多個外部狀態轉移規則,每個狀態轉移規則由?source
(源狀態)、target
(目標狀態)和?event
(觸發事件)三個關鍵部分組成,不同的狀態轉移規則之間通過?.and()
?方法進行連接。
各部分詳細解釋
withExternal()
withExternal()
?方法用于定義外部狀態轉移,即狀態機從一個狀態轉移到另一個不同的狀態。與之相對的還有內部轉移(withInternal()
),內部轉移不會改變狀態機的狀態,只是在當前狀態下執行一些操作。
.source(OrderState.PENDING_PAYMENT).target(OrderState.PAID).event(OrderEvent.PAY)
source(OrderState.PENDING_PAYMENT)
:指定狀態轉移的起始狀態,這里是?OrderState.PENDING_PAYMENT
,表示訂單處于待支付狀態。target(OrderState.PAID)
:指定狀態轉移的目標狀態,即?OrderState.PAID
,意味著訂單在滿足條件后將轉移到已支付狀態。event(OrderEvent.PAY)
:指定觸發狀態轉移的事件,當?OrderEvent.PAY
?事件發生時,狀態機將從待支付狀態轉移到已支付狀態。
.and()
and()
?方法用于連接多個狀態轉移規則,它表示一個規則定義的結束和下一個規則定義的開始,使得可以在同一個配置方法中定義多個不同的狀態轉移規則。
后續規則
java
.withExternal().source(OrderState.PAID).target(OrderState.SHIPPED).event(OrderEvent.SHIP)
.and()
.withExternal().source(OrderState.SHIPPED).target(OrderState.COMPLETED).event(OrderEvent.DELIVER);
這部分代碼定義了另外兩個狀態轉移規則:
- 當?
OrderEvent.SHIP
?事件發生時,訂單從已支付狀態(OrderState.PAID
)轉移到已發貨狀態(OrderState.SHIPPED
)。- 當?
OrderEvent.DELIVER
?事件發生時,訂單從已發貨狀態(OrderState.SHIPPED
)轉移到已完成狀態(OrderState.COMPLETED
)。
示例代碼擴展
如果需要在狀態轉移時執行一些額外的操作,比如記錄日志或者更新數據庫,可以使用?action()
?方法。以下是一個擴展后的示例:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.statemachine.action.Action;
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;import java.util.EnumSet;@Configuration
@EnableStateMachine
public class OrderStateMachineConfig extends EnumStateMachineConfigurerAdapter<OrderState, OrderEvent> {@Overridepublic void configure(StateMachineTransitionConfigurer<OrderState, OrderEvent> transitions) throws Exception {Action<OrderState, OrderEvent> payAction = context -> {System.out.println("訂單已支付,更新訂單狀態為已支付");// 這里可以添加更新數據庫等操作};Action<OrderState, OrderEvent> shipAction = context -> {System.out.println("訂單已發貨,更新訂單狀態為已發貨");// 這里可以添加更新數據庫等操作};Action<OrderState, OrderEvent> deliverAction = context -> {System.out.println("訂單已完成,更新訂單狀態為已完成");// 這里可以添加更新數據庫等操作};transitions.withExternal().source(OrderState.PENDING_PAYMENT).target(OrderState.PAID).event(OrderEvent.PAY).action(payAction).and().withExternal().source(OrderState.PAID).target(OrderState.SHIPPED).event(OrderEvent.SHIP).action(shipAction).and().withExternal().source(OrderState.SHIPPED).target(OrderState.COMPLETED).event(OrderEvent.DELIVER).action(deliverAction);}// 其他配置方法保持不變
}
在這個擴展示例中,通過定義?Action
?對象,并在狀態轉移規則中使用?action()
?方法,在狀態轉移時執行了相應的操作。