原文地址:Spring Boot 揭祕與實戰(七) 實用技術篇 - StateMachine 狀態機機制
部落格地址:blog.720ui.com/
Spring StateMachine 讓狀態機結構更加層次化,可以幫助開發者簡化狀態機的開發過程。之前,我們使用二維陣列實現狀態機機制,現在,我們來用 Spring StateMachine 進行改造。
環境依賴
修改 POM 檔案,新增 spring-statemachine-core 依賴。
<dependency>
<groupId>org.springframework.statemachine</groupId>
<artifactId>spring-statemachine-core</artifactId>
<version>1.2.0.RELEASE</version>
</dependency>複製程式碼
狀態和事件
現在,我以使用者註冊為案例,來講解狀態和事件之間的狀態機機制。
狀態列舉
註冊有哪些狀態呢,我們來想想,應該有4個狀態:未連線、已連線、註冊中、已註冊。
public enum RegStatusEnum {
// 未連線
UNCONNECTED,
// 已連線
CONNECTED,
// 註冊中
REGISTERING,
// 已註冊
REGISTERED;
}複製程式碼
事件列舉
相對應的,存在幾個核心事件:連線、註冊、註冊成功、註冊失敗、登出。
public enum RegEventEnum {
// 連線
CONNECT,
// 註冊
REGISTER,
// 註冊成功
REGISTER_SUCCESS,
// 註冊失敗
REGISTER_FAILED,
// 登出
UN_REGISTER;
}複製程式碼
狀態機配置
@Configuration
@EnableStateMachine
public class StateMachineConfig extends EnumStateMachineConfigurerAdapter<RegStatusEnum, RegEventEnum> {
}複製程式碼
@EnableStateMachine註解,標識啟用 Spring StateMachine 狀態機功能。
初始化狀態機狀態
我們需要初始化狀態機的狀態。
@Override
public void configure(StateMachineStateConfigurer<RegStatusEnum, RegEventEnum> states) throws Exception {
states.withStates()
// 定義初始狀態
.initial(RegStatusEnum.UNCONNECTED)
// 定義狀態機狀態
.states(EnumSet.allOf(RegStatusEnum.class));
}複製程式碼
其中,initial(RegStatusEnum.UNCONNECTED) 定義了初始狀態是未連線狀態。states(EnumSet.allOf(RegStatusEnum.class)) 定義了定義狀態機中存在的所有狀態。
初始化狀態遷移事件
我們需要初始化當前狀態機有哪些狀態事件。
@Override
public void configure(StateMachineTransitionConfigurer<RegStatusEnum, RegEventEnum> transitions)
throws Exception {
transitions
// 1.連線事件
// 未連線 -> 已連線
.withExternal()
.source(RegStatusEnum.UNCONNECTED)
.target(RegStatusEnum.CONNECTED)
.event(RegEventEnum.CONNECT)
.and()
// 2.註冊事件
// 已連線 -> 註冊中
.withExternal()
.source(RegStatusEnum.CONNECTED)
.target(RegStatusEnum.REGISTERING)
.event(RegEventEnum.REGISTER)
.and()
// 3.註冊成功事件
// 註冊中 -> 已註冊
.withExternal()
.source(RegStatusEnum.REGISTERING)
.target(RegStatusEnum.REGISTERED)
.event(RegEventEnum.REGISTER_SUCCESS)
.and()
// 5.登出事件
// 已連線 -> 未連線
.withExternal()
.source(RegStatusEnum.CONNECTED)
.target(RegStatusEnum.UNCONNECTED)
.event(RegEventEnum.UN_REGISTER)
.and()
// 註冊中 -> 未連線
.withExternal()
.source(RegStatusEnum.REGISTERING)
.target(RegStatusEnum.UNCONNECTED)
.event(RegEventEnum.UN_REGISTER)
.and()
// 已註冊 -> 未連線
.withExternal()
.source(RegStatusEnum.REGISTERED)
.target(RegStatusEnum.UNCONNECTED)
.event(RegEventEnum.UN_REGISTER)
;
}複製程式碼
這裡,我以連線事件為案例,其中 source 指定原始狀態,target 指定目標狀態,event 指定觸發事件。
因此,下面的狀態就很好理解了,即當發生連線事件時,從未連線狀態變更為已連線狀態。
// 未連線 -> 已連線
.withExternal()
.source(RegStatusEnum.UNCONNECTED)
.target(RegStatusEnum.CONNECTED)
.event(RegEventEnum.CONNECT)複製程式碼
狀態監聽器
Spring StateMachine 提供了註解配置實現方式,所有 StateMachineListener 介面中定義的事件都能通過註解的方式來進行配置實現。
@WithStateMachine
public class StateMachineEventConfig {
@OnTransition(source = "UNCONNECTED", target = "CONNECTED")
public void connect() {
System.out.println("///////////////////");
System.out.println("連線事件, 未連線 -> 已連線");
System.out.println("///////////////////");
}
@OnTransition(source = "CONNECTED", target = "REGISTERING")
public void register() {
System.out.println("///////////////////");
System.out.println("註冊事件, 已連線 -> 註冊中");
System.out.println("///////////////////");
}
@OnTransition(source = "REGISTERING", target = "REGISTERED")
public void registerSuccess() {
System.out.println("///////////////////");
System.out.println("註冊成功事件, 註冊中 -> 已註冊");
System.out.println("///////////////////");
}
@OnTransition(source = "REGISTERED", target = "UNCONNECTED")
public void unRegister() {
System.out.println("///////////////////");
System.out.println("登出事件, 已註冊 -> 未連線");
System.out.println("///////////////////");
}
}複製程式碼
這裡,我仍然以連線事件為案例,@OnTransition 中 source 指定原始狀態,target 指定目標狀態,當事件觸發時將會被監聽到從而呼叫 connect() 方法。
總結
Spring StateMachine 讓狀態機結構更加層次化,可以幫助開發者簡化狀態機的開發過程。
我們來回顧下幾個核心步驟
- 定義狀態列舉。
- 定義事件列舉。
- 定義狀態機配置,設定初始狀態,以及狀態與事件之間的關係。
- 定義狀態監聽器,當狀態變更時,觸發方法。
原始碼
相關示例完整程式碼: springboot-action
(完)
更多精彩文章,盡在「服務端思維」微信公眾號!