Loading [MathJax]/jax/input/TeX/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Spring Boot 2.x实战之StateMachine

Spring Boot 2.x实战之StateMachine

作者头像
阿杜
发布于 2019-11-12 11:40:26
发布于 2019-11-12 11:40:26
1.5K00
代码可运行
举报
文章被收录于专栏:阿杜的世界阿杜的世界
运行总次数:0
代码可运行

Spring StateMachine是一个状态机框架,在Spring框架项目中,开发者可以通过简单的配置就能获得一个业务状态机,而不需要自己去管理状态机的定义、初始化等过程。今天这篇文章,我们通过一个案例学习下Spring StateMachine框架的用法。

案例介绍

假设在一个业务系统中,有这样一个对象,它有三个状态:草稿、待发布、发布完成,针对这三个状态的业务动作也比较简单,分别是:上线、发布、回滚。该业务状态机如下图所示。

img

实战

接下来,基于上面的业务状态机进行Spring StateMachine的演示。

  • 创建一个基础的Spring Boot工程,在主pom文件中加入Spring StateMachine的依赖:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.1.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
  </parent>
  <groupId>online.javaadu</groupId>
  <artifactId>statemachinedemo</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>statemachinedemo</name>
  <description>Demo project for Spring Boot</description>

  <properties>
    <java.version>1.8</java.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
    </dependency>

    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <optional>true</optional>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
      <exclusions>
        <exclusion>
          <groupId>org.junit.vintage</groupId>
          <artifactId>junit-vintage-engine</artifactId>
        </exclusion>
      </exclusions>
    </dependency>

    <!--加入spring statemachine的依赖-->
        <dependency>
        <groupId>org.springframework.statemachine</groupId>
        <artifactId>spring-statemachine-core</artifactId>
        <version>2.1.3.RELEASE</version>
      </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>

</project>

定义状态枚举和事件枚举,代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
* 状态枚举
**/
public enum States {
    DRAFT,
    PUBLISH_TODO,
    PUBLISH_DONE,
}

/**
* 事件枚举
**/
public enum Events {
    ONLINE,
    PUBLISH,
    ROLLBACK
}
  • 完成状态机的配置,包括:(1)状态机的初始状态和所有状态;(2)状态之间的转移规则
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Configuration
@EnableStateMachine
public class StateMachineConfig extends EnumStateMachineConfigurerAdapter<States, Events> {

    @Override
    public void configure(StateMachineStateConfigurer<States, Events> states) throws Exception {
        states.withStates().initial(States.DRAFT).states(EnumSet.allOf(States.class));
    }

    @Override
    public void configure(StateMachineTransitionConfigurer<States, Events> transitions) throws Exception {
        transitions.withExternal()
            .source(States.DRAFT).target(States.PUBLISH_TODO)
            .event(Events.ONLINE)
            .and()
            .withExternal()
            .source(States.PUBLISH_TODO).target(States.PUBLISH_DONE)
            .event(Events.PUBLISH)
            .and()
            .withExternal()
            .source(States.PUBLISH_DONE).target(States.DRAFT)
            .event(Events.ROLLBACK);
    }
}
  • 定义一个测试业务对象,状态机的状态转移都会反映到该业务对象的状态变更上
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@WithStateMachine
@Data
@Slf4j
public class BizBean {

    /**
     * @see States
     */
    private String status = States.DRAFT.name();

    @OnTransition(target = "PUBLISH_TODO")
    public void online() {
        log.info("操作上线,待发布. target status:{}", States.PUBLISH_TODO.name());
        setStatus(States.PUBLISH_TODO.name());
    }

    @OnTransition(target = "PUBLISH_DONE")
    public void publish() {
        log.info("操作发布,发布完成. target status:{}", States.PUBLISH_DONE.name());
        setStatus(States.PUBLISH_DONE.name());
    }

    @OnTransition(target = "DRAFT")
    public void rollback() {
        log.info("操作回滚,回到草稿状态. target status:{}", States.DRAFT.name());
        setStatus(States.DRAFT.name());
    }

}
  • 编写测试用例,这里我们使用CommandLineRunner接口代替,定义了一个StartupRunner,在该类的run方法中启动状态机、发送不同的事件,通过日志验证状态机的流转过程。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class StartupRunner implements CommandLineRunner {

    @Resource
    StateMachine<States, Events> stateMachine;

    @Override
    public void run(String... args) throws Exception {
        stateMachine.start();
        stateMachine.sendEvent(Events.ONLINE);
        stateMachine.sendEvent(Events.PUBLISH);
        stateMachine.sendEvent(Events.ROLLBACK);
    }
}

在运行上述程序后,我们可以在控制台中获得如下输出,我们执行了三个操作:上线、发布、回滚,在下图中也确实看到了对应的日志。不过我还发现有一个意料之外的地方——在启动状态机的时候,还打印出了一个日志——“操作回滚,回到草稿状态. target status:DRAFT”,这里应该是状态机设置初始状态的时候触发的。

分析

如上面的实战过程所示,使用Spring StateMachine的步骤如下:

  1. 定义状态枚举和事件枚举
  2. 定义状态机的初始状态和所有状态
  3. 定义状态之间的转移规则
  4. 在业务对象中使用状态机,编写响应状态变化的监听器方法

为了将状态变更的操作都统一管理起来,我们会考虑在项目中引入状态机,这样其他的业务模块就和状态转移模块隔离开来了,其他业务模块也不会纠结于当前的状态是什么,应该做什么操作。在应用状态机实现业务需求时,关键是业务状态的分析,只要状态机设计得没问题,具体的实现可以选择用Spring StateMachine,也可以自己去实现一个状态机。

使用Spring StateMachine的好处在于自己无需关心状态机的实现细节,只需要关心业务有什么状态、它们之间的转移规则是什么、每个状态转移后真正要进行的业务操作。

本文完整实例参见:https://github.com/duqicauc/Spring-Boot-2.x-In-Action/tree/master/statemachinedemo

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Javascript抽象语法树下篇(实践篇)
目前babel不管是从生态上还是文档上比esprima要好很多,因此推荐大家使用babel工具,本文示例也使用babel来做演示。
WecTeam
2019/12/16
1.8K0
Javascript抽象语法树下篇(实践篇)
CodeMod 代码重构/升级必知必会
CodeMod(Code Modification) 的应用场景非常多,我在过去几年就使用 ‘codemod‘ 升级过多个项目,节省了大量的人力成本:
_sx_
2023/10/23
1K0
CodeMod 代码重构/升级必知必会
卧槽!VSCode 上竟然也能画流程图了???
此前,我们也曾在 GitHubDaily 公众号上分享过多篇文章,向大家推荐了不少 VSCode 上比较实用(或沙雕)的插件:
GitHubDaily
2020/05/16
3.7K0
AST in TypeScript 实践
  最近参与了一个 Node 项目脚手架的开发工作,为了提高编码效率,导师提议写一个 VSCode 的插件,功能上大体有点像 snippets 代码段,但比 snippets 优秀的地方是,插件还能实现以下两大功能:
JoviCheng
2019/06/30
5.6K1
AST in TypeScript 实践
逆向进阶,利用 AST 技术还原 JavaScript 混淆代码
AST(Abstract Syntax Tree),中文抽象语法树,简称语法树(Syntax Tree),是源代码的抽象语法结构的树状表现形式,树上的每个节点都表示源代码中的一种结构。语法树不是某一种编程语言独有的,JavaScript、Python、Java、Golang 等几乎所有编程语言都有语法树。
K哥爬虫
2022/04/28
6.1K3
逆向进阶,利用 AST 技术还原 JavaScript 混淆代码
【Webpack】632- 了不起的 Webpack 构建流程学习
Webpack 是前端很火的打包工具,它本质上是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 Webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有模块打包成一个或多个 bundle。
pingan8787
2020/06/24
1.1K0
写一个同事见了会打你的 Prettier 插件
前端的编译工具都是从源码到源码的转换,所以都是 parse、transform、generate 这三步:
神说要有光zxg
2023/02/01
1.1K0
借助AST ,手写一个解决运行环境差异的loader
最近遇到了一个很特殊的需求,业务代码打包后需要运行在两个不同的环境中,而两个环境中的属性有非常多的差异,我想在打包阶段来处理这些差异,所以就需要自定义一个loader来处理设计到的相关文件
Jou
2023/03/23
5020
借助AST ,手写一个解决运行环境差异的loader
基于drawio构建流程图编辑器
drawio是一款非常强大的开源在线的流程图编辑器,支持绘制各种形式的图表,提供了Web端与客户端支持,同时也支持多种资源类型的导出。
WindRunnerMax
2023/07/10
1.6K0
卧槽!VSCode 上竟然也能画流程图了???
看到大家对 VSCode 如此痴迷,那今天小 G 就再给大家推荐一款实用的 VSCode 插件:VSCode Drawio。
五分钟学算法
2020/11/17
1.8K0
卧槽!VSCode 上竟然也能画流程图了???
JS代码之混淆
抽象语法树(Abstract Syntax Tree),简称 AST,初识 AST 是在一门网页逆向的课程,该课程讲述了 js 代码中混淆与还原的对抗,而所使用的技术便是 AST,通过 AST 能很轻松的将 js 源代码混淆成难以辨别的代码。同样的,也可以通过 AST 将其混淆的代码 还原成执行逻辑相对正常的代码。
愧怍
2022/12/27
22.6K0
JS代码之混淆
Babel的另类实践 - 重构古董代码
在最近的工作中,接手了一个古老的项目,其中的 JS 代码是一整坨的面条代码,约 3000 行的代码全写在一个文件里,维护起来着实让人头疼。 想不通为啥之前维护项目的同学能够忍受这么难以维护的代码……既然现在这个锅被我拿下了,怎么着也不能容忍如此丑陋的代码继续存在着,必须把它优化一下。 横竖看了半天,由于逻辑都揉在了一个文件里,看都看得眼花缭乱,当务之急便是把它进行模块化拆分,把这一大坨面条状代码拆分成一个个模块并抽离成文件,这样才方便后续的持续优化。 一、结构分析 说干就干,既然要拆分成模块,首先就要
腾讯VTeam技术团队
2020/10/14
9140
手把手带你走进Babel的编译世界
谈及 Babel,必然离不开 AST。有关 AST 这个知识点其实是很重要的,但由于涉及到代码编译阶段,大多情况都是由各个框架内置相关处理,所以作为开发(使用)者本身,往往会忽视这个过程。希望通过这篇文章,带各位同学走进 AST,借助 AST 发挥更多的想象力。
PHP开发工程师
2022/03/24
5860
手把手带你走进Babel的编译世界
【编译技术】:AST——基础的基础
在计算机科学中,抽象语法树(abstract syntax tree 或者缩写为 *AST*),或者语法树(*syntax tree*),是源代码的抽象语法结构的树状表现形式,这里特指编程语言的源代码。树上的每个节点都表示源代码中的一种结构。之所以说语法是「抽象」的,是因为这里的语法并不会表示出真实语法中出现的每个细节。
WEBJ2EE
2020/10/26
2.1K0
【编译技术】:AST——基础的基础
编译原理工程实践—05使用babel操作AST实现代码转换
babel 是一个 JavaScript 编译器,使用 babel 可以随心所欲地转化和操作 AST,实现对代码的分析、优化、变更等。可以在 https://esprima.org/demo/parse.html 体验转换查看 js 代码的词法、语法和AST。
CS逍遥剑仙
2025/05/12
770
[AI学习笔记]代码生成实战:DeepSeek AST解析深度解析与应用
DeepSeek项目于2022年启动,专注于通过抽象语法树(AST)解析提升代码生成质量。核心团队由编译器专家、深度学习工程师和软件架构师组成。
数字扫地僧
2025/04/03
3140
[AI学习笔记]代码生成实战:DeepSeek AST解析深度解析与应用
使用GPT和Draw.io生成工作流程图
在现代工作环境中,工作流程图是一种常见的工具,用于可视化和传达复杂的流程和步骤。传统上,绘制工作流程图需要手动绘制或使用专业的绘图工具,这可能会面临一些难点和挑战。以下是一些需要考虑的因素:
DevOps云学堂
2024/07/04
2.3K0
使用GPT和Draw.io生成工作流程图
如何构建一个在线绘图工具:Feakin 是如何设计与构建的?
高中,读过几本 3D 图形编程相关的书。怎么说呢,自那以后,图形学相关的东西,都不在我的兴趣范围里了。直到最近,我重新燃起了一点兴趣: 架构治理工具 ArchGuard 依赖于「图即代码」,用于生成架构图,以更好的进行架构治理。 年初,开源的知识管理工具 Quake 中,需要支持「概念构建系统」这样一个理念。 需要管理多种不同的图形格式。 当然了,作为一个 Firefox 浏览器的忠实用户,Firefox 在 Feakin 里自然是支持最好的。开始之前,欢迎尝试在线 Demo:https://online.
Phodal
2022/08/29
1.7K0
如何构建一个在线绘图工具:Feakin 是如何设计与构建的?
AST 介绍
AST (Abstract Syntax Tree(抽象语法树)) 是源代码语法结构的一种抽象表示。它以树状的形式表现编程语言的语法结构。它由一堆节点(Node)组成,每个节点都表示源代码中的一种结构。不同结构用类型来区分,常见的类型有: Identifier(标识符),BinaryExpression(二元表达式),VariableDeclaration(变量定义),FunctionDeclaration(函数定义)等。
前端GoGoGo
2020/04/02
1.9K0
AST 介绍
Babel快速指南
结构上属于编译器,由于输入JS源码,输出也是JS源码(所谓source to source),所以也称为transpiler(转译器)
ayqy贾杰
2019/06/12
1.1K0
推荐阅读
相关推荐
Javascript抽象语法树下篇(实践篇)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验