在上一章的例子中,咱們實現了多個狀態機並存執行,不一樣的訂單有各自的狀態機運行,但只有一種狀態機,這顯然不能知足實際業務的要求,好比我就遇到了訂單流程和公文審批流程在同一個項目的狀況,因此咱們這一章講怎麼讓多種狀態機共存。java
咱們先把上一章的例子狀態機再複習一下,這是個訂單狀態機,流程圖以下: ![](https://oscimg.oschina.net/oscnet/e60cfa4b1956ed1863632b34b7f0d7a60ff.jpg)
定義這個狀態機咱們用到了OrderEvents,OrderStates來表達狀態(states)和事件(events),用OrderStateMachineBuilder來描述初始狀態和狀態變化流程,用OrderEventConfig來描述這個流程和狀態變化過程當中須要作的業務。git
如今咱們再弄一個新的狀態機流程,表單狀態機,流程圖以下:spring
爲此,咱們一樣配套了和訂單狀態機同樣的表單四件套,events,states,StateMachineBuilder和eventConfig。app
public enum FormStates {ui
BLANK_FORM, // 空白表單 FULL_FORM, // 填寫完表單 CONFIRM_FORM, // 校驗表單 SUCCESS_FORM// 成功表單
}spa
public enum FormEvents {.net
WRITE, // 填寫 CONFIRM, // 校驗 SUBMIT // 提交
}日誌
import java.util.EnumSet;code
import org.springframework.beans.factory.BeanFactory;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.config.StateMachineBuilder;
import org.springframework.stereotype.Component;orm
/**
*/
@Component
public class FormStateMachineBuilder {
private final static String MACHINEID = "formMachine"; /** * 構建狀態機 * * @param beanFactory * @return * @throws Exception */ 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); builder.configureStates() .withStates() .initial(FormStates.BLANK_FORM) .states(EnumSet.allOf(FormStates.class)); builder.configureTransitions() .withExternal() .source(FormStates.BLANK_FORM).target(FormStates.FULL_FORM) .event(FormEvents.WRITE) .and() .withExternal() .source(FormStates.FULL_FORM).target(FormStates.CONFIRM_FORM) .event(FormEvents.CONFIRM) .and() .withExternal() .source(FormStates.CONFIRM_FORM).target(FormStates.SUCCESS_FORM) .event(FormEvents.SUBMIT); return builder.build(); }
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.messaging.Message;
import org.springframework.statemachine.annotation.OnTransition;
import org.springframework.statemachine.annotation.WithStateMachine;
@WithStateMachine(id="formMachine")
public class FormEventConfig {
private Logger logger = LoggerFactory.getLogger(getClass());
/** * 當前狀態BLANK_FORM */ @OnTransition(target = "BLANK_FORM") public void create() { logger.info("---空白表單---"); } /** * BLANK_FORM->FULL_FORM 執行的動做 */ @OnTransition(source = "BLANK_FORM", target = "FULL_FORM") public void write(Message<FormEvents> message) { logger.info("---填寫完表單---"); } /** * FULL_FORM->CONFIRM_FORM 執行的動做 */ @OnTransition(source = "FULL_FORM", target = "CONFIRM_FORM") public void confirm(Message<FormEvents> message) { logger.info("---校驗表單---"); } /** * CONFIRM_FORM->SUCCESS_FORM 執行的動做 */ @OnTransition(source = "CONFIRM_FORM", target = "SUCCESS_FORM") public void submit(Message<FormEvents> message) { logger.info("---表單提交成功---"); }
}
從代碼能夠看到深深的套路感,裏面除了對流程狀態的描述不一樣外,另一個不一樣點就是MACHINEID,在不一樣的狀態機流程中,用MACHINEID來標識不一樣就能使用多種狀態機了,對比一下就很清楚。在builder裏面經過MACHINEID來區分
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 {
經過@WithStateMachine註解的id參數就區分出來了不一樣的狀態機,這個id就是builder裏面定義的MACHINEID。而後就是怎麼引用的問題了,咱們來看controller
@Autowired private OrderStateMachineBuilder orderStateMachineBuilder; @Autowired private FormStateMachineBuilder formStateMachineBuilder;
這樣,不一樣的builder就能同時引用,兩種狀態機就互不干擾的各自運行了,這是運行的代碼:
@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()); }
分別執行
http://localhost:9991/statemachine/testOrderState 使用StateMachineBuilder建立的多個狀態機演示
http://localhost:9991/statemachine/testFormState 多種狀態機的演示(上面都是order的狀態機,這個是form的狀態機)
在日誌裏面就能看到各自狀態機的運行結果了。
目前爲止,多個狀態機和多種狀態機均可以在spring statemachine裏面實現了,下一章咱們來解決下狀態機和實際業務間的數據傳輸問題,畢竟咱們不是爲了讓狀態機自個獨自玩耍,和業務數據互通有無纔是企業開發的正道。