最近在网上看到了一些关于spring statemachine 的示例,因为自己在学习过程中,看了例子后总是感觉缺少点什么,大家关注的业务参数如何传递说了但是没有给具体示例,下面是我自己在学习中的示例:示例简单实现一个订单状态机,模拟使用三个线程开启三个订单状态的的流转,其中流转过程中传递参数并获取参数;同时自己也遇到几个问题,文末提出;话不多说,上代码:
- 状态及事件定义:
public class Constant { @AllArgsConstructor @Getter public enum OrderStatusEnum { INIT(0, "初始化"), RAISING(1, "募集中"), FULL(2, "满标"), STAMP_ING(3, "合同签章中"), STAMP_SUCCESS(4, "合同签署完成"), LOAN_ING(6, "放款中"), LOAN_SUCCESS(7, "放款成功"), ; private int status; private String desc; } @AllArgsConstructor @Getter public enum OrderEventEnum { ORDER_APPLY(0, "进件事件"), SKU_NOTICE(1, "上架事件"), SKU_ENOUGH(2, "满标通知事件"), ORDER_STAMP(3, "合同签章事件"), STAMP_SUCCESS(4, "签章成功事件"), LOAN_ING(5, "放款中事件"), LOAN_SUCCESS(6, "放款成功事件"), ; private int status; private String desc; }
- 状态机定义:
@Configuration @EnableStateMachine(contextEvents = false) public class OrderMachine extends EnumStateMachineConfigurerAdapter<OrderStatusEnum, OrderEventEnum> { @Autowired private OrderMachineListener orderMachineListener; @Override public void configure(StateMachineStateConfigurer<OrderStatusEnum, OrderEventEnum> states) throws Exception { states.withStates().initial(OrderStatusEnum.INIT).states(EnumSet.allOf(OrderStatusEnum.class)); } @Override public void configure(StateMachineTransitionConfigurer<OrderStatusEnum, OrderEventEnum> transitions) throws Exception { transitions.withExternal() .source(OrderStatusEnum.INIT).target(OrderStatusEnum.RAISING).event(OrderEventEnum.SKU_NOTICE) .and() .withExternal() .source(OrderStatusEnum.RAISING).target(OrderStatusEnum.FULL).event(OrderEventEnum.SKU_ENOUGH) .and() .withExternal() .source(OrderStatusEnum.FULL).target(OrderStatusEnum.STAMP_ING).event(OrderEventEnum.ORDER_STAMP) .and() .withExternal().source(OrderStatusEnum.STAMP_ING).target(OrderStatusEnum.STAMP_SUCCESS).event(OrderEventEnum.STAMP_SUCCESS) .and() .withExternal().source(OrderStatusEnum.STAMP_SUCCESS).target(OrderStatusEnum.LOAN_ING).event(OrderEventEnum.LOAN_ING) .and() .withExternal().source(OrderStatusEnum.LOAN_ING).target(OrderStatusEnum.LOAN_SUCCESS).event(OrderEventEnum.LOAN_SUCCESS); } @Override public void configure(StateMachineConfigurationConfigurer<OrderStatusEnum, OrderEventEnum> config) throws Exception { config.withConfiguration().autoStartup(true).listener(orderMachineListener); }
- 状态机监听器:
@Component public class OrderMachineListener extends StateMachineListenerAdapter<OrderStatusEnum, OrderEventEnum> { @Override public void stateChanged(State<OrderStatusEnum, OrderEventEnum> from, State<OrderStatusEnum, OrderEventEnum> to) { System.out.println(Thread.currentThread().getName() + " | 状态机监听器:stateChanged | from:" + from + " -> to:" + to); } @Override public void stateContext(StateContext<OrderStatusEnum, OrderEventEnum> stateContext) { if (stateContext.getStage().equals(StateContext.Stage.STATE_CHANGED) && !stateContext.getTarget().getId().equals(OrderStatusEnum.INIT)) { Object orderId = ((Map) stateContext.getMessageHeaders().get("paramMap")).get("orderId"); System.out.println(Thread.currentThread().getName() + " | 状态机监听器->业务订单号:" + orderId); } } @Override public void eventNotAccepted(Message<OrderEventEnum> event) { System.err.println(Thread.currentThread().getName() + " | this event is not accepted "); } }
- 状态机发送事件:
@Component public class StateMachineHandler { private final Object object = new Object(); @Autowired private StateMachine<OrderStatusEnum, OrderEventEnum> stateMachine; public void sendEvent(OrderStatusEnum oldStatus, OrderEventEnum event, Map<String,Object> paramMap){ Message<OrderEventEnum> message = MessageBuilder .withPayload(event) .setHeaderIfAbsent("paramMap",paramMap) .build(); synchronized (object){ if (!stateMachine.getState().getId().equals(oldStatus)){ stateMachine.stop(); List<StateMachineAccess<OrderStatusEnum, OrderEventEnum>> withAllRegions = stateMachine.getStateMachineAccessor().withAllRegions(); for (StateMachineAccess<OrderStatusEnum, OrderEventEnum> stateMachineAccess:withAllRegions) { stateMachineAccess.resetStateMachine(new DefaultStateMachineContext<>(oldStatus,null,null,null)); } stateMachine.start(); } stateMachine.sendEvent(message); } } }
程序运行入口:
@SpringBootApplication public class StateMachmineApplication implements CommandLineRunner { public static void main(String[] args) { SpringApplication.run(StateMachmineApplication.class, args); } @Autowired private StateMachineHandler stateMachineHandler; @Override public void run(String... args) { for (int i = 0; i < 3; i++) { TreadRun treadRun = new TreadRun(); treadRun.setStateMachineHandler(stateMachineHandler); Thread thread = new Thread(treadRun); thread.start(); } } }
-------------------------------------------- public class TreadRun implements Runnable { private StateMachineHandler stateMachineHandler; @Override public void run(){ Map<String, Object> paramMap = new HashMap<>(); paramMap.put("orderId", "CBD-" + 10010); paramMap.put("type", "fruit"); stateMachineHandler.sendEvent(Constant.OrderStatusEnum.INIT, Constant.OrderEventEnum.SKU_NOTICE, paramMap); try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } paramMap = new HashMap<>(); paramMap.put("orderId", "CBD-" + 10011); paramMap.put("type", "fruit"); stateMachineHandler.sendEvent(Constant.OrderStatusEnum.STAMP_ING, Constant.OrderEventEnum.STAMP_SUCCESS, paramMap); try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } paramMap = new HashMap<>(); paramMap.put("orderId", "CBD-" + 10013); paramMap.put("type", "fruit"); stateMachineHandler.sendEvent(Constant.OrderStatusEnum.LOAN_ING, Constant.OrderEventEnum.LOAN_SUCCESS, paramMap); try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } paramMap = new HashMap<>(); paramMap.put("orderId", "CBD-" + 10013); paramMap.put("type", "fruit"); stateMachineHandler.sendEvent(Constant.OrderStatusEnum.RAISING, Constant.OrderEventEnum.SKU_ENOUGH, paramMap); try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } paramMap = new HashMap<>(); paramMap.put("orderId", "CBD-" + 10010); paramMap.put("type", "fruit"); stateMachineHandler.sendEvent(Constant.OrderStatusEnum.STAMP_SUCCESS, Constant.OrderEventEnum.LOAN_ING, paramMap); try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } paramMap = new HashMap<>(); paramMap.put("orderId", "CBD-" + 10011); paramMap.put("type", "fruit"); stateMachineHandler.sendEvent(Constant.OrderStatusEnum.FULL, Constant.OrderEventEnum.ORDER_STAMP, paramMap); try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } public void setStateMachineHandler(StateMachineHandler stateMachineHandler) { this.stateMachineHandler = stateMachineHandler; } }
- 运行结果:
重点说明几个点:
- StateMachineHandler.sendEvent():改方法每次对状态机进行重置,否则多订单时状态机状态无法流转;
- StateMachineHandler.sendEvent()方法中synchronized 很重要,如果没有这个,多个订单并发时状态会错乱;加入这个synchronized 控制后会导致状态机的吞吐率下降(这个也是我遇到的问题,个人认为是spring statemachine 的一大缺陷);