statemachine簡介及簡單應用

1.背景

1.1 spring statemachine是幹啥用的

有限狀態機(英語:finite-state machine,縮寫:FSM),簡稱狀態機,是表示有限個狀態以及在這些狀態之間的轉移和動作等行爲的數學模型。將狀態和事件控制從不同的業務Service方法的if else中抽離出來。FSM的應用範圍很廣,對於有複雜狀態流,擴展性要求比較高的場景都可以使用該模型。下面是狀態機模型中的4個要素,即現態、條件、動作、次態。

現態:是指當前所處的狀態。

條件:又稱爲“事件”。當一個條件被滿足,將會觸發一個動作,或者執行一次狀態的遷移。

動作:條件滿足後執行的動作。動作執行完畢後,可以遷移到新的狀態,也可以仍舊保持原狀態。動作不是必需的,當條件滿足後,也可以不執行任何動作,直接遷移到新狀態。

次態:條件滿足後要遷往的新狀態。“次態”是相對於“現態”而言的,“次態”一旦被激活,就轉變成新的“現態”了。

狀態機對於流程性、狀態變化的場景,它就是一個清晰的表達方式,這就是它的好處。

2.入門案例

使用maven項目,首先引入包

pom.xml

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.statemachine</groupId>
        <artifactId>spring-statemachine-core</artifactId>
        <version>1.2.0.RELEASE</version>
    </dependency>
</dependencies>

配置文件

StateMachineConfig.java

package com.huaquan.securityguard.config;

import com.huaquan.securityguard.enums.EventEnum;
import com.huaquan.securityguard.enums.StatusEnum;
import org.springframework.context.annotation.Configuration;
import org.springframework.statemachine.config.EnableStateMachine;
import org.springframework.statemachine.config.EnableStateMachineFactory;
import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter;
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;


import java.util.EnumSet;

/**
 * @author 陸傑
 * @since 0.1
 */
@Configuration
@EnableStateMachine
public class StateMachineConfig extends EnumStateMachineConfigurerAdapter<StatusEnum, EventEnum> {

    /**
     * 初始化狀態機狀態
     */
    @Override
    public void configure(StateMachineStateConfigurer<StatusEnum, EventEnum> states) throws Exception {
        states.withStates()
                // 定義初始狀態
                .initial(StatusEnum.UNCONNECTED)
                // 定義狀態機狀態
                .states(EnumSet.allOf(StatusEnum.class));
    }

    /**
     * 初始化狀態遷移事件
     */
    @Override
    public void configure(StateMachineTransitionConfigurer<StatusEnum, EventEnum> transitions)
            throws Exception {
        transitions
                // 1.連接事件
                // 未連接 -> 已連接
                .withExternal()
                .source(StatusEnum.UNCONNECTED)
                .target(StatusEnum.START)
                .event(EventEnum.CONNECT)
                .and()

                // 2.更新事件
                // 已連接 -> 更新中
                .withExternal()
                .source(StatusEnum.START)
                .target(StatusEnum.UPDATE)
                .event(EventEnum.UPDATE)
                .and()
                // 更新過期人員
                .withExternal()
                .source(StatusEnum.START)
                .target(StatusEnum.UPDATE_EXPER)
                .event(EventEnum.UPDATE_EXPER)
                .and()
                .withExternal()
                .source(StatusEnum.UPDATE_EXPER)
                .target(StatusEnum.END)
                .event(EventEnum.UPDATE_SUCCESS)
                .and()
                // 3.更新成功事件
                //更新中 -> 已完成
                .withExternal()
                .source(StatusEnum.UPDATE)
                .target(StatusEnum.END)
                .event(EventEnum.UPDATE_SUCCESS)
                .and()

                // 5.結束事件
                // 已完成 -> 未連接
                .withExternal()
                .source(StatusEnum.END)
                .target(StatusEnum.UNCONNECTED)
                .event(EventEnum.END)
        ;
    }
}

StateMachineEventConfig.java

package com.huaquan.securityguard.config;

import com.huaquan.securityguard.service.ISqliteService;

import com.huaquan.securityguard.service.impl.InfoService;
import com.huaquan.securityguard.util.IApiUtil;
import com.huaquan.securityguard.util.ISeverUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.statemachine.annotation.OnTransition;
import org.springframework.statemachine.annotation.WithStateMachine;

/**
 * 功能:狀態機
 * 2019年6月22日  v0.1
 *
 * @author 陸傑
 * @since 0.1
 */
@WithStateMachine
public class StateMachineEventConfig {

    Logger logger = LoggerFactory.getLogger(InfoService.class);
    @Autowired
    private ISqliteService iSqliteService;
    @Autowired
    private InfoService infoService;
    @Autowired
    private ISeverUtil iSeverUtil;
    @Autowired
    private IApiUtil iApiUtil;

    @OnTransition(source = "UNCONNECTED", target = "START")
    public void connect() {
        iApiUtil.login();
        logger.info("連接事件, 未連接 -> 已連接");
    }

    @OnTransition(source = "START", target = "UPDATE")
    public void update() throws InterruptedException {

        logger.info("UpdateIncrease----");
//        iSqliteService.insertLogViewWithStartTime(iSeverUtil.timeLtoS(System.currentTimeMillis()));
        infoService.UpdateTheFailMessage();
        infoService.UpdateFromRemoteDB();
        Thread.sleep(2000);
    }

    @OnTransition(source = "START", target = "UPDATE_EXPER")
    public void upExperson() throws InterruptedException {

        logger.info("UpdateEx----");
        infoService.UpdateExPerson();
        Thread.sleep(2000);
    }

    @OnTransition(source = "UPDATE", target = "END")
    public void updateSuccess() {
        logger.info("update success!");

    }

    @OnTransition(source = "END", target = "UNCONNECTED")
    public void unConnectSuccess() {
        logger.info("connect cancel");
    }

}

enums 枚舉,狀態機中表示狀態

EventEnum.java

public enum EventEnum {
    //未連接
    UNCONNECTED,
    // 連接
    CONNECT,
    // 更新
    UPDATE,
    // 更新成功
    UPDATE_SUCCESS,
    // 更新過期人員
    UPDATE_EXPER,
    // 更新失敗
    UPDATE_FAILED,
    // 註銷
    END;
}

StatusEnum.java

public enum StatusEnum {

    // 未連接
    UNCONNECTED,
    //開始
    START,
    // 更新中
    UPDATE,
    // 更新過期人員
    UPDATE_EXPER,
    // 結束
    END,
}

啓動狀態機

@Autowired
private StateMachine<StatusEnum, EventEnum> stateMachine;//引入狀態機
stateMachine.start();
stateMachine.sendEvent(EventEnum.CONNECT);
stateMachine.sendEvent(EventEnum.UPDATE);
stateMachine.sendEvent(EventEnum.UPDATE_SUCCESS);
stateMachine.sendEvent(EventEnum.END);
stateMachine.sendEvent(EventEnum.UNCONNECTED);

(1)在實際使用中,應該是有很多任務的流程在同時跑,而不是像這個例子,全部任務共用一個流程,一個訂單到EventEnum.UNCONNECTED狀態了,其他任務就不能是UPDATE狀態了。

(2)參數問題,我們做項目,不是爲了看日誌打出“---訂單創建,待支付---”給我們玩的,而是要處理具體業務的,拿訂單流程來說吧,訂單號怎麼傳,狀態改變時間怎麼回數據庫,等等問題其實都需要解決。

(3)存儲問題,狀態機如果有多個,需要的時候從哪去,暫時不需要了放哪去,這都是問題,所以存儲狀態機也是需要解決的。

3.多個狀態機共存

在實際項目中一般性都會有多個狀態機併發執行,在上面的例子中在程序中同時執行只能同時存在一個狀態機,如果想要多個狀態機併發就需要用到builer

StateMachineBuilder.Builder<OrderStates, OrderEvents> builder = StateMachineBuilder.builder();

OrderStateMachineBuilder.java

private final static String MACHINEID = "orderMachine";
public StateMachine<OrderStates, OrderEvents> build(BeanFactory beanFactory) throws Exception {
		 StateMachineBuilder.Builder<OrderStates, OrderEvents> builder = StateMachineBuilder.builder();
		 
		 System.out.println("構建訂單狀態機");
		 
		 builder.configureConfiguration()
		 		.withConfiguration()
                .machineId(MACHINEID)
		 		.beanFactory(beanFactory);
		 
		 builder.configureStates()
		 			.withStates()
		 			.initial(OrderStates.UNPAID)
		 			.states(EnumSet.allOf(OrderStates.class));
		 			
		 builder.configureTransitions()
					 .withExternal()
						.source(OrderStates.UNPAID).target(OrderStates.WAITING_FOR_RECEIVE)
						.event(OrderEvents.PAY).action(action())
						.and()
					.withExternal()
						.source(OrderStates.WAITING_FOR_RECEIVE).target(OrderStates.DONE)
						.event(OrderEvents.RECEIVE);
		 			
		 return builder.build();
	 }

其中MACHINEID指向EventConfig

private final static String MACHINEID = "orderMachine";
	 builder.configureConfiguration()
		 		.withConfiguration()
                .machineId(MACHINEID)
		 		.beanFactory(beanFactory);

當然爲了能調用到EventConfig,需要在EventConfig中註明它的名字,在1.x版本中不存在id這個字段,所以是無法關聯的

@WithStateMachine(id="orderMachine")

在WithStateMachine中有兩個字段

public @interface WithStateMachine {
    String name() default "stateMachine";

    String id() default "";
}

OrderEventConfig.java

@WithStateMachine(id="orderMachine")
public class OrderEventConfig {
private Logger logger = LoggerFactory.getLogger(getClass());
   
    /**
     * 當前狀態UNPAID
     */
    @OnTransition(target = "UNPAID")
    public void create() {
        logger.info("---訂單創建,待支付---");
    }
    
    /**
     * UNPAID->WAITING_FOR_RECEIVE 執行的動作
     */
    @OnTransition(source = "UNPAID", target = "WAITING_FOR_RECEIVE")
    public void pay(Message<OrderEvents> message) {
       System.out.println("傳遞的參數:" + message.getHeaders().get("order"));
        logger.info("---用戶完成支付,待收貨---");
    }
    
    /**
     * WAITING_FOR_RECEIVE->DONE 執行的動作
     */
    @OnTransition(source = "WAITING_FOR_RECEIVE", target = "DONE")
    public void receive(Message<OrderEvents> message) {
       System.out.println("傳遞的參數:" + message.getHeaders().get("order"));
       System.out.println("傳遞的參數:" + message.getHeaders().get("otherObj"));
        logger.info("---用戶已收貨,訂單完成---");
    }

}

調用狀態機代碼,在調用狀態機時候,現在每一次調用都會新建一個狀態機併發運行。

StateMachine<OrderStates, OrderEvents> stateMachine = orderStateMachineBuilder.build(beanFactory);
System.out.println(stateMachine.getId());

// 創建流程
stateMachine.start();

// 觸發PAY事件
stateMachine.sendEvent(OrderEvents.PAY);

// 觸發RECEIVE事件
//stateMachine.sendEvent(OrderEvents.RECEIVE);

//用message傳遞數據
Order order = new Order(orderId, String.valueOf(Math.random()), "華師大科技園", "13435465465", "RECEIVE");
Message<OrderEvents> message = MessageBuilder.withPayload(OrderEvents.RECEIVE).setHeader("order", order).setHeader("otherObj", "otherObjValue").build();
stateMachine.sendEvent(message);

// 獲取最終狀態
System.out.println("最終狀態:" + stateMachine.getState().getId());

調用結果,因爲這裏的狀態不是一個循環,如果不是併發創建新的狀態機是無法第二次執行打印狀態機執行的內容的。

2020-05-13 10:50:04.645  INFO 12176 --- [nio-9991-exec-1] tConfig$$EnhancerBySpringCGLIB$$928f5e35 : ---用戶完成支付,待收貨---
傳遞的參數:Order [id=null, userId=0.6601078768353066, address=華師大科技園, phoneNum=13435465465, state=RECEIVE]
傳遞的參數:otherObjValue
2020-05-13 10:50:04.648  INFO 12176 --- [nio-9991-exec-1] tConfig$$EnhancerBySpringCGLIB$$928f5e35 : ---用戶已收貨,訂單完成---
最終狀態:DONE
構建訂單狀態機
orderMachine
2020-05-13 10:50:09.395  INFO 12176 --- [nio-9991-exec-2] tConfig$$EnhancerBySpringCGLIB$$928f5e35 : ---訂單創建,待支付---
2020-05-13 10:50:09.396  INFO 12176 --- [nio-9991-exec-2] o.s.s.support.LifecycleObjectSupport     : started org.springframework.statemachine.support.DefaultStateMachineExecutor@321218cf
2020-05-13 10:50:09.396  INFO 12176 --- [nio-9991-exec-2] o.s.s.support.LifecycleObjectSupport     : started DONE UNPAID WAITING_FOR_RECEIVE  / UNPAID / uuid=e9b620a5-cfe1-4018-b0d0-f5c14046f760 / id=orderMachine
DefaultStateContext [stage=TRANSITION, message=GenericMessage [payload=PAY, headers={id=36d3ed81-88ce-8524-db84-a791a258b891, timestamp=1589338209396}], messageHeaders={id=5cb640ed-3d05-972e-a291-eb2a66377b66, _sm_id_=e9b620a5-cfe1-4018-b0d0-f5c14046f760, timestamp=1589338209396}, extendedState=DefaultExtendedState [variables={}], transition=AbstractTransition [source=ObjectState [getIds()=[UNPAID], getClass()=class org.springframework.statemachine.state.ObjectState, hashCode()=1159188520, toString()=AbstractState [id=UNPAID, pseudoState=org.springframework.statemachine.state.DefaultPseudoState@1546ee, deferred=[], entryActions=[], exitActions=[], stateActions=[], regions=[], submachine=null]], target=ObjectState [getIds()=[WAITING_FOR_RECEIVE], getClass()=class org.springframework.statemachine.state.ObjectState, hashCode()=836740284, toString()=AbstractState [id=WAITING_FOR_RECEIVE, pseudoState=null, deferred=[], entryActions=[], exitActions=[], stateActions=[], regions=[], submachine=null]], kind=EXTERNAL, guard=null], stateMachine=DONE UNPAID WAITING_FOR_RECEIVE  / UNPAID / uuid=e9b620a5-cfe1-4018-b0d0-f5c14046f760 / id=orderMachine, source=null, target=null, sources=null, targets=null, exception=null]
傳遞的參數:null
2020-05-13 10:50:09.397  INFO 12176 --- [nio-9991-exec-2] tConfig$$EnhancerBySpringCGLIB$$928f5e35 : ---用戶完成支付,待收貨---
傳遞的參數:Order [id=null, userId=0.5339466950886407, address=華師大科技園, phoneNum=13435465465, state=RECEIVE]
傳遞的參數:otherObjValue
2020-05-13 10:50:09.397  INFO 12176 --- [nio-9991-exec-2] tConfig$$EnhancerBySpringCGLIB$$928f5e35 : ---用戶已收貨,訂單完成---
最終狀態:DONE

4.多種狀態機共存

在實際需求中,一個程序可能有不同種類的狀態機,服務不同的需求所以往往需要多個狀態機,在實際操作中,我們只需要把創建狀態機的步驟再做一遍取不同的名字就可以在一個程序內創建不同的狀態機了。

在builder中根據需求各自創建config

private final static String MACHINEID = "orderMachine";

	public StateMachine<OrderStates, OrderEvents> build(BeanFactory beanFactory) throws Exception {
		 StateMachineBuilder.Builder<OrderStates, OrderEvents> builder = StateMachineBuilder.builder();
		 
		 System.out.println("構建訂單狀態機");
		 
		 builder.configureConfiguration()
		 		.withConfiguration()
		 		.machineId(MACHINEID)
		 		.beanFactory(beanFactory);
...省略
private final static String MACHINEID = "formMachine";

	public StateMachine<FormStates, FormEvents> build(BeanFactory beanFactory) throws Exception {
		 StateMachineBuilder.Builder<FormStates, FormEvents> builder = StateMachineBuilder.builder();
		 
		 System.out.println("構建表單狀態機");
		 
		 builder.configureConfiguration()
		 		.withConfiguration()
		 		.machineId(MACHINEID)
		 		.beanFactory(beanFactory);
...省略

各自聲明對應的eventConfig

@WithStateMachine(id="orderMachine")
public class OrderEventConfig {
...省略
@WithStateMachine(id="formMachine")
public class FormEventConfig {

分別執行不同的狀態機

@RequestMapping("/testOrderState")
	public void testOrderState(String orderId) throws Exception {

		StateMachine<OrderStates, OrderEvents> stateMachine = orderStateMachineBuilder.build(beanFactory);
		System.out.println(stateMachine.getId());

		// 創建流程
		stateMachine.start();

		// 觸發PAY事件
		stateMachine.sendEvent(OrderEvents.PAY);

		// 觸發RECEIVE事件
		stateMachine.sendEvent(OrderEvents.RECEIVE);


		// 獲取最終狀態
		System.out.println("最終狀態:" + stateMachine.getState().getId());
	}
	
	@RequestMapping("/testFormState")
	public void testFormState() throws Exception {

		StateMachine<FormStates, FormEvents> stateMachine = formStateMachineBuilder.build(beanFactory);
		System.out.println(stateMachine.getId());

		// 創建流程
		stateMachine.start();

		stateMachine.sendEvent(FormEvents.WRITE);

		stateMachine.sendEvent(FormEvents.CONFIRM);

		stateMachine.sendEvent(FormEvents.SUBMIT);

		// 獲取最終狀態
		System.out.println("最終狀態:" + stateMachine.getState().getId());
	}

5.傳遞參數的Message

在企業開發中,數據在不同的業務間傳輸是最常見的工作,所以雖然我們的主架構是用的狀態機,也就是從流程狀態的角度來看待這個項目,但在具體業務中,每個狀態的轉變中會牽涉到各類業務,這些業務有些需要收到狀態機變化的通知,需要把狀態值傳遞給業務類和業務方法,同樣的,在處理狀態變化是,也需要獲取業務數據,方便不同的業務在同一個狀態變化環節做各自的業務,下面我們就講下這個數據在spring statemachine裏面的傳遞。

 

通過message傳遞,message只有兩部分,header和payload

public interface Message<T> {
    T getPayload();

    MessageHeaders getHeaders();
}

傳參數,在調用狀態機時候,在message中可以傳遞對象,或者基礎數據類型,在payload中裝載狀態即可

Message<OrderEvents> message = MessageBuilder.withPayload(OrderEvents.PAY).setHeader("order", order).setHeader("otherObj", "otherObjValue").build();
stateMachine.sendEvent(message);

與普通不裝消息的設置狀態對比

stateMachine.sendEvent(OrderEvents.PAY);

在接收端,即EventConfig中,方法中添加參數Message<OrderEvents> message,用於獲取發過來的message

@OnTransition(source = "WAITING_FOR_RECEIVE", target = "DONE")
public void receive(Message<OrderEvents> message) {
   System.out.println("傳遞的參數:" + message.getHeaders().get("order"));
   System.out.println("傳遞的參數:" + message.getHeaders().get("otherObj"));
    logger.info("---用戶已收貨,訂單完成---");
}

6.Statemachine持久化

持久化操作在實際業務中經常遇到,有時候需要對一個任務隔天處理,或者再久一點等到需要的時候再拿出來,這時候就要用到持久化操作。

6.1持久化在本地內存中(Map保存)

用唯一id作爲key,把狀態機保存到map表裏面,在實際的業務中,自由存取

public class MachineMap {
	public static Map<String,StateMachine<OrderStates, OrderEvents>> orderMap = new HashMap<String,StateMachine<OrderStates, OrderEvents>>();
	public static Map<String,StateMachine<FormStates, FormEvents>> formMap = new HashMap<String,StateMachine<FormStates, FormEvents>>();

}

關於StateMachinePersist和StateMachinePersister接口:

這兩個接口名字很類似,很容易搞混,但下面的是有er的,包名也不同的。StateMachinePersister是可以直接保存StateMachine對象的,對於StateMachinePersist首先要實現它,然後再一個Config類裏面轉換成下面的StateMachinePersister,轉換的代碼就在上面的PersistConfig類裏。

 

StateMachinePersist.class和StateMachinePersister.class對比

package org.springframework.statemachine;
public interface StateMachinePersist<S, E, T> {
	void write(StateMachineContext<S, E> context, T contextObj) throws Exception;
	StateMachineContext<S, E> read(T contextObj) throws Exception;
}
package org.springframework.statemachine.persist;
import org.springframework.statemachine.StateMachine;
public interface StateMachinePersister<S, E, T> {
	void persist(StateMachine<S, E> stateMachine, T contextObj) throws Exception;
	StateMachine<S, E> restore(StateMachine<S, E> stateMachine, T contextObj) throws Exception;
}

 

上述描述如何使用StateMachinePersist可能有點模糊,以下爲具體步驟:

Step1:StateMachinePersist

@Component
public class InMemoryStateMachinePersist implements StateMachinePersist<OrderStates, OrderEvents, String> {

   private Map<String, StateMachineContext<OrderStates, OrderEvents>> map = new HashMap<String, StateMachineContext<OrderStates,OrderEvents>>();
   
   @Override
   public void write(StateMachineContext<OrderStates, OrderEvents> context, String contextObj) throws Exception {
      map.put(contextObj, context);
   }

   @Override
   public StateMachineContext<OrderStates, OrderEvents> read(String contextObj) throws Exception {
      return map.get(contextObj);
   }

}

Step2:然後在 PersistConfig 中 注入:InMemoryStateMachinePersist

@Configuration
public class PersistConfig {
   
   @Autowired
   private InMemoryStateMachinePersist inMemoryStateMachinePersist;

   /**
    * 注入StateMachinePersister對象
    * 
    * @return
    */
   @Bean(name="orderMemoryPersister")
   public StateMachinePersister<OrderStates, OrderEvents, String> getPersister() {
      return new DefaultStateMachinePersister<>(inMemoryStateMachinePersist);
   }
   

}

Step3: 調用時根據bean注入

@Resource(name="orderMemoryPersister")
private StateMachinePersister<OrderStates, OrderEvents, String> orderMemorypersister;

...

StateMachine<OrderStates, OrderEvents> stateMachine = orderStateMachineBuilder.build(beanFactory);
stateMachine.start();

//發送PAY事件
stateMachine.sendEvent(OrderEvents.PAY);
Order order = new Order();
order.setId(id);

//持久化stateMachine
orderMemorypersister.persist(stateMachine, order.getId());

...

//取數據
StateMachine<OrderStates, OrderEvents> stateMachine = orderStateMachineBuilder.build(beanFactory);
orderMemorypersister.restore(stateMachine, id);
System.out.println("恢復狀態機後的狀態爲:" + stateMachine.getState().getId());

6.2持久化到redis

pom.xml 依賴

<!-- redis持久化狀態機 -->
		<dependency>
			<groupId>org.springframework.statemachine</groupId>
			<artifactId>spring-statemachine-redis</artifactId>
			<version>1.2.9.RELEASE</version>
		</dependency>

redis配置

# REDIS (RedisProperties)
# Redis數據庫索引(默認爲0)
spring.redis.database=0
# Redis服務器地址
spring.redis.host=localhost
# Redis服務器連接端口
spring.redis.port=6379
# Redis服務器連接密碼(默認爲空)
spring.redis.password=
# 連接池最大連接數(使用負值表示沒有限制)
spring.redis.pool.max-active=8
# 連接池最大阻塞等待時間(使用負值表示沒有限制)
spring.redis.pool.max-wait=-1
# 連接池中的最大空閒連接
spring.redis.pool.max-idle=8
# 連接池中的最小空閒連接
spring.redis.pool.min-idle=0
# 連接超時時間(毫秒)
spring.redis.timeout=0

跟上面map存儲一樣,在配置文件內配置redis信息

PersistConfig.java

@Autowired
private RedisConnectionFactory redisConnectionFactory;

...

/**
 * 注入RedisStateMachinePersister對象
 * 
 * @return
 */
@Bean(name = "orderRedisPersister")
public RedisStateMachinePersister<OrderStates, OrderEvents> redisPersister() {
   return new RedisStateMachinePersister<>(redisPersist());
}

/**
 * 通過redisConnectionFactory創建StateMachinePersist
 * 
 * @return
 */
public StateMachinePersist<OrderStates, OrderEvents,String> redisPersist() {
   RedisStateMachineContextRepository<OrderStates, OrderEvents> repository =
         new RedisStateMachineContextRepository<>(redisConnectionFactory);
   return new RepositoryStateMachinePersist<>(repository);
}

調用

@RequestMapping("/testRedisPersister")
public void testRedisPersister(String id) throws Exception {
   StateMachine<OrderStates, OrderEvents> stateMachine = orderStateMachineBuilder.build(beanFactory);
   stateMachine.start();
   Order order = new Order();
   order.setId(id);
   //發送PAY事件
   Message<OrderEvents> message = MessageBuilder.withPayload(OrderEvents.PAY).setHeader("order", order).build();
   stateMachine.sendEvent(message);
   //持久化stateMachine
   orderRedisPersister.persist(stateMachine, order.getId());
}

@RequestMapping("/testRedisPersisterRestore")
public void testRestore(String id) throws Exception {
   StateMachine<OrderStates, OrderEvents> stateMachine = orderStateMachineBuilder.build(beanFactory);
   orderRedisPersister.restore(stateMachine, id);
   System.out.println("恢復狀態機後的狀態爲:" + stateMachine.getState().getId());
}

7.statement僞持久化和中間段狀態機

在實際的企業開發中,不可能所有情況都是從頭到尾的按狀態流程來,會有很多意外,比如歷史數據,故障重啓後的遺留流程......,所以這種可以任意調節狀態的纔是我們需要的狀態機。

簡而言之就是直接拿到狀態機的一箇中間過成,並可以繼續執行。

 

回到persisit的實現類,既然目的是爲了拿到一箇中間段的狀態機,那麼就沒有必要存當前狀態了

@Component
public class OrderStateMachinePersist implements StateMachinePersist<OrderStates, OrderEvents, Order> {

	@Override
	public void write(StateMachineContext<OrderStates, OrderEvents> context, Order contextObj) throws Exception {
		//這裏不做任何持久化工作
	}

	@Override
	public StateMachineContext<OrderStates, OrderEvents> read(Order contextObj) throws Exception {
		StateMachineContext<OrderStates, OrderEvents> result = new DefaultStateMachineContext<OrderStates, OrderEvents>(OrderStates.valueOf(contextObj.getState()), 
				null, null, null, null, "orderMachine");
		return result;
	}
}

跟之前的步驟一樣把persist注入到config文件中

@Configuration
public class PersistConfig {
@Autowired
    private OrderStateMachinePersist orderStateMachinePersist;
@Bean(name="orderPersister")
    public StateMachinePersister<OrderStates, OrderEvents, Order> orderPersister() {
		return new DefaultStateMachinePersister<OrderStates, OrderEvents, Order>(orderStateMachinePersist);
	}
}

在執行的地方用通過persisiter拿到操作類

@RestController
@RequestMapping("/statemachine")
public class StateMachineController {

    @Resource(name="orderPersister")
	private StateMachinePersister<OrderStates, OrderEvents, Order> persister;
    
    @RequestMapping("/testOrderRestore")
    public void testOrderRestore(String id) throws Exception {
       StateMachine<OrderStates, OrderEvents> stateMachine = orderStateMachineBuilder.build(beanFactory);
       //訂單
       Order order = new Order();
       order.setId(id);
       order.setState(OrderStates.WAITING_FOR_RECEIVE.toString());
       //恢復
       persister.restore(stateMachine, order);
       System.out.println("恢復後的狀態:" + stateMachine.getState().getId());
       stateMachine.sendEvent(OrderEvents.RECEIVE);
       //查看恢復後狀態機的狀態
       System.out.println("執行下一步後的狀態:" + stateMachine.getState().getId());
    }
}
構建訂單狀態機
恢復後的狀態:WAITING_FOR_RECEIVE
傳遞的參數:null
傳遞的參數:null
執行下一步後的狀態:DONE

 

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