欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

springboot 有限状态机入门指南

程序员文章站 2024-03-19 18:22:46
...

为什么要使用状态机?

如果你写过很复杂的流程系统,流程系统中涉及多步操作,流程达到不同的状态需要有不同的处理,同时状态间的转换也是有特定逻辑的。如果不使用状态机,那么你的代码我估计会有大量的if判断语句,你得判断某个操作指令过来了,当前这个状态是否能执行该指令。当程序需要异常终止,你需要在本不该对数据库进行操作的状态对数据库进行操作,进而导致到处都在操作数据库,一旦数据库出现数据异常,很难确定时哪里出现了问题。给修改bug带来了极大的困难。如果你有上面的困惑,那么我建议你修改代码模式,如果你的代码中存在大量的if elseif,你可以用反射,也可以用策略模式或者状态模式。如果你的系统中很明显就是一个状态流传的逻辑,那么我强烈建议你使用有限状态机重构自己的代码。有限状态机可以解决上面的问题。

什么是状态机?

从我的实际经验来讲,有限状态机可以看作是有向有环图。有向,因为状态一般是以一个状态到另一个状态;有环,因为流程很有可能出现失败然后重新定位到前面某个状态节点。如下所示:
springboot 有限状态机入门指南

为什么说有限状态机能解决去掉大量if判断的问题?

在有限自动机中,状态的转换被预先设定好。以上图为例,按照流程化的编写代码,原本T1状态是只能转移到T2状态的,如果此时来了一个Command要求当前状态转为S2状态,传统的写法只能是加判断条件,判断该指令是否合法,满足我业务流程,不满足就抛弃。这样就使得代码中会嵌入大量的状态-指令契合的条件判断语句。如果使用有限状态机,预先定义好状态之间的转化流程,当一个非法命令来到某个状态的执行部分,当前状态并没有为该命令定义处理方法,那么该命令自然就被舍弃掉了。在流程比较复杂的系统中,这样的优势更为明显。

有限状态机的实现原理是什么呢?

可以利用栈这种数据结构手写一个有限状态机。通过把状态的入栈和出栈操作定义为状态的转化过程,栈顶状态为当前自动机状态流程的状态,通过压栈和出栈的方式实现有限自动机的流程控制。不过,手写的有限状态机要上生产线还是比较麻烦,例如如何解决状态持久化问题等等。

我使用的是springboot,有现成的框架供我使用吗?
答案是肯定的。springboot为spring使用开发人员提供了StateMachine这个有限状态自动机,官方文档传送门

接下来让我详细为大家介绍一下如何简单上手StateMachine(详细使用参照官方文档)

引入maven依赖
<dependency>
    <groupId>org.springframework.statemachine</groupId>
    <artifactId>spring-statemachine-starter</artifactId>
    <version>2.2.0.RELEASE</version>
</dependency>
定义状态和事件的枚举类型
public enum Events {
    EVENT1, EVENT2
}

public enum States {
    SI, STATE1, STATE2
}
自定义配置文件
@Configuration
@EnableStateMachine
@Slf4j
public class StateMachineConfig extends EnumStateMachineConfigurerAdapter<States, Events> {

    @Bean
    public StateMachineListener<States,Events> listener() {
        return new StateMachineListenerAdapter<States,Events>(){
            @Override
            public void stateChanged(State<States,Events> from,State<States,Events> to){
                log.info("State change to {}",to.getId());
            }
        };
    }

    @Override
    public void configure(StateMachineConfigurationConfigurer<States,Events> config) throws Exception {
        config.withConfiguration()
                .autoStartup(true)
                .listener(listener());
    }

    @Override
    public void configure(StateMachineStateConfigurer<States,Events> states) throws Exception {
        states.withStates()
                .initial(States.SI)
                .states(EnumSet.allOf(States.class));
    }

    @Override
    public void configure(StateMachineTransitionConfigurer<States,Events> transitions) throws Exception {
        transitions
                .withExternal().source(States.SI).target(States.STATE1).event(Events.EVENT1).action(action())
                .and()
                .withExternal().source(States.STATE1).target(States.STATE2).event(Events.EVENT2).action(action());
    }

    @Bean
    public Action<States,Events> action(){
        return new Action<States, Events>() {
            @Override
            public void execute(StateContext<States, Events> stateContext) {
                log.info("from {} to {}",stateContext.getSource().getId(),stateContext.getTarget().getId());
            }
        };
    }
}
修改Application主类,实现CommandLineRunner接口
@EnableAutoConfiguration
@Configuration
@ComponentScan({"com.chaojilaji.workorderservice"})
public class WorkorderserviceApplication implements CommandLineRunner {


    @Autowired
    private StateMachine<States, Events> stateMachine;

    public static void main(String[] args) {
        SpringApplication.run(WorkorderserviceApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        stateMachine.sendEvent(Events.EVENT1);
        stateMachine.sendEvent(Events.EVENT2);
    }
}
运行代码,可以看见如下输出:

springboot 有限状态机入门指南

官方文档关于这些配置的因果有详细的描述,再次给出官方文档地址:
https://docs.spring.io/spring-statemachine/docs/2.2.x-SNAPSHOT/reference/