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使用笔记 的相关文章

  • TestNG 启动期间发生内部错误

    我创建了一个 TestNG 类 FirstTest java 当我将测试用例作为 TestNG Test 运行时 出现以下错误 期间发生内部错误 启动 FirstTest java lang NullPointerException Ecl
  • 如何根据 JComboBox 选择动态地将控件添加到表单?

    我正在尝试使用 Swing 创建一个简单的 java 表单 这个想法的基本思想是用户将在 JComboBox 中选择 0 到 5 然后 通过 ItemStateChanged 侦听器 将动态添加几个面板 每个面板包含 4 个控件 因此 如果
  • Criteria eager fetch-joined 集合以避免 n+1 选择

    假设 Item 和 Bid 是实体 一个 Item 有多个 Bid 它们被映射到休眠在典型的父子关系中
  • 在游戏框架中编写功能测试的正确方法

    在为基于 play1 2 4 的 web 应用程序编写功能测试时 我对如何正确编码感到有点困惑 困惑在于所涉及的事务边界 我在某处读到每个测试都有自己的事务 在我的应用程序中 用户可以登录并向购物车添加一些商品 然后他可以提供一个地址 以便
  • 使用 CXF 通过 HTTP 基本身份验证使用 Web 服务时出现 401 错误

    我正在尝试在 JUnit 测试中使用使用 HTTP 基本身份验证 使用 Apache CXF 的远程 Web 服务 我收到的错误是 javax xml ws WebServiceException Failed to access the
  • BigDecimal 中 Divide 方法的 Scale()

    new BigDecimal 37146555 53880000 divide new BigDecimal 1000000 scale 这返回10 但根据API divide method 返回一个 BigDecimal 其值为 这个 除
  • JSF 错误 - IllegalStateException:PWC3999:提交响应后无法创建会话[重复]

    这个问题在这里已经有答案了 我是 JSF 新手 正在构建一个使用 Facelet 创建的应用程序 这是我的模板master xhtml
  • 从另一个类添加 Swing 组件

    我正在学习java 我正在尝试从另一个类向我的框架添加一个菜单栏 练习将代码划分为多个类以更好地组织程序 这是我的代码示例 public class MainApp public static void main String args C
  • Android 防火墙与 VpnService

    我正在尝试使用 BS 项目的 VpnService 为 Android 实现一个简单的防火墙 我选择 VpnService 因为它将在非 root 设备上运行 它将记录连接并让您过滤连接 基于IP 有一个应用程序可以做到这一点 因此这是可能
  • Android Studio 找不到 com.android.support:support-v4:19.1.0

    我已将一个项目导入Android Studio但我收到错误 找不到 com android support support v4 19 1 0 我在哪里可以找到这个文件 我已经使用导入了项目Gradle 我有Android Studio v
  • 大型 XML 的 XML 节点到字符串转换

    到目前为止我一直在使用DOM源在我的 Android 应用程序中将 XML 文件转换为字符串 这是我的代码 public String convertElementToString Node element throws Transform
  • JavaFX 动画使用循环?

    我正在尝试制作一款类似太空侵略者的游戏 我画了一个正方形 我想通过使用循环逐步向下移动它thread sleep 然而 正方形立即被绘制出来 我知道有可以使用的动画路径 但我想保持低水平并仅使用坐标系 有没有办法使用这样的循环来制作时间轴动
  • Android 上的自定义视图和窗口属性

    我想要做的是在我的应用程序顶部添加一个视图 该视图类似于过滤器视图 我想操纵屏幕的颜色 并且我还希望能够同时更改屏幕的亮度时间 这两件事似乎是分开起作用的 但不能一起起作用 这是我的代码 添加视图 colourView new Layer
  • EasyMock : java.lang.IllegalStateException: 1 个匹配器预期,2 个记录

    我在使用 EasyMock 2 5 2 和 JUnit 4 8 2 通过 Eclipse 运行 时遇到问题 我已阅读此处所有类似的帖子 但尚未找到答案 我有一个包含两个测试的类 它们测试相同的方法 我正在使用匹配器 每个测试单独运行时都会通
  • 如何在开头时解析 json 文件

    我想解析以下 JSON 文件 但以 向我表明这是一个数组 然后继续 对象 我当前的解析器返回一个 JSON 对象 我的问题是 如何修改解析器来解析这个文件 这样解析器将为我提供其他 JSON 文件 从对象或排列开始 JSON 文件 codi
  • org.apache.catalina.core.JreMemoryLeakPreventionListener 中急切调用 URLConnection 的 setDefaultUseCaches(false) 是什么原因

    这个问题可能有点难以找到答案 这是一个系列中的问题考虑使用 Policy getPolicy 的原因是什么 因为它将保留对上下文的静态引用并可能导致内存泄漏 https stackoverflow com questions 7057421
  • System.out.println("嗨"+6+10);打印Hi610?

    为什么要这样做 太令人困惑了 运算符优先级和结合性 两点 操作员 如果一个或两个参数都是字符串 则进行字符串连接 操作员 从左到右工作 所以在你的例子中 Hi 6 is Hi6 and Hi6 10 is Hi610 编辑 正如您在对另一个
  • 为什么我的 Java 路径中添加了“L”?

    我在我的类路径中加载了一个 jar 在 iReport 中 如果重要的话 我确信它具有所需的方法 但是当我尝试测试连接 从而调用该 jar 时 我得到一个 java lang NoSuchMethodError 说它正在引用班上 Lorg
  • 需要同步仅增量计数器吗?

    我使用整数作为计数器 该整数只会增加 并且肯定有多个线程会同时增加它 当没有其他线程尝试访问其值时 在程序执行结束时读取该计数器的值 我假设我不必为这种仅增量计数器使用锁或任何类型的同步 这是正确的吗 如果这有什么区别的话 我用 Java
  • 使用 Spring Batch 将文件中的日期解析为 LocalDateTime

    我正在尝试使用 Spring Batch 读取包含日期的 CSV 文件 但在将日期解析为LocalDateTime Object 字段 日期 上的对象 目标 中的字段错误 拒绝值 2017 07 20 04 15 25 0 代码 typeM

随机推荐

  • 技术博客笔记大汇总

    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分层 配置区域 当相同的分层状态机具有多组状态时 每个都具有初