Spring状态机

Spring Statemachine

spring statemachine框架的作用在于提供一个项目业务切入的视角,关注点不在于具体的业务数据,而是状态流程。

为什么要用状态机?

增强代码的可维护性, 对于流程复杂易变的场景能大大减轻维护的难度。

业务逻辑与状态流程解耦,避免业务与状态“散弹式”维护。

状态流转越复杂,越能体现状态流转的逻辑清晰,减少的“胶水”代码也越多。

相关概念:

  • Transition: 状态转移节点,是组成状态机引擎的核心。
  • source/from:现态。
  • target/to:次态。
  • event/trigger:触发节点从现态转移到次态的动作,这里也可能是一个timer。
  • guard/when:状态迁移前的校验,执行于action前。
  • action:用于实现当前节点对应的业务逻辑处理。

1. 如何初始化状态机

声明States、Events,定义State、Event、Choice、Action间的关系,定义Action

在这里插入图片描述

	public enum States {
	    SI,
	    S1,
	    S2,
	    S3
	}
	public enum Events {
	    E1,
        E2
	}
	public class StateMachineConfig extends EnumStateMachineConfigurerAdapter<States, Events> {
	
	    @Override
	    public void configure(StateMachineStateConfigurer<States, Events> states) throws Exception {
            //声明伪状态(初始和终止状态)和choice
	        states.withStates()
	            .initial(States.SI)
	            .choice(States.S1)
	            .states(EnumSet.allOf(States.class));
	    }
	
	    @Override
	    public void configure(StateMachineTransitionConfigurer<States, Events> transitions) throws Exception {
	        transitions
                //状态内部推进,即单个状态自旋
                .withInternal()
                .source(States.SI)
                .event(Events.E1)
                .action(new SimpleAction())
                .and()
                //两个状态间推进
                .withExternal()
	            .source(States.SI)
	            .target(States.S1)
	            .event(Events.E2)
	            .action(new TestAction(),new ErrorHandlerAction())
	            .and()
	            //分支状态
	            .withChoice()
	            .source(States.S1)
	            //类比为If判断,Guard实现类返回true/false,ErrorHandlerAction()会在Action抛异常时执行
	            .first(States.S2,new FirstGuard(),new FirstAction(),new ErrorHandlerAction())
	            .last(State.S3);
	    }

choice里方法设计比较特殊,first、then、last都具有排他性,first类比if,then类比else if,last类比else

在这里插入图片描述

2. 怎么注册任务

状态机存在多种模式,单例模式,即全局共用一个状态机实例;以及工厂模式,即每次获取状态机时创建一个状态机实例,多个状态机使用ID进行区分,工厂模式显然更契合企业级开发的场景。

任务的状态流程由状态机进行管控。由于每个任务都会创建一个状态机实例,这会消耗一定的资源,在任务量较少的情况下不影响,当任务数量较多时可引入对象池(较难设计,容易出现并发问题)。

3. 怎么推进状态

向状态机发送事件(sendEvent(Events e))

  • 在action中sendEvent推进状态机实现自驱。(是否算是耦合?)

  • Choice中调用Guard方法,返回true/false判断状态如何变化,guard方法可自己实现;为目标state定义action,处理业务逻辑,建议抽象出实现类,将action做为粘连。

使用TimerTrigger(官方用于Internal transition)

  • timer(1000):每1000ms执行一次,搭配action使用
  • timerOnce(1000):进入目标状态1000ms后执行一次,搭配action使用

4. 怎么读取当前状态

spring状态机引擎设计了StateContext来传递状态机当前状态的上下文,其中包括的信息有:

  • 当前的MessageEvent(或它们的MessageHeaders,如果已知的话)。
  • 状态机的Extended State
  • StateMachine本身。
  • 可能的状态机错误。
  • 当前Transition
  • 状态机的源状态。
  • 状态机的目标状态。
  • 当前的Stage,stage表示状态机与用户的交互状态详情,如:EVENT_NOT_ACCEPTED、STATE_CHANGED、STATE_ENTRY等

5. 状态机持久化

  • 持久化StateMachineContext(简称状态机上下文)到redis或db,通过获取状态机上下文重建状态机(因为对象图太多,普通Java序列化无法保存,官方推荐默认多张表进行存储)
  • 伪持久化,仅持久化任务状态和所需数据,通过build新状态机并设置状态及数据来实现重建状态机的目的(数据量小)

6. 业务逻辑异常怎么处理

spring-statemachine的异常处理比较独特,它会捕获异常并转为警告,状态也能成功转移到次态,调用业务逻辑后需要手动判断(或者使用拦截器)是否发生异常,通常以变量的方式来传递信息,存储在StateContext中

7. 监听器和拦截器

​ 官方已经定义好了listener和interceptor的接口方法。

  • listener运行监听上下文事件(对应上面的Stage,如OnStateExitEvent、OnStateEntryEvent、OnStateChangedEvent),在发生各种状态更改、操作等时获得回调。
  • interceptor可以拦截和停止当前状态的更改或更改其状态转换逻辑,例如上面提到的异常处理拦截器,官方也提供了相应的示例,Spring Statemachine - 参考文档

存在问题:

​ 3.0.x后引入了reactor响应式编程,不建议使用原有的同步方法,sendEvent方法参数变为流,后续升级及维护可能需要引入reactor

在这里插入图片描述

​ 长等待action(如等待用户确认)需要长时间轮询,这个过程状态机和线程都会被占用,可否将任务关闭,等待用户操作后重建状态机;但是这样引入了未被状态机管控的流程,不符合我们管理状态流程的初衷

参考文献

Alan宇 的个人主页 - 文章 - 掘金 (juejin.cn)

wphmoon123-CSDN博客

Spring Statemachine - 参考文档

Spring StateMachine 文档 | 中文文档 (gitcode.host)

x-zeros - 博客园 (cnblogs.com)

  • 25
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java Spring状态机是一个框架,用于在Spring应用程序中使用状态机概念。它提供了易于使用的扁平单级状态机和分层状态机结构,以简化复杂的状态配置。它还提供了状态机区域,用于更复杂的状态配置,并使用触发器、转换、警卫和操作。此外,它还提供了键入安全配置适配器、生成器模式、基于Zookeeper的分布式状态机状态机事件监听器、UML Eclipse Papyrus建模和将计算机配置存储在永久存储中。Spring IOC集成将bean与状态机关联起来。 以下是一个简单的Java Spring状态机示例: ```java @Configuration @EnableStateMachine public class SimpleStateMachineConfig extends StateMachineConfigurerAdapter<String, String> { @Override public void configure(StateMachineStateConfigurer<String, String> states) throws Exception { states .withStates() .initial("SI") .state("S1") .state("S2") .end("SF"); } @Override public void configure(StateMachineTransitionConfigurer<String, String> transitions) throws Exception { transitions .withExternal() .source("SI").target("S1").event("E1") .and() .withExternal() .source("S1").target("S2").event("E2") .and() .withExternal() .source("S2").target("SF").event("E3"); } } ``` 此配置定义了一个简单的状态机,其中包含三个状态:SI、S1和S2,以及一个结束状态SF。状态机从SI开始,然后根据事件E1转换到S1,然后根据事件E2转换到S2,最后根据事件E3转换到SF。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值