在上一章的例子中,咱們實現了多個狀態機並存執行,不一樣的訂單有各自的狀態機運行,但只有一種狀態機,這顯然不能知足實際業務的要求,好比我就遇到了訂單流程和公文審批流程在同一個項目的狀況,因此咱們這一章講怎麼讓多種狀態機共存。java
咱們先把上一章的例子狀態機再複習一下,這是個訂單狀態機,流程圖以下:git
定義這個狀態機咱們用到了OrderEvents,OrderStates來表達狀態(states)和事件(events),用OrderStateMachineBuilder來描述初始狀態和狀態變化流程,用OrderEventConfig來描述這個流程和狀態變化過程當中須要作的業務。spring
如今咱們再弄一個新的狀態機流程,表單狀態機,流程圖以下:app
爲此,咱們一樣配套了和訂單狀態機同樣的表單四件套,events,states,StateMachineBuilder和eventConfig。ui
public enum FormStates { BLANK_FORM, // 空白表單 FULL_FORM, // 填寫完表單 CONFIRM_FORM, // 校驗表單 SUCCESS_FORM// 成功表單 }
public enum FormEvents { WRITE, // 填寫 CONFIRM, // 校驗 SUBMIT // 提交 }
import java.util.EnumSet; import org.springframework.beans.factory.BeanFactory; import org.springframework.statemachine.StateMachine; import org.springframework.statemachine.config.StateMachineBuilder; import org.springframework.stereotype.Component; /** * 訂單狀態機構建器 */ @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裏面code
@WithStateMachine(id="orderMachine") public class OrderEventConfig { ...
@WithStateMachine(id="formMachine") public class FormEventConfig {
經過@WithStateMachine註解的id參數就區分出來了不一樣的狀態機,這個id就是builder裏面定義的MACHINEID。而後就是怎麼引用的問題了,咱們來看controllerorm
@Autowired private OrderStateMachineBuilder orderStateMachineBuilder; @Autowired private FormStateMachineBuilder formStateMachineBuilder;
這樣,不一樣的builder就能同時引用,兩種狀態機就互不干擾的各自運行了,這是運行的代碼:blog
@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裏面實現了,下一章咱們來解決下狀態機和實際業務間的數據傳輸問題,畢竟咱們不是爲了讓狀態機自個獨自玩耍,和業務數據互通有無纔是企業開發的正道。