Spring Statemachine 业务应用

司空俊雄
2023-12-01

1  前言   什么是Spring Statemachine

有限状态机,简称状态机(FSM),是一种对有限个状态,以及这些状态之间的流转和动作等行为的数据建模。

使用 FSM 模型,可以有助于这些状态的顺序、导致这种状态变化的事件进行管理,主要用于将状态和时间的控制权从不同业务的 services 方法中的 if else 逻辑中抽离出来,进行统一的管理和配置。对于有复杂的状态流,扩展性要求比较高的场景都可以使用该模型。

面是状态机模型中的4个要素,即现态、条件、动作、次态。

  • 现态:是指当前所处的状态。
  • 事件:又称为“条件”。当一个条件被满足,将会触发一个动作,或者执行一次状态的迁移。
  • 动作:条件满足后执行的动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。动作不是必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态。
  • 次态:条件满足后要迁往的新状态。“次态”是相对于“现态”而言的,“次态”一旦被激活,就转变成新的“现态”了。

2 使用 spring Statemachine 的好处

2.1 什么时候使用spring statemachine

        在做软件项目的时候,我们会以各种各样的角度看一个项目,比如OOP,万物皆对象,一个订单就是一个对象,所谓的状态变化,无非就是订单这个对象的变量在不停的变化,变化的过程就是对象的方法;再比如数据库,万物无非CRUD的组合,订单不过就是增删改查,数据库原子操作的组合罢了。这些角度对吗?可以说都对,因为用这些角度都有人做出了项目,但有些角度对项目的理解会比较方便,有些角度就比较费劲,比如订单,如果是订单的生成查看,用CRUD就是很适合,因为这个角度很契合;如果是订单和商品的关系,订单和其他业务的关联,OOP的角度就很适合,因为用封装、多态的视角比较容易看清楚这类业务和他们之间的边界,方便划分各自的功能。而状态机的角度,就是以状态变化和流程运转为角度切入看问题,所以对于流程性为主轴的项目就很适合,所以是不是用spring statemachine,衡量的标准就是,这个项目的主轴,或者场景是不是一个流程性、状态变化为主的项目,如果是,就可以考虑用spring statemachine了。

 2.2 使用spring statemachine的好处

    软件项目其实有很多的办法完成,手段很多,完成的情况也各有优劣,那怎么判断用什么办法好呢,我觉得答案就是看项目主要解决的问题是什么。软件项目既不是程序员练手的工具,也不是老板用来蒙钱的门面,它是用来解决问题的,所以能清晰的表达问题,解决问题的手段就是好的技术手段,所以,如果一种技术手段很清晰的表达了软件项目的问题和解决方法,那么这就是这个技术最大的好处,而状态机对于流程性、状态变化的场景,它就是一个清晰的表达方式,这就是它的好处。

3 多个状态机共存

在实际的应用中,不可能只有一个状态机流程在跑,比如订单,肯定是很多个订单在运行,每个订单都有自己的状态机流程。

如果我们想在使用时再创建状态机并详细配置,那么就可以使用StateMachineBuilder.builder()来动态创建

public abstract class FsmBuilder<JobStatusEnum, EventEnum> {
    protected StateMachineBuilder.Builder<JobStatusEnum, EventEnum> builder;

    public FsmBuilder() {
        builder = StateMachineBuilder.builder();
    }
}

其他多个状态机流程就可以继承上面的抽象类,多套状态机通过MACHINEID 来做区分。。在程序启动的时候,对多套状态机做初始化的配置,加载不同的状态节点、触发的事件、执行的动作、以及他们之间的流转关系。

4、状态转变时的参数传递---Message<EventEnum>

在具体的业务开发中,每个状态的转变中会涉及到各类业务,这些业务有些需要收到状态机变化的通知,需要把状态值传递给业务类和业务方法,同样的,在处理状态变化时,也需要获取业务数据,方便不同的业务在同一个状态变化环节做各自的业务,那这些业务数据在 spring statemachine 中如何传递呢? 

回答: 通过 spring.message,是 spring 通用的一种消息工具。

package org.springframework.messaging;

public interface Message<T> {

	/**
	 * Return the message payload.
	 */
	T getPayload();

	/**
	 * Return message headers for the message (never {@code null} but may be empty).
	 */
	MessageHeaders getHeaders();

}

在spring statemachine里面,我们把状态塞到message的payload里面,然后把需要传递的业务数据塞到header里面。创建message用的是messagebuilder,看它的名字就知道是专门创建message的。

            Message<EventEnum> message;
            message = MessageBuilder
                    .withPayload(context.getEventEnum())
                    .setHeader("jobContext", context)
                    .build();

状态机通过message对象就和其他的业务代码做到了数据连接。其实这个很关键,只有做到和其他业务的数据传递,才能算的上真正的可用。

 

5、业务抽象的例子

 

 

 

 

 

 

 

 

 

 

 

参考:

https://my.oschina.net/u/173343/blog/3043965

https://projects.spring.io/spring-statemachine/

 

 

 

 

 

 

 

 类似资料: