飞道的博客

spring-statemachine状态机梳理

516人阅读  评论(0)

目录

一、基本回顾

1、为什么要用状态机

2、什么是状态机

3、状态机可归纳为4个要素

4、对应Spring StateMachine的核心步骤

5、简单例子

添加maven依赖

定义状态枚举和事件枚举

完成状态机的配置

简单测试一下

添加Listener 监听器,当状态变更时,触发方法

添加拦截器

StateMachine 状态机实例

 定义一个基于状态机实例的Handler

Springboot注入Handler和Listener bean的Configuration类

​编辑 注解方式使用

多个状态机共存

6、适用场景 

二、测试注意


一、基本回顾

1、为什么要用状态机

系统状态和条件非常多、状态间切换复杂的场景,如何更好实现状态的切换:

方法1: if-else/switch方式实现,缺点是 代码可读性、可维护性差;

方式2:状态机实现。 本质上,状态机具备了多种优点,如 可读性好,可维护性高,以及容易扩展等等

2、什么是状态机

状态机(state machine)是一种行为,它指定对象在其生命周期内响应事件所经历的状态序列,以及对象对这些事件的响应

3、状态机可归纳为4个要素

即现态、条件、动作、次态。“现态”和“条件”是因,“动作”和“次态”是果。

现态:现在的状态。

条件:又称为“事件”,是一个动作发生的前提。当一个条件被满足,将会触发一个动作,或者执行一次状态的迁移。

动作:条件满足后执行的动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。动作不是必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态。

次态:条件满足后要迁往的新状态。“次态”是相对于“现态”而言的,“次态”一旦被激活,就转变成新的“现态”了。

4、对应Spring StateMachine的核心步骤

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

5、简单例子

添加maven依赖

<dependency>
    <groupId>org.springframework.statemachine</groupId>
    <artifactId>spring-statemachine-core</artifactId>
    <version>2.0.1.RELEASE</version>
</dependency>

定义状态枚举和事件枚举


  
  1. public enum CompanyStatus {
  2.     WAIT_SUBMIT( 0, "待企业实名"),
  3.     WAIT_REAL_NAME( 1, "待个人实名"),
  4.      。。。
  5. CompanyStatus(int status, String desc) {
  6. this.status = status;
  7. this.desc = desc;
  8. }
  9. }
  10. public enum CompanyEvents {
  11. UNKNOWN_EVENT( 0, "未知事件"),
  12. UN_REAL_NAME( 1, "个人未实名事件"),
  13. REAL_NAME( 2, "个人实名事件"),
  14. ...
  15. CompanyEvents(int value, String desc) {
  16. this.value = value;
  17. this.desc = desc;
  18. }
  19. }

完成状态机的配置

包括:(1)状态机的初始状态和所有状态;(2)状态之间的转移规则


  
  1. @Configuration
  2. @EnableStateMachineFactory(name = "companyStateMachineFactory")
  3. public class CompanyStateMachineConfig extends EnumStateMachineConfigurerAdapter<CompanyStatus, CompanyEvents> {
  4. @Override
  5. public void configure(StateMachineStateConfigurer<CompanyStatus, CompanyEvents> states) throws Exception {
  6. states .withStates()
  7. // 定义初始状态
  8. .initial(CompanyStatus.WAIT_SUBMIT)
  9. // 定义所有状态集合
  10. .states(EnumSet.allOf(CompanyStatus.class));
  11. }
  12. @Override
  13. public void configure(StateMachineTransitionConfigurer<CompanyStatus, CompanyEvents> transitions) throws Exception {
  14. transitions .withExternal()
  15. .source(CompanyStatus.WAIT_SUBMIT) .target(CompanyStatus.WAIT_REAL_NAME)
  16. .event(CompanyEvents.UN_REAL_NAME)
  17. .and() .withExternal()
  18. .source(CompanyStatus.WAIT_SUBMIT) .target(CompanyStatus.WAIT_CHECK)
  19. .event(CompanyEvents.COMPANY_SUBMITED)
  20. .and() .withExternal()
  21. ...
  22. }
  23. }

简单测试一下

启动状态机、发送不同的事件,通过日志验证状态机的流转过程


  
  1. @Resource
  2.     StateMachine<CompanyStatus, CompanyEvents> stateMachine;
  3.     public void createStateMachine(){
  4.         stateMachine .start();
  5.         stateMachine .sendEvent(CompanyEvents.UN_REAL_NAME);
  6.         stateMachine .sendEvent(CompanyEvents.REAL_NAME);
  7.     }

添加Listener 监听器,当状态变更时,触发方法


  
  1. public interface PersistStateChangeListener {
  2. /**
  3. * 当状态被持久化,调用此方法
  4. *
  5. * @param state
  6. * @param message
  7. * @param transition
  8. * @param stateMachine 状态机实例
  9. */
  10. void onPersist(State<CompanyStatus, CompanyEvents> state, Message<CompanyEvents> message,
  11. Transition<CompanyStatus, CompanyEvents> transition, StateMachine<CompanyStatus, CompanyEvents> stateMachine);
  12. }
  13. @Slf4j
  14. @Component("companyPersistStateChangeListener")
  15. public class CompanyPersistStateChangeListener implements PersistStateChangeListener {
  16. ...真正实现
  17. }

添加拦截器

不同于Listener。其可以改变状态转移链的变化。

主要在preEvent(事件预处理)、preStateChange(状态变更的前置处理)、postStateChange(状态变更的后置处理)、preTransition(转化的前置处理)、postTransition(转化的后置处理)、stateMachineError(异常处理)等执行点生效。


  
  1. private class PersistingStateChangeInterceptor extends StateMachineInterceptorAdapter<CompanyStatus, CompanyEvents> {
  2. @Override
  3. public void preStateChange( State< CompanyStatus, CompanyEvents> state, Message< CompanyEvents> message,
  4. Transition< CompanyStatus, CompanyEvents> transition, StateMachine< CompanyStatus,
  5. CompanyEvents> stateMachine) {
  6. listeners.onPersist(state, message, transition, stateMachine);
  7. }
  8. }

StateMachine 状态机实例

StateMachine 状态机实例,spring statemachine支持单例、工厂模式两种方式创建。

每个statemachine有一个独有的machineId用于标识machine实例;

需要注意的是statemachine实例内部存储了当前状态机等上下文相关的属性,因此这个实例不能够被多线程共享。

例如:工厂模式创建实例

 定义一个基于状态机实例的Handler

为了方便扩展更多的Listener,以及管理Listeners和Interceptors。可以定义一个基于状态机实例的Handler: PersistStateMachineHandler,以及持久化实体的监听器。

Springboot注入Handler和Listener bean的Configuration类

 注解方式使用


  
  1. @Slf4j
  2. @Service
  3. public class TestService {
  4. @PostConstruct
  5. private void initialize() {
  6.     this .persistStateMachineHandler .addPersistStateChangeListener(companyPersistStateChangeListener);
  7. }
  8. public void userToDoSomething() {
  9. ... //userToDoSomething
  10. persistStateMachineHandler .handleEventWithState(MessageBuilder. withPayload(events)
  11. . setHeader( "company", company. getId()). build(), CompanyStatus. of(status));
  12. }
  13. }

多个状态机共存

在实际项目中一般都会有多个状态机并发执行,比如订单,同一时刻会有不止一个订单在运行,而每个订单都有自己的订单状态机流程。想要实现多个状态机的并行执行,就需要用到builder.

6、适用场景 

各种审核逻辑业务: 如 财务审核、交易业务、TOB结算业务审核、合同状态

订单支付类业务: 如 商家单据,订单会有多种状态:已下单、待支付、已支付、待发货、待收货、已完成、退款中、退款成功等等

二、测试注意

  • 单个业务场景下,状态流转正常;
  • 状态异常流转时兼容处理;
  • 并发操作,分别流转到不同状态正常


转载:https://blog.csdn.net/wodeyijia911/article/details/128410470
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场