通过实例学习SpringStateMachine之TURN STILE

背景介绍

本系列通过学习SpringStateMachine中附带的10余个Sample来学习SpringStateMachine中的各个概念和用法。项目是使用的分支为2.2.0.RELEASE[1]。项目参考文档也是2.2.0.RELEASE[1]。

TURN STILE简介

turnstile是对体育场入口或地铁入口的旋转栅门构建的状态机。

状态机的两种状态:

  • LOCKED
  • UNLOCKED

状态机的两种事件

  • COIN
  • PUSH

触发相应事件后发生状态转换。
在这里插入图片描述

TURN STILE 依赖

项目在实现上述功能时,需要依赖springshell,官方给出的demo[4]使用了spring-shell1.2,本文将其改为spring-shell 2.0.0.RELEASE。

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

        <dependency>
            <groupId>org.springframework.shell</groupId>
            <artifactId>spring-shell-starter</artifactId>
            <version>2.0.0.RELEASE</version>
        </dependency>

TURN STILE 实现

为了实现本例,我们需要描述状态及其转换。首先定义状态与事件枚举类型。

状态枚举类型:

public enum States {
    LOCKED, UNLOCKED
}

使状态发生变化的事件枚举类型:

public enum Events {
    COIN, PUSH
}

接着我们配置状态与转换。

package springboot.statemachine.example.turnstile.demo;

import org.springframework.context.annotation.Configuration;
import org.springframework.statemachine.config.EnableStateMachine;
import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter;
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;

import java.util.EnumSet;

@Configuration
@EnableStateMachine
public class StateMachineConfig
        extends EnumStateMachineConfigurerAdapter<States, Events> {

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

    @Override
    public void configure(StateMachineTransitionConfigurer<States, Events> transitions)
            throws Exception {
                transitions
                .withExternal()
                .source(States.LOCKED)
                .target(States.UNLOCKED)
                .event(Events.COIN)
                .and()
                .withExternal()
                .source(States.UNLOCKED)
                .target(States.LOCKED)
                .event(Events.PUSH);
    }

}

在配置状态与转换时用到如下的注解与类:

  • @Configuration
  • @EnableStateMachine
  • EnumStateMachineConfigurerAdapter

代码继承EnumStateMachineConfigurerAdapter类,并覆盖两个configure方法,在这两个方法中分别来配置转换与状态列表同时指定好初始状态。

随后在创建的类上增加@EnableStateMachine与@Configuration注解,通过这些创建状态机实例,系统随后会检测是否使用了adapter类,并在运行时根据这些配置修改状态机。

statemachine中有三种形式的转换(transition)external, internal, local。
这里我们使用了withExternal,返回一个ExternalTransitionConfigurer来完成转换的配置。target方法指定了目标状态,source指定了源状态,event指定了使状态发生变更的事件。

最后是命令实现。这里对StateMachineCommands官方demo[5]进行了小修改。最后通过stateMachine的sendEvent方法发送事件,使状态机状态发生变化。

import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
import springboot.statemachine.example.AbstractStateMachineCommands;


@ShellComponent
public class StateMachineCommands extends AbstractStateMachineCommands<States, Events> {

    @ShellMethod(key = "sm event", value = "Sends an event to a state machine")
    public String event(Events event) {
        getStateMachine().sendEvent(event);
        return "Event " + event + " send";
    }
}

此外将官方AbstractStateMachineCommands[6]中打印turn stile字符图形的部分去掉了。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.state.State;
import org.springframework.util.StringUtils;

import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Set;


public class AbstractStateMachineCommands<S, E>{

    @Autowired
    private StateMachine<S, E> stateMachine;

    protected StateMachine<S, E> getStateMachine() {
        return stateMachine;
    }

    @ShellMethod(key = "sm state", value = "Prints current state")
    public String state() {
        State<S, E> state = stateMachine.getState();
        if (state != null) {
            return StringUtils.collectionToCommaDelimitedString(state.getIds());
        } else {
            return "No state";
        }
    }

    @ShellMethod(key = "sm start", value = "Start a state machine")
    public String start() {
        stateMachine.start();
        return "State machine started";
    }

    @ShellMethod(key = "sm stop", value = "Stop a state machine")
    public String stop() {
        stateMachine.stop();
        return "State machine stopped";
    }


    @ShellMethod(key = "sm variables", value = "Prints extended state variables")
    public String variables() {
        StringBuilder buf = new StringBuilder();
        Set<Entry<Object, Object>> entrySet = stateMachine.getExtendedState().getVariables().entrySet();
        Iterator<Entry<Object, Object>> iterator = entrySet.iterator();
        if (entrySet.size() > 0) {
            while (iterator.hasNext()) {
                Entry<Object, Object> e = iterator.next();
                buf.append(e.getKey() + "=" + e.getValue());
                if (iterator.hasNext()) {
                    buf.append("\n");
                }
            }
        } else {
            buf.append("No variables");
        }
        return buf.toString();
    }

}

验证

发送不同的命令触发事件,并查看状态机当前状态,发现状态正确发生变化。

State machine started
shell:>sm event COIN
Event COIN send
shell:>sm event PUSH
Event PUSH send
shell:>sm start
State machine started
shell:>sm state
LOCKED
shell:>sm event COIN
Event COIN send
shell:>sm state
UNLOCKED
shell:>sm event PUSH
Event PUSH send
shell:>sm state
LOCKED
shell:>sm event PUSH
Event PUSH send
shell:>sm state
LOCKED
shell:>sm state COIN
LOCKED
shell:>sm state
LOCKED
shell:>sm start
State machine started
shell:>sm state
LOCKED
shell:>sm stop

总结

通过turnstile这个例子学习了基础的概念及相关配置。通过

  • @Configuration,
  • @EnableStateMachine,
  • EnumStateMachineConfigurerAdapter

完成配置及创建单实例。实现触发事件使状态机当前状态发生变化,从源状态到目标状态变化。

参考

[1]2.2.0.RELEASE source,https://github.com/spring-projects/spring-statemachine/blob/2.2.0.RELEASE/
[2]2.2.0.RELEASE/reference,https://docs.spring.io/spring-statemachine/docs/2.2.0.RELEASE/reference
[3]turnstile,https://docs.spring.io/spring-statemachine/docs/2.2.0.RELEASE/reference/#statemachine-examples-turnstile
[4]turnstile demo,https://github.com/spring-projects/spring-statemachine/tree/2.2.0.RELEASE/spring-statemachine-samples/turnstile/src/main/java/demo/turnstile
[5]StateMachineCommands,https://github.com/spring-projects/spring-statemachine/blob/2.2.0.RELEASE/spring-statemachine-samples/turnstile/src/main/java/demo/turnstile/StateMachineCommands.java
[6]AbstractStateMachineCommands,https://github.com/spring-projects/spring-statemachine/blob/2.2.0.RELEASE/spring-statemachine-samples/src/main/java/demo/AbstractStateMachineCommands.java

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章