1. 程式人生 > >通過spring statemmachine 自定義構建屬於自己的狀態機(兩種方式)

通過spring statemmachine 自定義構建屬於自己的狀態機(兩種方式)

  spring 的stateMachine 相對於當前的版本,還是比較新穎的,但是對於合適的業務場景,使用起來還是十分的方便的。但是對於官網提供的文件,講解的是十分的精簡,要想更深入的瞭解其內部架構,只有不斷的測試,檢視內部原始碼的實現,能夠大幅度的給你更大的啟發!在今天,小編將介紹如何不通過使用官網的方式構建狀態機,實現自己的業務邏輯:

 首先,這裡為了配置方便構建,建立業務所需要的entity配置類,

package statemachine.v2.entity;

public class ConfigEntity {
    /**
     * 業務 id 號
     */
    private int id;
    /**
     * 源狀態
     */
    private String source;
    /**
     * 目標狀態
     */
    private String target;
    /**
     * 觸發的事件
     */
    private String event;
    /**
     * 備註資訊
     */
    private String info;

    /**
     * 業務型別
     */
    private int type;

    public ConfigEntity(int id, String source, String target, String event, String info, int type) {
        this.id = id;
        this.source = source;
        this.target = target;
        this.event = event;
        this.info = info;
        this.type = type;
    }

    public ConfigEntity(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getSource() {
        return source;
    }

    public void setSource(String source) {
        this.source = source;
    }

    public String getTarget() {
        return target;
    }

    public void setTarget(String target) {
        this.target = target;
    }

    public String getEvent() {
        return event;
    }

    public void setEvent(String event) {
        this.event = event;
    }

    public String getInfo() {
        return info;
    }

    public void setInfo(String info) {
        this.info = info;
    }

    public int getType() {
        return type;
    }

    public void setType(int type) {
        this.type = type;
    }

    @Override
    public String toString() {
        return "ConfigEntity{" +
                "id=" + id +
                ", source='" + source + '\'' +
                ", target='" + target + '\'' +
                ", event='" + event + '\'' +
                ", info='" + info + '\'' +
                ", type=" + type +
                '}';
    }
}
   然後構建自己的配置資訊類,構造相關的配置資訊。
package statemachine.v2.config;

import org.springframework.statemachine.config.model.*;
import org.springframework.statemachine.state.PseudoStateKind;
import statemachine.v2.entity.ConfigEntity;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

/**
 * 配置必要的配置資訊
 */
public class SSMConfig {
    private static final HashSet<String> states = new HashSet<String>();
    private static final HashSet<ConfigEntity> configEntities = new HashSet<ConfigEntity>();

    public static final StateData<String, String> initState = new StateData<String, String>("初始狀態" ,true);
    public static final StateData<String, String> endState = new StateData<String, String>("結束狀態");

    public static HashSet <String> getStates() {
        return states;
    }

    public static HashSet <ConfigEntity> getConfigEntities() {
        return configEntities;
    }

    /**
     * 配置的構造方法
     */
    static {
        //構造配置資訊列表,這個可以根據業務實際需求設定,可自定義
        Set<ConfigEntity> configEntities = new HashSet <ConfigEntity>(Arrays.asList(
                new ConfigEntity(1,"初始狀態","狀態1","事件1","",001),
                new ConfigEntity(1,"狀態1","狀態2","事件2","",001),
                new ConfigEntity(1,"狀態2","狀態1","事件3","",001),
                new ConfigEntity(1,"狀態2","結束狀態","事件4","",001)));
        for(ConfigEntity configEntity : configEntities){
            states.add(configEntity.getSource());
            configEntities.add(configEntity);
        }
    }


    /**
     * 構建 ConfigurationData,在這一步也可以構建為分散式的,如基於zookeeper
     * @return
     */
    public static ConfigurationData<String,String> getConfigurationData(){
        ConfigurationData<String, String> configurationData = new ConfigurationData<String, String>();
        return configurationData;
    }

    /**
     * 構建狀態資料資訊物件, 這一步是構建狀態機的各個狀態欄位,用於裝載狀態機的狀態轉換之間的狀態配置
     * @return
     */
    public static StatesData<String,String> getStatesData(){
        HashSet<StateData<String, String>> stateDatas = new HashSet<StateData<String, String>>();
        //初始狀態
        initState.setPseudoStateKind(PseudoStateKind.INITIAL);
        stateDatas.add(initState);

        //結束狀態
        endState.setEnd(true);
        endState.setPseudoStateKind(PseudoStateKind.END);
        stateDatas.add(endState);

        //其他狀態載入
        for (String state: states){
            StateData<String, String> stateData = new StateData<String, String>(state);
            stateDatas.add(stateData);
        }

        //構建
        StatesData<String, String> statesData = new StatesData<String, String>(stateDatas);

        return statesData;
    }

    /**
     * 狀態事物轉換的流程配置
     * @return
     */
    public static TransitionsData<String,String> getTransitionsData(){
        HashSet<TransitionData<String,String>> transitionDatas = new HashSet<TransitionData<String,String>>();
        for (ConfigEntity configEntity : configEntities ){

            TransitionData<String,String> transitionData = new TransitionData<String,String>(configEntity.getSource(),
                    configEntity.getTarget(),
                    configEntity.getEvent()
            );
            transitionDatas.add(transitionData);

        }
        TransitionsData<String,String> transitionsData = new TransitionsData<String,String>(transitionDatas);

        return transitionsData;
    }

}

  最後通過以上的資訊,構建狀態機的類,通過該類來建立狀態機,獲取狀態機的例項:這裡提供兩種方式構建,大家可以根據自己的業務自行選擇:

package statemachine.v2.config;


import org.springframework.beans.factory.support.StaticListableBeanFactory;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.config.ObjectStateMachineFactory;
import org.springframework.statemachine.config.StateMachineBuilder;
import org.springframework.statemachine.config.model.ConfigurationData;
import org.springframework.statemachine.config.model.DefaultStateMachineModel;
import org.springframework.statemachine.config.model.StatesData;
import org.springframework.statemachine.config.model.TransitionsData;
import statemachine.v2.entity.ConfigEntity;

import java.util.Collection;
import java.util.HashSet;

public class MakeStateMachine {
    /**
     * 構建狀態機,方式一
     * @return
     * @throws Exception
     */
    public static StateMachine<String,String> createStateMachine() throws Exception {
        ConfigurationData<String, String> configData = SSMConfig.getConfigurationData();
        StatesData<String, String> statesData = SSMConfig.getStatesData();
        TransitionsData<String, String> transitionsData = SSMConfig.getTransitionsData();
        DefaultStateMachineModel<String,String> machineModel = new DefaultStateMachineModel<String, String>(configData,statesData,transitionsData);
        ObjectStateMachineFactory<String, String> machineFactory = new ObjectStateMachineFactory<String, String>(machineModel);
        StateMachine<String, String> stateMachine  = machineFactory.getStateMachine();
        //新增狀態機的監聽器,自行實現
//        stateMachine.addStateListener(new StateMachineListener <String, String>() {});

        //新增狀態機的攔截器,自行實現內部介面即可
//        stateMachine.getStateMachineAccessor()
//                         .withRegion()
//                         .addStateMachineInterceptor(new StateMachineInterceptor <String, String>() {});
        return stateMachine;
    }

    /**
     * 構建狀態機,方式二
     */
    public  StateMachine<String,String> getStateMachine() throws Exception {
        StateMachineBuilder.Builder<String,String> builder = StateMachineBuilder.builder();
        builder.configureConfiguration()
                .withConfiguration()
                //新增狀態機監聽器
//                .listener(new StateMachineListener <String, String>() {})
                .beanFactory(new StaticListableBeanFactory());//新增構建bean的工廠類,可以自行實現,這裡是使用系統的預設

        Collection<ConfigEntity> data = SSMConfig.getConfigEntities();
        HashSet<String> states = new HashSet<String>();
        for (ConfigEntity configEntity : data) {
            states.add(configEntity.getTarget());
            builder.configureTransitions()
                    .withExternal()
                    .source(configEntity.getSource())
                    .target(configEntity.getTarget())
                    .event(configEntity.getEvent());
        }

        builder.configureStates()
                .withStates()
                .initial(SSMConfig.initState.getState())
                .state(SSMConfig.initState.getState())
                .end(SSMConfig.endState.getState())
                .states(states);

        return builder.build();
    }

}

  使用的話,可以像之前的版本方式使用即可,詳細可以參考:初識狀態機

public class Main {

    public static void main(String[] args) throws Exception {

        StateMachine<String, String> stateMachine = MakeStateMachine.createStateMachine();
        //方式一, 傳送觸發事件,改變狀態
        stateMachine.sendEvent("事件1");

        //方式二, 傳送觸發事件,改變狀態
        stateMachine.sendEvent(MessageBuilder
                .withPayload("事件1")
                .setHeader("testStateMachine", "測試頭部") // header中可以存放相關資料資訊,
                // 這些資訊,在執行過程中,可以在監聽器和攔截器中獲取到,通過攔截器你可以在做額外的一些事情
                .build());
        
    }
}