spring statemachine 简单示例

      最近在网上看到了一些关于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;
    }
}

  • 运行结果:


重点说明几个点:

  1. StateMachineHandler.sendEvent():改方法每次对状态机进行重置,否则多订单时状态机状态无法流转;
  2. StateMachineHandler.sendEvent()方法中synchronized 很重要,如果没有这个,多个订单并发时状态会错乱;加入这个synchronized 控制后会导致状态机的吞吐率下降(这个也是我遇到的问题,个人认为是spring statemachine 的一大缺陷);
  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
- chapter1:[基本项目构建(可作为工程脚手架),引入web模块,完成一个简单的RESTful API](http://blog.didispace.com/spring-boot-learning-1/) - [使用Intellij中的Spring Initializr来快速构建Spring Boot/Cloud工程](http://blog.didispace.com/spring-initializr-in-intellij/) ### 工程配置 - chapter2-1-1:[配置文件详解:自定义属性、随机数、多环境配置等](http://blog.didispace.com/springbootproperties/) ### Web开发 - chapter3-1-1:[构建一个较为复杂的RESTful API以及单元测试](http://blog.didispace.com/springbootrestfulapi/) - chapter3-1-2:[使用Thymeleaf模板引擎渲染web视图](http://blog.didispace.com/springbootweb/) - chapter3-1-3:[使用Freemarker模板引擎渲染web视图](http://blog.didispace.com/springbootweb/) - chapter3-1-4:[使用Velocity模板引擎渲染web视图](http://blog.didispace.com/springbootweb/) - chapter3-1-5:[使用Swagger2构建RESTful API](http://blog.didispace.com/springbootswagger2/) - chapter3-1-6:[统一异常处理](http://blog.didispace.com/springbootexception/) ### 数据访问 - chapter3-2-1:[使用JdbcTemplate](http://blog.didispace.com/springbootdata1/) - chapter3-2-2:[使用Spring-data-jpa简化数据访问层(推荐)](http://blog.didispace.com/springbootdata2/) - chapter3-2-3:[多数据源配置(一):JdbcTemplate](http://blog.didispace.com/springbootmultidatasource/) - chapter3-2-4:[多数据源配置(二):Spring-data-jpa](http://blog.didispace.com/springbootmultidatasource/) - chapter3-2-5:[使用NoSQL数据库(一):Redis](http://blog.didispace.com/springbootredis/) - chapter3-2-6:[使用NoSQL数据库(二):MongoDB](http://blog.didispace.com/springbootmongodb/) - chapter3-2-7:[整合MyBatis](http://blog.didispace.com/springbootmybatis/) - chapter3-2-8:[MyBatis注解配置详解](http://blog.didispace.com/mybatisinfo/) ### 事务管理 - chapter3-3-1:[使用事务管理](http://blog.didispace.com/springboottransactional/) - chapter3-3-2:[分布式事务(未完成)] ### 其他内容 - chapter4-1-1:[使用@Scheduled创建定时任务](http://blog.didispace.com/springbootscheduled/) - chapter4-1-2:[使用@Async实现异步调用](http://blog.didispace.com/springbootasync/) #### 日志管理 - chapter4-2-1:[默认日志的配置](http://blog.didispace.com/springbootlog/) - chapter4-2-2:[使用log4j记录日志](http://blog.didispace.com/springbootlog4j/) - chapter4-2-3:[对log4j进行多环境不同日志级别的控制](http://blog
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值