文章目錄
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
(完)