Spring StateMachine使用笔记

2023-11-18

Spring StateMachine使用笔记

配置状态机

状态

  • 分层状态

  • withStates() 配置状态 states状态列表

可以使用多个withStates进行parent分层

  • 配置区域:当相同的分层状态机具有多组状态时,每个都具有初始状态,就产生正交状态,多个独立区域
            .withStates()
                .initial(States2.S1)
                .state(States2.S2)
                .and()
                .withStates()
                    .parent(States2.S2)
                    .initial(States2.S2I)
                    .state(States2.S21)
                    .end(States2.S2F)
                    .and()
                .withStates()
                    .parent(States2.S2)
                    .initial(States2.S3I)
                    .state(States2.S31)
                    .end(States2.S3F);
  • 配置转换:三种不同类型的转换external, internal和local
            .withExternal()
                .source(States.S1).target(States.S2)
                .event(Events.E1)
                .and()
            .withInternal()
                .source(States.S2)
                .event(Events.E2)
                .and()
            .withLocal()
                .source(States.S2).target(States.S3)
                .event(Events.E3);
  • END 标记为结束状态 .end(States.SF)

只需使用end()方法将特定状态标记为结束状态。这可以在每个单独的子机器或区域最多进行一次

  • ?history 历史状态 .history(States3.SH, History.SHALLOW);

可以为每个单独的状态机定义历史状态一次。你需要选择其状态标识,History.SHALLOW或 History.DEEP分别

  • withChoice() 根据警卫结果选择状态

  • ?withJunction() 连接状态

  • ?withFork() 叉状态

  • ?withJoin() 加入状态:需要在两个状态和转换中定义连接才能正常工作

  • ?Configuring Common Settings 配置通用设置

  • withEntry\withExit进入和退出,对进入和退出的过程设置更多的节点

            在状态配置中
            .entry("S2ENTRY")
            .exit("S2EXIT")
            在转换中
            .withEntry()
            .source("S2ENTRY").target("S22")
            .and()
            .withExit()
            .source("S2EXIT").target("S3");
  • withModel() 配置模型:StateMachineModelFactory,通过自定义工厂模型

状态机ID 状态机实例的标志

    .withConfiguration()
        .machineId("mymachine");

状态机工厂EnableStateMachineFactory:工厂的配置通过config,编译限制

工厂的当前限制是所有动作和保护它与创建的状态机相关联将共享相同的实例

构建器模式StateMachineBuilder.builder:此构建器模式可用于在Spring应用程序上下文之外构建完全动态的状态机

目前builder.configureStates(),builder.configureTransitions() 和builder.configureConfiguration()接口方法不能被链接在一起意味着生成器方法需要单独调用。

StateMachine<String, String> buildMachine2() throws Exception {
    Builder<String, String> builder = StateMachineBuilder.builder();
    builder.configureConfiguration()
        .withConfiguration()
            .autoStartup(false)
            .beanFactory(null)
            .taskExecutor(null)
            .taskScheduler(null)
            .listener(null);
    return builder.build();
}

重要的是要了解在从构建器实例化的机器需要使用的常见配置的情况

使用延期事件

            在状态配置中,事件DEPLOY推迟到DEPLOYPREPARE和DEPLOYEXECUTE
            .state("DEPLOYPREPARE", "DEPLOY")
            .state("DEPLOYEXECUTE", "DEPLOY");
            在转换配置中
            .withExternal()
                .source("READY").target("DEPLOYPREPARE")
                .event("DEPLOY")
                .and()
            .withExternal()
                .source("DEPLOYPREPARE").target("DEPLOYEXECUTE")
                .and()
            .withExternal()
                .source("DEPLOYEXECUTE").target("READY");

在上面的状态机中,状态为READY,表示机器已准备好处理将使其进入实际部署的DEPLOY状态的事件。执行部署操作后,机器将返回READY状态。如果计算机正在使用同步执行程序,则在READY状态下发送多个事件 不会造成任何问题,因为事件发送会在事件调用之间阻塞。但是,如果执行程序正在使用线程,则其他事件可能会丢失,因为计算机不再处于可以处理事件的状态。因此推迟一些这些事件允许机器保存这些事件。

使用范围

对状态机中的作用域的支持非常有限,但可以使用普通的spring 注释来启用会话作用域@Scope,前提是@Bean

一旦你将状态机作为范围session,@Controller将它自动装入一个将为每个会话提供新的状态机实例。然后在HttpSession无效时销毁状态机。

在session作用域中使用状态机需要仔细规划,主要是因为它是一个相对较重的组件

Action操作

Action:执行转换之间的动作 可以设置进入状态与退出状态的动作

    .stateEntry(States.S2, action(), errorAction())
    .stateDo(States.S2, action(), errorAction())
    .stateExit(States.S2, action(), errorAction())

可以直接将Action实现为匿名函数,也可以创建自己的实现并将适当的实现定义为bean

Guard防护

Guard防护保护状态转换,Guard接口用于评估方法的StateContext:用于判断是否可以转换

        .withExternal()
        .source(States.S2).target(States.S3)
        .event(Events.E2)
        .guardExpression("extendedState.variables.get('myvar')");

在上面的示例guardExpression中,只需检查扩展状态变量myvar是否为TRUE,是可以使用Spel表达式替代完整的Guard实现

扩展状态

StateContext有一个方法getExtendedState()返回一个接口ExtendedState,该接口提供对扩展状态变量的访问。您可以直接通过状态机或StateContext在从操作或转换回调期间访问变量

    public void execute(StateContext<String, String> context) {
        context.getExtendedState()
            .getVariables().put("mykey", "myvalue");
    }

如果需要获得扩展状态变量的通知:
1、使用StateMachineListener和收听extendedStateChanged(key, value)
2、为其实现Spring Application上下文侦听器 OnExtendedStateChanged

    public class ExtendedStateVariableListener
            extends StateMachineListenerAdapter<String, String> {

        @Override
        public void extendedStateChanged(Object key, Object value) {
            // do something with changed variable
        }
    }

使用StateContext

StateContext是使用状态机时最重要的对象之一,因为它被传递到各种方法和回调中,以提供状态机的当前状态及其可能的状态。如果稍微简化一下,可以将其视为当前状态机阶段的快照,其中传递 StateContext

    作用:
    访问当前Message,Event或者他们 MessageHeaders如果知道的话。
    访问状态机Extended State。
    获得StateMachine自己。
    访问可能的状态机错误。
    Transition如果适用, 访问当前。
    访问状态机可能来自和前往的源和目标状态
    Stage阶段

阶段是stage状态机当前与用户交互的表示。目前的阶段是EVENT_NOT_ACCEPTED,EXTENDED_STATE_CHANGED, STATE_CHANGED,STATE_ENTRY,STATE_EXIT,STATEMACHINE_ERROR, STATEMACHINE_START,STATEMACHINE_STOP,TRANSITION, TRANSITION_START和TRANSITION_END

触发过渡

通过由触发器触发的转换来完成驱动状态机。目前支持的触发器是EventTrigger和 TimerTrigger。

    stateMachine.sendEvent(Events.E1);

    Message<Events> message = MessageBuilder
            .withPayload(Events.E2)
            .setHeader("foo", "bar")
            .build();
    stateMachine.sendEvent(message);

在上面的例子中,我们使用两种不同的方式发送事件。首先,我们使用状态机api方法发送一个类型安全事件 sendEvent(E event)。其次,我们使用 带有自定义事件头的api方法发送包含在Spring消息消息中的sendEvent(Message message)事件。这允许用户使用事件添加任意额外信息,然后当用户正在实施操作时,StateContext可以看到该事件

TimerTrigger定时触发器:当需要在没有任何用户交互的情况下自动触发某些内容时, TimerTrigger非常有用

监听状态机事件

有两个选项,要么监听Spring应用程序上下文事件,要么直接将侦听器附加到状态机
1、应用程序上下文事件类是OnTransitionStartEvent, OnTransitionEvent,OnTransitionEndEvent,OnStateExitEvent, OnStateEntryEvent,OnStateChangedEvent,OnStateMachineStart和 OnStateMachineStop以及扩展基本事件类StateMachineEvent的其他类
2、实现监听器,并添加到状态机实例上

    StateMachineEventListener listener = new StateMachineEventListener();
    stateMachine.addStateListener(listener);

语境整合

通过监听事件或使用状态和转换的动作来与状态机进行交互有点受限。这种方法的时间太长,而且很难创建与状态机正在使用的应用程序的交互。对于这个特定的用例,我们进行了一种Spring样式上下文集成,可以轻松地将状态机功能附加到bean中

    在状态配置中
    builder.configureConfiguration()
        .withConfiguration()
        .machineId("myMachineId")
        .beanFactory(beanFactory);
    通过上下文集成状态机的配置
    @WithStateMachine(id = "myMachineId")
    static class Bean17 {

        @OnStateChanged
        public void onStateChanged() {
        }
    }

如果机器没有创建为Bean,则必须为机器设置 BeanFactory,如上所示。否则机器将不知道调用@WithStateMachine方法的处理程序

?状态机访问器

    stateMachine.getStateMachineAccessor().doWithAllRegions(access -> access.setRelay(stateMachine));
    stateMachine.getStateMachineAccessor().withAllRegions().stream().forEach(access -> access.setRelay(stateMachine));
    stateMachine.getStateMachineAccessor().withRegion().setRelay(stateMachine);

恢复状态机后恢复状态

stateMachine.stop();
stateMachine.getStateMachineAccessor()
.doWithAllRegions(access ->
access.resetStateMachine(new DefaultStateMachineContext<>(startingState, null, null, null)));
stateMachine.start();

StateMachineInterceptor拦截器

拦截器可用于拦截和停止当前状态变化或转换逻辑,还可以拦截到错误,通过访问器注册

stateMachine.getStateMachineAccessor()
    .withRegion().addStateMachineInterceptor(new StateMachineInterceptor<String, String>() {

        @Override
        public Message<String> preEvent(Message<String> message, StateMachine<String, String> stateMachine) {
            return message;
        }

        @Override
        public StateContext<String, String> preTransition(StateContext<String, String> stateContext) {
            return stateContext;
        }

        @Override
        public void preStateChange(State<String, String> state, Message<String> message,
                Transition<String, String> transition, StateMachine<String, String> stateMachine) {
        }

        @Override
        public StateContext<String, String> postTransition(StateContext<String, String> stateContext) {
            return stateContext;
        }

        @Override
        public void postStateChange(State<String, String> state, Message<String> message,
                Transition<String, String> transition, StateMachine<String, String> stateMachine) {
        }

        @Override
        public Exception stateMachineError(StateMachine<String, String> stateMachine,
                Exception exception) {
            return exception;
        }
    });

?状态机安全 State Machine Security

    .withSecurity()
        .enabled(true)
        .transitionAccessDecisionManager(null)
        .eventAccessDecisionManager(null);

使用安全属性和表达式

状态机错误处理StateMachineInterceptor

如果状态机在状态转换逻辑期间检测到内部错误,则可能引发异常。在内部处理此异常之前,用户有机会进行拦截

stateMachine.getStateMachineAccessor()
        .doWithRegion(new StateMachineFunction<StateMachineAccess<String, String>>() {

        @Override
        public void apply(StateMachineAccess<String, String> function) {
            function.addStateMachineInterceptor(
                    new StateMachineInterceptorAdapter<String, String>() {
                @Override
                public Exception stateMachineError(StateMachine<String, String> stateMachine,
                        Exception exception) {
                    // return null indicating handled error
                    return exception;
                }
            });
        }
    });

还可以整合监听器

public class ErrorApplicationEventListener
        implements ApplicationListener<OnStateMachineError> {

    @Override
    public void onApplicationEvent(OnStateMachineError event) {
        // do something with error
    }
}

持久化状态机 StateMachinePersister

持久性功能允许用户将状态机本身的状态保存到外部存储库中,然后根据序列化状态重置状态机。例如,如果您有一个数据库表保持订单,如果需要为每个更改构建一个新实例,那么通过状态机更新订单状态会太昂贵。持久性功能允许您重置状态机状态,而无需实例化新的状态机实例

可以使用状态机拦截器而不是在状态机内的状态改变期间完成将序列化状态保存到外部存储器的尝试。如果此拦截器回调失败,则状态更改尝试将暂停,而不是结束到不一致状态,然后用户可以手动处理此错误

状态机监控器 Monitoring State Machine

可用于获取有关转换和操作执行时间的持续时间的更多信息。

分布式状态机(还未成熟)

测试支持:一组实用程序类来轻松测试状态机实例

?存储支持:Repository Support,JPA\Redis\Mongo

状态机示例

  • 可以设置实例的范围:Scope是一个状态机示例,它使用会话范围为每个用户提供单独的实例。

  • 完全性:整合spring security

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spring StateMachine使用笔记 的相关文章

  • Java EE:如何获取我的应用程序的 URL?

    在 Java EE 中 如何动态检索应用程序的完整 URL 例如 如果 URL 是 localhost 8080 myapplication 我想要一个可以简单地将其作为字符串或其他形式返回给我的方法 我正在运行 GlassFish 作为应
  • 如何在 Play java 中创建数据库线程池并使用该池进行数据库查询

    我目前正在使用 play java 并使用默认线程池进行数据库查询 但了解使用数据库线程池进行数据库查询可以使我的系统更加高效 目前我的代码是 import play libs Akka import scala concurrent Ex
  • 使用 Android 发送 HTTP Post 请求

    我一直在尝试从 SO 和其他网站上的大量示例中学习 但我无法弄清楚为什么我编写的示例不起作用 我正在构建一个小型概念验证应用程序 它可以识别语音并将其 文本 作为 POST 请求发送到 node js 服务器 我已确认语音识别有效 并且服务
  • 制作一个交互式Windows服务

    我希望我的 Java 应用程序成为交互式 Windows 服务 用户登录时具有 GUI 的 Windows 服务 我搜索了这个 我发现这样做的方法是有两个程序 第一个是服务 第二个是 GUI 程序并使它们进行通信 服务将从 GUI 程序获取
  • 反射找不到对象子类型

    我试图通过使用反射来获取包中的所有类 当我使用具体类的代码 本例中为 A 时 它可以工作并打印子类信息 B 扩展 A 因此它打印 B 信息 但是当我将它与对象类一起使用时 它不起作用 我该如何修复它 这段代码的工作原理 Reflection
  • 操作错误不会显示在 JSP 上

    我尝试在 Action 类中添加操作错误并将其打印在 JSP 页面上 当发生异常时 它将进入 catch 块并在控制台中打印 插入异常时出错 请联系管理员 在 catch 块中 我添加了它addActionError 我尝试在jsp页面中打
  • 路径中 File.separator 和斜杠之间的区别

    使用有什么区别File separator和一个正常的 在 Java 路径字符串中 与双反斜杠相反 平台独立性似乎不是原因 因为两个版本都可以在 Windows 和 Unix 下运行 public class SlashTest Test
  • 斯坦福 NLP - 处理文件列表时 OpenIE 内存不足

    我正在尝试使用斯坦福 CoreNLP 中的 OpenIE 工具从多个文件中提取信息 当多个文件 而不是一个 传递到输入时 它会给出内存不足错误 All files have been queued awaiting termination
  • 从 127.0.0.1 到 2130706433,然后再返回

    使用标准 Java 库 从 IPV4 地址的点分字符串表示形式获取的最快方法是什么 127 0 0 1 到等效的整数表示 2130706433 相应地 反转所述操作的最快方法是什么 从整数开始2130706433到字符串表示形式 127 0
  • 使用Caliper时如何指定命令行?

    我发现 Google 的微型基准测试项目 Caliper 非常有趣 但文档仍然 除了一些示例 完全不存在 我有两种不同的情况 需要影响 JVM Caliper 启动的命令行 我需要设置一些固定 最好在几个固定值之间交替 D 参数 我需要指定
  • 将不同类型的参数传递给 jdbctemplate 查询

    我正在尝试使用带有少量不同类型参数的 where 子句从数据库中检索记录 这是我编写的简单方法 我将breedId和性别作为参数传递 public List
  • 如何在控制器、服务和存储库模式中使用 DTO

    我正在遵循控制器 服务和存储库模式 我只是想知道 DTO 在哪里出现 控制器应该只接收 DTO 吗 我的理解是您不希望外界了解底层域模型 从领域模型到 DTO 的转换应该发生在控制器层还是服务层 在今天使用 Spring MVC 和交互式
  • 在 Mac 上正确运行基于 SWT 的跨平台 jar

    我一直致力于一个基于 SWT 的项目 该项目旨在部署为 Java Web Start 从而可以在多个平台上使用 到目前为止 我已经成功解决了由于 SWT 依赖的系统特定库而出现的导出问题 请参阅相关thread https stackove
  • Google App Engine 如何预编译 Java?

    App Engine 对应用程序的 Java 字节码使用 预编译 过程 以增强应用程序在 Java 运行时环境中的性能 预编译代码的功能与原始字节码相同 有没有详细的信息这是做什么的 我在一个中找到了这个谷歌群组消息 http groups
  • 如何从指定日期获取上周五的日期? [复制]

    这个问题在这里已经有答案了 如何找出上一个 上一个 星期五 或指定日期的任何其他日期的日期 public getDateOnDay Date date String dayName 我不会给出答案 先自己尝试一下 但是 也许这些提示可以帮助
  • 在mockito中使用when进行模拟ContextLoader.getCurrentWebApplicationContext()调用。我该怎么做?

    我试图在使用 mockito 时模拟 ContextLoader getCurrentWebApplicationContext 调用 但它无法模拟 here is my source code Mock org springframewo
  • 如何从泛型类调用静态方法?

    我有一个包含静态创建方法的类 public class TestClass public static
  • 玩!框架:运行“h2-browser”可以运行,但网页不可用

    当我运行命令时activator h2 browser它会使用以下 url 打开浏览器 192 168 1 17 8082 但我得到 使用 Chrome 此网页无法使用 奇怪的是它以前确实有效 从那时起我唯一改变的是JAVA OPTS以启用
  • 将 List 转换为 JSON

    Hi guys 有人可以帮助我 如何将我的 HQL 查询结果转换为带有对象列表的 JSON 并通过休息服务获取它 这是我的服务方法 它返回查询结果列表 Override public List
  • Spring Boot @ConfigurationProperties 不从环境中检索属性

    我正在使用 Spring Boot 1 2 1 并尝试创建一个 ConfigurationProperties带有验证的bean 如下所示 package com sampleapp import java net URL import j

随机推荐

  • 技术博客笔记大汇总

    hello 小伙伴们大家好 今天给小伙伴们推荐的开源项目是 YCBlogs 这个开源项目整合博客笔记等资料信息 15年10月到至今 包括Java基础及深入知识点 Android技术博客 Python Go学习笔记等等 还包括平时开发中遇到的
  • 高性能MySQL学习笔记(1) —— MySQL架构

    MySQL架构 1 MySQL逻辑架构 这里分为三层 1 连接层 连接与线程处理 这一层并不是MySQL独有 一般的基于C S架构的都有类似组件 比如连接处理 授权认证 安全等 2 SQL处理层 也叫MySQL服务器层 包括缓存查询 解析器
  • python——pip 安装出现ERROR: Exception: Traceback (most recent call last):的问题

    用pip安装东西 总会提示 当我按照指示输入 python m pip install upgrade pip 命令时 用100次pip 99次会报下面的错误之前看了很多前人的办法 有说是因为网络不好 建议多次暴力尝试的 还有建议说使用ea
  • AI时代你需要知道的:知识图谱技术原理(必读)

    知识图谱是什么 知识图谱最早由谷歌发布 为了提升搜索引擎返回答案的质量以及用户查询的效率 在知识图谱辅助下 搜索引擎可以洞察到用户查询背后的一个语义信息 然后返回更为精准结构化的信息 从而更大可能的去满足用户的一个查询需求 当我们进行搜索时
  • mysql集群

    3 mysql集群 3 1 企业中常用的数据库解决方案 3 2 mysql常见的几种集群方式 3 2 1 MYSQl MMM Master Master Replication Manager for MySQL MySQL MMM 是 M
  • idea debug到一半停止_使用IDEA的Debug调试功能,查看程序的运行过程

    Debug追踪 使用IDEA的断点调试功能 查看程序的运行过程 知乎视频 www zhihu com 1 在有效代码行 点击行号右边的空白区域 设置断点 程序执行到断点将停止 我们可以手动来运行程序 2 点击Debug运行模式 3 程序停止
  • C语言printf打印的奥秘

    基础补充 想完全掌握C语言的 printf 函数 你就得明白C语言中的基本类型及其所占字节数 位 字节 字的概念大家自己百度了解 下面我只给其关系 8位 1字节 2字节 1字 代码示例 作为一个刚入门的小白 我们玩的数据都是十分小的 一般不
  • kubernetes最佳实践(三) - kubedns部署

    1 服务发现 kubernetes 提供了 service 的概念可以通过 VIP 访问 pod 提供的服务 但是在使用的时候还有一个问题 怎么知道某个应用的 VIP 比如我们有两个应用 一个 app 一个 是 db 每个应用使用 rc 进
  • Spring boot的配置文件中属性值有特殊符号,比如@的怎么解决

    用双引号将值引起来就可以识别 例子
  • 【Zabbix实战之运维篇】Zabbix监控模板的配置管理

    Zabbix实战之运维篇 Zabbix监控模板的配置管理 一 检查Zabbix平台的状态 1 检查Zabbix各组件容器状态 2 检查Zabbix的web页面 二 查看系统的默认模板信息 1 查看系统的所有监控模板 2 搜索某个监控模板 3
  • springCloud-系统学习3- 创建微服务工程2

    2 11 Feign应用 是对下面代码的优化 自动根据参数拼接http请求地址 2 11 1 操作 效果 2 12 Feign负载均衡及熔断 Feign集成了ribbon配置项和Hystrix熔断的Fallback配置项 可以使用Feign
  • 基于ARM编译安装docker-harbor

    基于ARM编译安装docker harbor 一 编译内核 此举是为了保证redis镜像可以正常启动 1 安装依赖 yum y install gcc bc gcc c ncurses ncurses devel cmake elfutil
  • Python3 类型转换

    INT 支持转换为INT类型的 仅有 float str bytes 其他类型均不支持 float gt int 会去掉小数点及后面的数值 仅保留整数部分 int 12 94 12 str gt int 如果字符串中有数字 0 9 和正负号
  • 将yyyy-MM-dd hh:mm:ss转化为yyyy-MM-dd

    Date currentTime new Date SimpleDateFormat formatter new SimpleDateFormat yyyy MM dd Date strtodate String datas try str
  • Java异常和处理机制

    棒棒有言 追逐梦想的过程就像是一个人在走一条黑暗 幽深而又漫长的隧道 多少次跌倒又爬起 经历了多少个暗无天日的黑夜与白天 一路上沉淀着难以计数的汗水与泪水 不断地自我暗示 只要自己坚持 只要勇敢地一向往前走 就必须能找到出口 必须会看到光明
  • 读书笔记 摘自:《思维导图攻略:快速上手与落地实践》

    思维导图攻略 快速上手与落地实践 王健文 出版 2019 01 01 7 3万字 内容提要 无落地 不导图 思维导图的学习并不在于思维导图的绘制本身 而是在于实际应用和思维提升 第一章 精英人士自我提升的思维利器 第一节 提升大脑学习力的秘
  • 广州华锐互动:利用VR复原文化遗址,沉浸式体验历史文物古迹的魅力

    在过去的几十年里 科技发展飞速 为我们打开了无数新的视角和可能性 其中 虚拟现实 Virtual Reality 简称VR 技术的崭新应用 为我们提供了一种全新的 近乎身临其境的体验历史的方式 本文将重点探讨VR技术在复原历史古迹方面的应用
  • How to use tar command to complete file compression and decompression in Ubuntu

    TAR 1 GNU TAR Manual TAR 1 NAME tar an archiving utility SYNOPSIS Traditional usage tar A
  • beam search的例子

    看了一下网上对beam search的讲解 感觉都说的太杂了 我试图用一个最简单的例子来帮助读者理解 见下图 假设我有一个模型 能够根据当前词输出下一个词的概率分布 最后依次这样就能生成一大串文本 以上面的图为例 The 的下一个词的最大概
  • Spring StateMachine使用笔记

    Spring StateMachine使用笔记 配置状态机 状态 分层状态 withStates 配置状态 states状态列表 可以使用多个withStates进行parent分层 配置区域 当相同的分层状态机具有多组状态时 每个都具有初