Spring StateMachine 让状态机结构更加层次化,可以帮助开发者简化状态机的开发过程。

之前,我们使用二维数组实现状态机机制,现在,我们来用 Spring StateMachine 进行改造。

环境依赖

修改 POM 文件,添加 spring-statemachine-core 依赖。

  1. <dependency>
  2. <groupId>org.springframework.statemachine</groupId>
  3. <artifactId>spring-statemachine-core</artifactId>
  4. <version>1.2.0.RELEASE</version>
  5. </dependency>

状态和事件

现在,我以用户注册为案例,来讲解状态和事件之间的状态机机制。

状态枚举

注册有哪些状态呢,我们来想想,应该有4个状态:未连接、已连接、注册中、已注册。

  1. public enum RegStatusEnum {
  2.  
  3. // 未连接
  4. UNCONNECTED,
  5. // 已连接
  6. CONNECTED,
  7. // 注册中
  8. REGISTERING,
  9. // 已注册
  10. REGISTERED;
  11.  
  12. }

事件枚举

相对应的,存在几个核心事件:连接、注册、注册成功、注册失败、注销。

  1. public enum RegEventEnum {
  2. // 连接
  3. CONNECT,
  4. // 注册
  5. REGISTER,
  6. // 注册成功
  7. REGISTER_SUCCESS,
  8. // 注册失败
  9. REGISTER_FAILED,
  10. // 注销
  11. UN_REGISTER;
  12. }

状态机配置

  1. @Configuration
  2. @EnableStateMachine
  3. public class StateMachineConfig extends EnumStateMachineConfigurerAdapter<RegStatusEnum, RegEventEnum> {
  4.  
  5. }

@EnableStateMachine注解,标识启用 Spring StateMachine 状态机功能。

初始化状态机状态

我们需要初始化状态机的状态。

  1. @Override
  2. public void configure(StateMachineStateConfigurer<RegStatusEnum, RegEventEnum> states) throws Exception {
  3. states.withStates()
  4. // 定义初始状态
  5. .initial(RegStatusEnum.UNCONNECTED)
  6. // 定义状态机状态
  7. .states(EnumSet.allOf(RegStatusEnum.class));
  8. }

其中,initial(RegStatusEnum.UNCONNECTED) 定义了初始状态是未连接状态。states(EnumSet.allOf(RegStatusEnum.class)) 定义了定义状态机中存在的所有状态。

初始化状态迁移事件

我们需要初始化当前状态机有哪些状态事件。

  1. @Override
  2. public void configure(StateMachineTransitionConfigurer<RegStatusEnum, RegEventEnum> transitions)
  3. throws Exception {
  4. transitions
  5. // 1.连接事件
  6. // 未连接 -> 已连接
  7. .withExternal()
  8. .source(RegStatusEnum.UNCONNECTED)
  9. .target(RegStatusEnum.CONNECTED)
  10. .event(RegEventEnum.CONNECT)
  11. .and()
  12.  
  13. // 2.注册事件
  14. // 已连接 -> 注册中
  15. .withExternal()
  16. .source(RegStatusEnum.CONNECTED)
  17. .target(RegStatusEnum.REGISTERING)
  18. .event(RegEventEnum.REGISTER)
  19. .and()
  20.  
  21. // 3.注册成功事件
  22. // 注册中 -> 已注册
  23. .withExternal()
  24. .source(RegStatusEnum.REGISTERING)
  25. .target(RegStatusEnum.REGISTERED)
  26. .event(RegEventEnum.REGISTER_SUCCESS)
  27. .and()
  28.  
  29. // 5.注销事件
  30. // 已连接 -> 未连接
  31. .withExternal()
  32. .source(RegStatusEnum.CONNECTED)
  33. .target(RegStatusEnum.UNCONNECTED)
  34. .event(RegEventEnum.UN_REGISTER)
  35. .and()
  36. // 注册中 -> 未连接
  37. .withExternal()
  38. .source(RegStatusEnum.REGISTERING)
  39. .target(RegStatusEnum.UNCONNECTED)
  40. .event(RegEventEnum.UN_REGISTER)
  41. .and()
  42. // 已注册 -> 未连接
  43. .withExternal()
  44. .source(RegStatusEnum.REGISTERED)
  45. .target(RegStatusEnum.UNCONNECTED)
  46. .event(RegEventEnum.UN_REGISTER)
  47. ;
  48. }

这里,我以连接事件为案例,其中 source 指定原始状态,target 指定目标状态,event 指定触发事件。

因此,下面的状态就很好理解了,即当发生连接事件时,从未连接状态变更为已连接状态。

  1. // 未连接 -> 已连接
  2. .withExternal()
  3. .source(RegStatusEnum.UNCONNECTED)
  4. .target(RegStatusEnum.CONNECTED)
  5. .event(RegEventEnum.CONNECT)

状态监听器

Spring StateMachine 提供了注解配置实现方式,所有 StateMachineListener 接口中定义的事件都能通过注解的方式来进行配置实现。

  1. @WithStateMachine
  2. public class StateMachineEventConfig {
  3.  
  4. @OnTransition(source = "UNCONNECTED", target = "CONNECTED")
  5. public void connect() {
  6. System.out.println("///////////////////");
  7. System.out.println("连接事件, 未连接 -> 已连接");
  8. System.out.println("///////////////////");
  9. }
  10.  
  11. @OnTransition(source = "CONNECTED", target = "REGISTERING")
  12. public void register() {
  13. System.out.println("///////////////////");
  14. System.out.println("注册事件, 已连接 -> 注册中");
  15. System.out.println("///////////////////");
  16. }
  17.  
  18. @OnTransition(source = "REGISTERING", target = "REGISTERED")
  19. public void registerSuccess() {
  20. System.out.println("///////////////////");
  21. System.out.println("注册成功事件, 注册中 -> 已注册");
  22. System.out.println("///////////////////");
  23. }
  24.  
  25. @OnTransition(source = "REGISTERED", target = "UNCONNECTED")
  26. public void unRegister() {
  27. System.out.println("///////////////////");
  28. System.out.println("注销事件, 已注册 -> 未连接");
  29. System.out.println("///////////////////");
  30. }
  31. }

这里,我仍然以连接事件为案例,@OnTransition 中 source 指定原始状态,target 指定目标状态,当事件触发时将会被监听到从而调用 connect() 方法。

总结

Spring StateMachine 让状态机结构更加层次化,可以帮助开发者简化状态机的开发过程。

我们来回顾下几个核心步骤

  • 定义状态枚举。
  • 定义事件枚举。
  • 定义状态机配置,设置初始状态,以及状态与事件之间的关系。
  • 定义状态监听器,当状态变更时,触发方法。

源代码

相关示例完整代码: springboot-action

(完)

相关文章: