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. UNCONNECTED,
  4. // 已连接
  5. CONNECTED,
  6. // 注册中
  7. REGISTERING,
  8. // 已注册
  9. REGISTERED;
  10. }

事件枚举

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

  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. }

@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. // 2.注册事件
  13. // 已连接 -> 注册中
  14. .withExternal()
  15. .source(RegStatusEnum.CONNECTED)
  16. .target(RegStatusEnum.REGISTERING)
  17. .event(RegEventEnum.REGISTER)
  18. .and()
  19. // 3.注册成功事件
  20. // 注册中 -> 已注册
  21. .withExternal()
  22. .source(RegStatusEnum.REGISTERING)
  23. .target(RegStatusEnum.REGISTERED)
  24. .event(RegEventEnum.REGISTER_SUCCESS)
  25. .and()
  26. // 5.注销事件
  27. // 已连接 -> 未连接
  28. .withExternal()
  29. .source(RegStatusEnum.CONNECTED)
  30. .target(RegStatusEnum.UNCONNECTED)
  31. .event(RegEventEnum.UN_REGISTER)
  32. .and()
  33. // 注册中 -> 未连接
  34. .withExternal()
  35. .source(RegStatusEnum.REGISTERING)
  36. .target(RegStatusEnum.UNCONNECTED)
  37. .event(RegEventEnum.UN_REGISTER)
  38. .and()
  39. // 已注册 -> 未连接
  40. .withExternal()
  41. .source(RegStatusEnum.REGISTERED)
  42. .target(RegStatusEnum.UNCONNECTED)
  43. .event(RegEventEnum.UN_REGISTER)
  44. ;
  45. }

这里,我以连接事件为案例,其中 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. @OnTransition(source = "UNCONNECTED", target = "CONNECTED")
  4. public void connect() {
  5. System.out.println("///////////////////");
  6. System.out.println("连接事件, 未连接 -> 已连接");
  7. System.out.println("///////////////////");
  8. }
  9. @OnTransition(source = "CONNECTED", target = "REGISTERING")
  10. public void register() {
  11. System.out.println("///////////////////");
  12. System.out.println("注册事件, 已连接 -> 注册中");
  13. System.out.println("///////////////////");
  14. }
  15. @OnTransition(source = "REGISTERING", target = "REGISTERED")
  16. public void registerSuccess() {
  17. System.out.println("///////////////////");
  18. System.out.println("注册成功事件, 注册中 -> 已注册");
  19. System.out.println("///////////////////");
  20. }
  21. @OnTransition(source = "REGISTERED", target = "UNCONNECTED")
  22. public void unRegister() {
  23. System.out.println("///////////////////");
  24. System.out.println("注销事件, 已注册 -> 未连接");
  25. System.out.println("///////////////////");
  26. }
  27. }

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

总结

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

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

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

源代码

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

(完)

如果觉得我的文章对你有帮助,请随意打赏。

Spring Boot 揭秘与实战(七) 实用技术篇 - StateMachine 状态机机制的更多相关文章

  1. Spring Boot 揭秘与实战(七) 实用技术篇 - 异步任务

    文章目录 1. Spring Boot 集成异步任务 2. 单发服务模式 3. 请求应答模式 4. 源代码 Spring 对异步任务具有很好的支持.这篇文章,我们透过 Spring Boot 来讲解下 ...

  2. Spring Boot 揭秘与实战(七) 实用技术篇 - Java Mail 发送邮件

    文章目录 1. Spring Boot 集成 Java Mail 2. 单元测试 3. 源代码 Spring 对 Java Mail 有很好的支持.因此,Spring Boot 也提供了自动配置的支持 ...

  3. Spring Boot 揭秘与实战(七) 实用技术篇 - FreeMarker 模板引擎

    文章目录 1. FreeMaker 代替 JSP 作为页面渲染 2. 生成静态文件 3. 扩展阅读 4. 源代码 Spring Boot 提供了很多模板引擎的支持,例如 FreeMarker.Thym ...

  4. Spring Boot 揭秘与实战 附录 - Spring Boot 公共配置

    Spring Boot 公共配置,配置 application.properties/application.yml 文件中. 摘自:http://docs.spring.io/spring-boot ...

  5. Spring Boot 揭秘与实战 自己实现一个简单的自动配置模块

    文章目录 1. 实战的开端 – Maven搭建 2. 参数的配置 - 属性参数类 3. 真的很简单 - 简单的服务类 4. 自动配置的核心 - 自动配置类 5. spring.factories 不要 ...

  6. Spring Boot 揭秘与实战 源码分析 - 工作原理剖析

    文章目录 1. EnableAutoConfiguration 帮助我们做了什么 2. 配置参数类 – FreeMarkerProperties 3. 自动配置类 – FreeMarkerAutoCo ...

  7. Spring Boot 揭秘与实战 源码分析 - 开箱即用,内藏玄机

    文章目录 1. 开箱即用,内藏玄机 2. 总结 3. 源代码 Spring Boot提供了很多”开箱即用“的依赖模块,那么,Spring Boot 如何巧妙的做到开箱即用,自动配置的呢? 开箱即用,内 ...

  8. Spring Boot 揭秘与实战(九) 应用监控篇 - 自定义监控端点

    文章目录 1. 继承 AbstractEndpoint 抽象类 2. 创建端点配置类 3. 运行 4. 源代码 Spring Boot 提供的端点不能满足我们的业务需求时,我们可以自定义一个端点. 本 ...

  9. Spring Boot 揭秘与实战(九) 应用监控篇 - HTTP 健康监控

    文章目录 1. 内置 HealthIndicator 监控检测 2. 自定义 HealthIndicator 监控检测 3. 源代码 Health 信息是从 ApplicationContext 中所 ...

随机推荐

  1. 牛客练习赛32-D-MST+tarjin割边

    链接:https://ac.nowcoder.com/acm/contest/272/D来源:牛客网 题目描述 小p和他的朋友约定好去游乐场游玩,但是他们到了游乐场后却互相找不到对方了. 游乐场可以看 ...

  2. GitHub学习三-远程版本库更新与提交

    1.远程版本库更新 一般来说,将本地与远程相关联之后,首先将数据从远程更新下来再上传比较好. 输入 git pull origin master 如果新建版本库的话勾选了初始化包含readme.md, ...

  3. SQL 查询语句

    4.2 单表查询 4.2.1 列名(表名)的别名(as 可以不加) 给列名取别名既可以加 as 也可以不加. (2008 - Sage.lower(Sdept)等可计算但无列名,需要指定列名) 原列名 ...

  4. 【LeetCode】数组移除元素

    链表等复杂数据结构用多了,简单的数组操作也不能遗忘! 1. 给定一个有序数组,移除所有重复元素并返回新的数组长度,不能分配额外数组的内存空间. e.g. 给定输入的数组 = [1,1,2],函数应当返 ...

  5. [luogu P3384] [模板]树链剖分

    [luogu P3384] [模板]树链剖分 题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点 ...

  6. 安天透过北美DDoS事件解读IoT设备安全——Mirai的主要感染对象是linux物联网设备,包括:路由器、网络摄像头、DVR设备,入侵主要通过telnet端口进行流行密码档暴力破解,或默认密码登陆,下载DDoS功能的bot,运行控制物联网设备

    安天透过北美DDoS事件解读IoT设备安全 安天安全研究与应急处理中心(安天CERT)在北京时间10月22日下午启动高等级分析流程,针对美国东海岸DNS服务商Dyn遭遇DDoS攻击事件进行了跟进分析. ...

  7. Greys Java在线问题诊断工具

    摘要: 线上系统为何经常出错?数据库为何屡遭黑手?业务调用为何频频失败?连环异常堆栈案,究竟是那次调用所为? 数百台服务器意外雪崩背后又隐藏着什么?是软件的扭曲还是硬件的沦丧? 走进科学带你了解Gre ...

  8. 在springboot中验证表单信息(六)

    构建工程 创建一个springboot工程,由于用到了 web .thymeleaf.validator.el,引入相应的起步依赖和依赖,代码清单如下: 1 2 3 4 5 6 7 8 9 10 11 ...

  9. SpringMVC中JSP页面显示为源码

    @RequestMapping(value = "login") public ModelAndView login(ModelAndView mav) throws Except ...

  10. API设计和命名

    1.前言 对于前端开发而言,肯定会和API打交道,大家也都会想过怎么设计自己的API.优秀的 API 之于代码,就如良好内涵对于每个人.好的 API 不但利于使用者理解,开发时也会事半功倍,后期维护更 ...