Spring状态机StateMachine框架---企业开发实战(含代码)

Spring状态机StateMachine框架—企业开发实战(含代码)

​ 最近,在工作中上级派了一个任务给我,叫我写下状态机。一开始我听了是直接懵逼的,我都不知道什么是状态机。后来听了业务需求后才慢慢弄懂,这个状态机的作用是修改订单状态,为了让业务代码能够高效复用。这又引出了第二问题了,要怎样实现状态机,是写一堆if-else判断吗?一开始我的想法就是这样,后来上网查了状态机,发现有个StateMachine框架,然后就去看了官方文档https://docs.spring.io/spring-statemachine/docs/2.0.2.RELEASE/reference/htmlsingle/#with-enablestatemachinefactory。当然期间在使用过程中也踩了不少的坑,就把自己一些心得写下来,当然自己没阅读过什么源码,只能是自己一些粗浅的见解而已。仅供参考。

一、状态流程图

这个流程图我是感觉非常重要的,要清楚每个状态的流转,和哪种事件会触发什么事件流转这都是很重要的。下面是我自己工作的一个状态流程图
Spring状态机StateMachine框架---企业开发实战(含代码)_第1张图片

二、理解下几个基本常用的组件

.withExternal()
.source(TradeOrderStateMachineEnum.WAIT_FOR_PAY)
.target(TradeOrderStateMachineEnum.CLOSED)
.event(TradeOrderEvent.CLOSE).and()

这是配置规则,表示从WAIT_FOR_PAY->CLOSED,需要CLOSE事件来触发。这是比较简单的一种,没有guard判断,直接流转到CLOSED状态。

.withExternal()
.source(TradeOrderStateMachineEnum.WAIT_FOR_AUDIT)
.target(TradeOrderStateMachineEnum.WAIT_FOR_DELIVER)
.event(TradeOrderEvent.AUDIT).guard(tradeOrderGuardFactory.new TradeOrderGuard()).and()

下面一种,则是多了一个.guard(),这是判断,相当于java里面if的判断条件,这个自定义判断的类必须实现Guard接口,重写里面evaluate方法,这个方法就是返回boolean。值得一提的是,每个规则都可以配置action,可以直接在后面加上.action(),也可以用@WithStateMachine和@OnTransition两个注解配合用写下自己业务代码。这个action表示,满足整个链路规则后才要做的是。

.withChoice()
.source(TradeOrderStateMachineEnum.AUDIT_CHOICE)
.first(TradeOrderStateMachineEnum.COMPLETED, tradeOrderGuardFactory.new TradeOrderAuditChoiceGuard(),new TradeOrderChoiceAction())
.then(TradeOrderStateMachineEnum.WAIT_FOR_EVALUATE, tradeOrderGuardFactory.new TradeOrderAuditChoiceGuard2(),new TradeOrderChoiceAction())
.then(TradeOrderStateMachineEnum.WAIT_FOR_SIGN, tradeOrderGuardFactory.new TradeOrderAuditChoiceGuard3(),new TradeOrderChoiceAction())
.then(TradeOrderStateMachineEnum.WAIT_FOR_DELIVER, tradeOrderGuardFactory.new TradeOrderAuditChoiceGuard4(),new TradeOrderChoiceAction())
.last(TradeOrderStateMachineEnum.WAIT_FOR_AUDIT).and()

前面两种是比较简单的,一个事件只会流转到一个状态,上面这个就是比较复杂点,也是业务上经常会用,一个event会有几种状态,first-then-last,就相当于if-else if,只要满足一个guard判断就不会往下流转,注意这里有几个坑,后面说下的。

三、代码

3.1 引入依赖包


    org.springframework.statemachine
    spring-statemachine-starter
    2.0.1.RELEASE

3.2 定义状态枚举和事件枚举

TradeOrderStateMachineEnum
public enum TradeOrderStateMachineEnum {

    WAIT_FOR_PAY(10, "待付款"),
    WAIT_FOR_AUDIT(20, "待评审"),
    WAIT_FOR_DELIVER(30, "待发货"),
    WAIT_FOR_SIGN(40, "待签收"),
    WAIT_FOR_EVALUATE(45, "待评价"),
    COMPLETED(98, "完成"),
    CLOSED(99, "关闭"),
    AUDIT_CHOICE(1000, "评审选择态"),
    SIGN_CHOICE(1001, "签收选择态");

    private final Integer value;
    private final String desc;
    private static final Map valueMap = (Map) Arrays.stream(values()).collect(Collectors.toMap(TradeOrderStateMachineEnum::getValue, Function.identity()));

    private TradeOrderStateMachineEnum(Integer value, String desc) {
        this.value = value;
        this.desc = desc;
    }

    public Integer getValue() {
        return this.value;
    }

    public String getDesc() {
        return this.desc;
    }

    public static TradeOrderStateMachineEnum fromValue(Integer value) {
        return (TradeOrderStateMachineEnum) Optional.ofNullable(valueMap.get(value)).orElseThrow(() -> {
            return new RuntimeException("can not find the enum for this value: " + value);
        });
    }
}
TradeOrderEvent
public enum TradeOrderEvent {
    PAY,//付款
    CLOSE,//关闭订单
    CANCEL,//取消数量
    AUDIT,//评审
    DELIVER,//发货
    SIGN,//签收
    EVALUATE;//评价
}

3.2 定义状态机规则和配置状态机

TradeOrderStateMachineBuilder
@Component
@EnableStateMachine(name= TradeOrderStateMachineBuilder.MACHINEID_TO)
public class TradeOrderStateMachineBuilder {

    private static final TradeOrderGuardFactory tradeOrderGuardFactory= new TradeOrderGuardFactory();

    @Autowired
    private BeanFactory beanFactory;
    private Logger logger = LoggerFactory.getLogger(getClass());
    public final static String MACHINEID_TO = "MACHINEID_TO";//TO状态机
    public StateMachine build() throws Exception {
        StateMachine stateMachine = build(beanFactory);
        logger.info("状态机ID:"+stateMachine.getId());
        stateMachine.start();
        return stateMachine;
    }
    /**
     * 构建状态机
     * -构建TO单状态机
     * @param beanFactory
     * @return
     * @throws Exception
     */
    public StateMachine build(BeanFactory beanFactory) throws Exception {
        StateMachineBuilder.Builder builder = StateMachineBuilder.builder();
        builder.configureConfiguration()
                .withConfiguration()
                .machineId(MACHINEID_TO)
                .beanFactory(beanFactory);

        builder.configureStates()
                .withStates()
                .initial(TradeOrderStateMachineEnum.WAIT_FOR_PAY)
                .choice(TradeOrderStateMachineEnum.AUDIT_CHOICE)
                .choice(TradeOrderStateMachineEnum.SIGN_CHOICE)
                .states(EnumSet.allOf(TradeOrderStateMachineEnum.class));

        builder.configureTransitions()
                //支付后,从待付款到待审核
                .withExternal()
                .source(TradeOrderStateMachineEnum.WAIT_FOR_PAY)
                .target(TradeOrderStateMachineEnum.WAIT_FOR_AUDIT)
                .event(TradeOrderEvent.PAY).and()
                //取消订单,从待付款到关闭
                .withExternal()
                .source(TradeOrderStateMachineEnum.WAIT_FOR_PAY)
                .target(TradeOrderStateMachineEnum.CLOSED)
                .event(TradeOrderEvent.CLOSE).and()
                //取消数量,从待审核到审核选择态
                .withExternal()
                .source(TradeOrderStateMachineEnum.WAIT_FOR_AUDIT)
                .target(TradeOrderStateMachineEnum.AUDIT_CHOICE)
                .event(TradeOrderEvent.CANCEL).and()
                //取消数量,从审核选择态->待发货,待签收,待评价,完成任意一种状态
                .withChoice()
                .source(TradeOrderStateMachineEnum.AUDIT_CHOICE)
                .first(TradeOrderStateMachineEnum.COMPLETED, tradeOrderGuardFactory.new TradeOrderAuditChoiceGuard(),new TradeOrderChoiceAction())
                .then(TradeOrderStateMachineEnum.WAIT_FOR_EVALUATE, tradeOrderGuardFactory.new TradeOrderAuditChoiceGuard2(),new TradeOrderChoiceAction())
                .then(TradeOrderStateMachineEnum.WAIT_FOR_SIGN, tradeOrderGuardFactory.new TradeOrderAuditChoiceGuard3(),new TradeOrderChoiceAction())
                .then(TradeOrderStateMachineEnum.WAIT_FOR_DELIVER, tradeOrderGuardFactory.new TradeOrderAuditChoiceGuard4(),new TradeOrderChoiceAction())
                .last(TradeOrderStateMachineEnum.WAIT_FOR_AUDIT).and()
                //审核后,从待审核到待发货
                .withExternal()
                .source(TradeOrderStateMachineEnum.WAIT_FOR_AUDIT)
                .target(TradeOrderStateMachineEnum.WAIT_FOR_DELIVER)
                .event(TradeOrderEvent.AUDIT).guard(tradeOrderGuardFactory.new TradeOrderGuard()).and()
                //发货后,从待发货到待签收
                .withExternal()
                .source(TradeOrderStateMachineEnum.WAIT_FOR_DELIVER)
                .target(TradeOrderStateMachineEnum.WAIT_FOR_SIGN)
                .event(TradeOrderEvent.DELIVER).guard(tradeOrderGuardFactory.new TradeOrderGuard()).and()
                //签收后,从待签收到待签收选择态
                .withExternal()
                .source(TradeOrderStateMachineEnum.WAIT_FOR_SIGN)
                .target(TradeOrderStateMachineEnum.SIGN_CHOICE)
                .event(TradeOrderEvent.SIGN).and()
                //签收后,从待签收选择态到待评价或者到已完成
                .withChoice()
                .source(TradeOrderStateMachineEnum.SIGN_CHOICE)
                .first(TradeOrderStateMachineEnum.WAIT_FOR_EVALUATE, tradeOrderGuardFactory.new TradeOrderSignChoiceGuard(),new TradeOrderChoiceAction())
                .then(TradeOrderStateMachineEnum.COMPLETED, tradeOrderGuardFactory.new TradeOrderSignChoiceGuard2(),new TradeOrderChoiceAction())
                .last(TradeOrderStateMachineEnum.WAIT_FOR_SIGN).and()
                //评价后,从待评价到已完成
                .withExternal()
                .source(TradeOrderStateMachineEnum.WAIT_FOR_EVALUATE)
                .target(TradeOrderStateMachineEnum.COMPLETED)
                .event(TradeOrderEvent.EVALUATE);
        return builder.build();
    }

    @Bean(name = "tradeOrderStateMachinePersister")
    public StateMachinePersister getOrderPersister() {
        return new DefaultStateMachinePersister<>(new StateMachinePersist() {
            @Override
            public void write(StateMachineContext context, TradeOrder contextObj) {
            }
            @Override
            public StateMachineContext read(TradeOrder contextObj) {
                StateMachineContext result = new DefaultStateMachineContext(TradeOrderStateMachineEnum.fromValue(contextObj.getOrderState()),
                        null, null, null, null, MACHINEID_TO);
                return result;
            }
            ;
        });


    }
}

3.3 配置guard判断类

这部分是可以优化,因为我看官网的文档,每个判断都要新建guard类重写evaluate方法。我不想建太多类,就集成一个类里面了。我本意是想用工厂模式想新建Guard类,但是本人开发经验不是很丰富。

TradeOrderGuardFactory
public class TradeOrderGuardFactory {

    private static final String TRADE_ORDER = StateMachineHeaderNameConstants.TRADE_ORDER;

    public class TradeOrderAuditChoiceGuard implements Guard {

        @Override
        public boolean evaluate(StateContext context) {
            TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
            if(isAllSign(tradeOrder) && !StringUtils.isEmpty(tradeOrder.getBuyer().getBuyerTenantCode())){
                return true;
            }
            return false;
        }
    }
    public class TradeOrderAuditChoiceGuard2 implements Guard {

        @Override
        public boolean evaluate(StateContext context) {
            TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
            if(isAllSign(tradeOrder) && StringUtils.isEmpty(tradeOrder.getBuyer().getBuyerTenantCode())){
                return true;
            }
            return false;
        }
    }
    public class TradeOrderAuditChoiceGuard3 implements Guard {

        @Override
        public boolean evaluate(StateContext context) {
            TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
            if(isAllDeliver(tradeOrder)){
                return true;
            }
            return false;
        }
    }

    public class TradeOrderAuditChoiceGuard4 implements Guard {

        @Override
        public boolean evaluate(StateContext context) {
            TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
            if(isAllAudit(tradeOrder) ){
                return true;
            }
            return false;
        }
    }

    public class TradeOrderSignChoiceGuard implements Guard {

        @Override
        public boolean evaluate(StateContext context) {
            TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
            if(isAllSign(tradeOrder) && StringUtils.isEmpty(tradeOrder.getBuyer().getBuyerTenantCode())){
                return true;
            }
            return false;
        }
    }

    public class TradeOrderSignChoiceGuard2 implements Guard {

        @Override
        public boolean evaluate(StateContext context) {
            TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
            if(isAllSign(tradeOrder) && !StringUtils.isEmpty(tradeOrder.getBuyer().getBuyerTenantCode())){
                return true;
            }
            return false;
        }
    }

    public class TradeOrderGuard implements Guard {
        @Override
        public boolean evaluate(StateContext context) {
            boolean result=false;

            System.out.println(context.getSource().getId());
            System.out.println(context.getTarget().getId());
            switch (context.getTarget().getId()) {
                case WAIT_FOR_DELIVER:
                    return WAIT_FOR_DELIVER(context);
                case WAIT_FOR_SIGN:
                    return WAIT_FOR_SIGN(context);
                case SIGN_CHOICE:
                    return SIGN_CHOICE(context);
                case WAIT_FOR_EVALUATE:
                    return WAIT_FOR_EVALUATE(context);
                case COMPLETED:
                    return COMPLETED(context);
                default:
                    break;
            }
            return result;
        }

        private boolean WAIT_FOR_DELIVER(StateContext context){
            TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
            if(isAllAudit(tradeOrder)){
                return true;
            }
            return false;
        }

        private boolean WAIT_FOR_SIGN(StateContext context){
            TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
            if(isAllDeliver(tradeOrder)){
                return true;
            }
            return false;
        }
        private boolean SIGN_CHOICE(StateContext context){
            TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
            if(isAllSign(tradeOrder)){
                return true;
            }
            return false;
        }

        private boolean WAIT_FOR_EVALUATE(StateContext context){
            TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
            if(isAllSign(tradeOrder)&& StringUtils.isEmpty(tradeOrder.getBuyer().getBuyerTenantCode())){
                return true;
            }
            return false;
        }

        private boolean COMPLETED(StateContext context){
            TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
            if(isAllSign(tradeOrder)&& !StringUtils.isEmpty(tradeOrder.getBuyer().getBuyerTenantCode())){
                return true;
            }
            return false;
        }

    }

    private boolean isAllAudit(TradeOrder tradeOrder) {
        for (TradeOrderDetail tradeOrderDetail : tradeOrder.getTradeOrderDetailList()) {
            if(ObjectUtils.isEmpty(tradeOrderDetail.getCancelQty())){
                tradeOrderDetail.setCancelQty(0);
            }
            if(ObjectUtils.isEmpty(tradeOrderDetail.getAuditQty())){
                tradeOrderDetail.setAuditQty(0);
            }
            //待评审的数量
            if(tradeOrderDetail.getItemQty()-tradeOrderDetail.getCancelQty()-tradeOrderDetail.getAuditQty()!=0){
                return false;
            }
        }
        return true;
    }

    private boolean isAllDeliver(TradeOrder tradeOrder) {
        for (TradeOrderDetail tradeOrderDetail : tradeOrder.getTradeOrderDetailList()) {
            if(ObjectUtils.isEmpty(tradeOrderDetail.getCancelQty())){
                tradeOrderDetail.setCancelQty(0);
            }
            if(ObjectUtils.isEmpty(tradeOrderDetail.getDeliverQty())){
                tradeOrderDetail.setDeliverQty(0);
            }
            //待评审的数量
            if(tradeOrderDetail.getItemQty()-tradeOrderDetail.getCancelQty()-tradeOrderDetail.getDeliverQty()!=0){
                return false;
            }
        }
        return true;
    }

    private boolean isAllSign(TradeOrder tradeOrder) {
        for (TradeOrderDetail tradeOrderDetail : tradeOrder.getTradeOrderDetailList()) {
            if(ObjectUtils.isEmpty(tradeOrderDetail.getCancelQty())){
                tradeOrderDetail.setCancelQty(0);
            }
            if(ObjectUtils.isEmpty(tradeOrderDetail.getCustSignQty())){
                tradeOrderDetail.setCustSignQty(0);
            }
            //代签收的数量
            if((tradeOrderDetail.getItemQty()-tradeOrderDetail.getCancelQty()-tradeOrderDetail.getCustSignQty()!=0)){
                return false;
            }
        }
        return true;
    }
}

3.4 action类

这里有用两种方式,一种是注解,另外一种是实现Action类重写execute方法,至于为什么用两种,后面会有说的,这也是其中一个坑。

TradeOrderChoiceAction
@Slf4j
public class TradeOrderChoiceAction implements Action {


    private static final String TRADE_ORDER = StateMachineHeaderNameConstants.TRADE_ORDER;

    @Override
    public void execute(StateContext context) {
        System.out.println(context.getTarget().getId());
        switch (context.getTarget().getId()) {
            case AUDIT_CHOICE:
                AUDIT_CHOICE(context);
                break;
            case SIGN_CHOICE:
                SIGN_CHOICE(context);
                break;
            default:
                break;
        }
    }

    private void SIGN_CHOICE(StateContext context) {
        TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
        log.info("签收事件之前,订单的状态为:{}"+tradeOrder.getOrderState());
        if(isAllSign(tradeOrder)){
            //全部签收,并且是2C,则为待评价状态
            if(StringUtils.isEmpty(tradeOrder.getBuyer().getBuyerTenantCode())){
                tradeOrder.setOrderState(TradeOrderStateMachineEnum.WAIT_FOR_EVALUATE.getValue());
            }else{
                //全部签收,并且是2B,则为完成状态
                tradeOrder.setOrderState(TradeOrderStateMachineEnum.COMPLETED.getValue());
            }
        }
        log.info("签收事件之后,订单的状态为:{}"+tradeOrder.getOrderState());
    }

    private void AUDIT_CHOICE(StateContext context) {
        TradeOrder tradeOrder = context.getMessage().getHeaders().get(TRADE_ORDER, TradeOrder.class);
        log.info("取消数量事件之前,订单的状态为:{}"+tradeOrder.getOrderState());
        //如果全部签收,则可能是待评价状态或者是完成状态
        if(isAllSign(tradeOrder)){
            //2C,则为待评价状态
            if(StringUtils.isEmpty(tradeOrder.getBuyer().getBuyerTenantCode())){
                tradeOrder.setOrderState(TradeOrderStateMachineEnum.WAIT_FOR_EVALUATE.getValue());
            }else{
                //2B,则为完成状态
                tradeOrder.setOrderState(TradeOrderStateMachineEnum.COMPLETED.getValue());
            }
        }else if(isAllDeliver(tradeOrder)){
            //如果全部发货,则为代签收状态
            tradeOrder.setOrderState(TradeOrderStateMachineEnum.WAIT_FOR_SIGN.getValue());
        }else if(isAllAudit(tradeOrder)){
            //如果全部审核,则为待发货状态
            tradeOrder.setOrderState(TradeOrderStateMachineEnum.WAIT_FOR_DELIVER.getValue());
        }
        log.info("取消数量事件之后,订单的状态为:{}"+tradeOrder.getOrderState());
    }


    private boolean isAllAudit(TradeOrder tradeOrder) {
        for (TradeOrderDetail tradeOrderDetail : tradeOrder.getTradeOrderDetailList()) {
            if(ObjectUtils.isEmpty(tradeOrderDetail.getCancelQty())){
                tradeOrderDetail.setCancelQty(0);
            }
            if(ObjectUtils.isEmpty(tradeOrderDetail.getAuditQty())){
                tradeOrderDetail.setAuditQty(0);
            }
            //待评审的数量
            if(tradeOrderDetail.getItemQty()-tradeOrderDetail.getCancelQty()-tradeOrderDetail.getAuditQty()!=0){
                return false;
            }
        }
        return true;
    }

    private boolean isAllDeliver(TradeOrder tradeOrder) {
        for (TradeOrderDetail tradeOrderDetail : tradeOrder.getTradeOrderDetailList()) {
            if(ObjectUtils.isEmpty(tradeOrderDetail.getCancelQty())){
                tradeOrderDetail.setCancelQty(0);
            }
            if(ObjectUtils.isEmpty(tradeOrderDetail.getDeliverQty())){
                tradeOrderDetail.setDeliverQty(0);
            }
            //待评审的数量
            if(tradeOrderDetail.getItemQty()-tradeOrderDetail.getCancelQty()-tradeOrderDetail.getDeliverQty()!=0){
                return false;
            }
        }
        return true;
    }

    private boolean isAllSign(TradeOrder tradeOrder) {
        for (TradeOrderDetail tradeOrderDetail : tradeOrder.getTradeOrderDetailList()) {
            if(ObjectUtils.isEmpty(tradeOrderDetail.getCancelQty())){
                tradeOrderDetail.setCancelQty(0);
            }
            if(ObjectUtils.isEmpty(tradeOrderDetail.getCustSignQty())){
                tradeOrderDetail.setCustSignQty(0);
            }
            //代签收的数量
            if((tradeOrderDetail.getItemQty()-tradeOrderDetail.getCancelQty()-tradeOrderDetail.getCustSignQty()!=0)){
                return false;
            }
        }
        return true;
    }
}
TradeOrderAction
@WithStateMachine(id= TradeOrderStateMachineBuilder.MACHINEID_TO)
public class TradeOrderAction {

    private static final String TRADE_ORDER = StateMachineHeaderNameConstants.TRADE_ORDER;

    @OnTransition(source = "WAIT_FOR_PAY", target = "WAIT_FOR_AUDIT")
    public void CUSTOMER_PAY(Message message) {
        TradeOrder tradeOrder = (TradeOrder) message.getHeaders().get(TRADE_ORDER);
        tradeOrder.setOrderState(TradeOrderStateMachineEnum.WAIT_FOR_AUDIT.getValue());
    }

    @OnTransition(source = "WAIT_FOR_PAY", target = "CLOSED")
    public void CUSTOMER_CLOSE(Message message) {
        TradeOrder tradeOrder = (TradeOrder) message.getHeaders().get(TRADE_ORDER);
        tradeOrder.setOrderState(TradeOrderStateMachineEnum.CLOSED.getValue());
    }

    @OnTransition(source = "WAIT_FOR_AUDIT", target = "WAIT_FOR_DELIVER")
    public void CUSTOMER_AUDIT(Message message) {
        TradeOrder tradeOrder = (TradeOrder) message.getHeaders().get(TRADE_ORDER);
        tradeOrder.setOrderState(TradeOrderStateMachineEnum.WAIT_FOR_DELIVER.getValue());
    }
    @OnTransition(source = "WAIT_FOR_DELIVER", target = "WAIT_FOR_SIGN")
    public void CUSTOMER_DELIVER(Message message) {
        TradeOrder tradeOrder = (TradeOrder) message.getHeaders().get(TRADE_ORDER);
        tradeOrder.setOrderState(TradeOrderStateMachineEnum.WAIT_FOR_SIGN.getValue());
    }

    @OnTransition(source = "WAIT_FOR_EVALUATE", target = "COMPLETED")
    public void CUSTOMER_EVALUATE(Message message) {
        TradeOrder tradeOrder = (TradeOrder) message.getHeaders().get(TRADE_ORDER);
        tradeOrder.setOrderState(TradeOrderStateMachineEnum.COMPLETED.getValue());
    }


}

3.5 其他类

这个类是为了发message保证header一致,当然也可以硬编码保证一致。

StateMachineHeaderNameConstants
public class StateMachineHeaderNameConstants {

    //交易订单
    public static final String TRADE_ORDER = "tradeOrder";
}

3.6 使用工具类

StateMachineUtils
@Slf4j
public class StateMachineUtils {
    private static final String TRADE_ORDER = StateMachineHeaderNameConstants.TRADE_ORDER;

    @Autowired
    private TradeOrderStateMachineBuilder tradeOrderStateMachineBuilder;
    @Resource(name = "tradeOrderStateMachinePersister")
    private StateMachinePersister tradeOrderStateMachinePersister;

    public void execute(TradeOrder tradeOrder, TradeOrderEvent event) throws Exception{
        log.debug("调用状态机前的订单状态为>>>>>>>>>>{}"+tradeOrder.getOrderState());
        //获取TO状态机
        StateMachine stateMachine = tradeOrderStateMachineBuilder.build();
        Message message = MessageBuilder.withPayload(event).setHeader(TRADE_ORDER, tradeOrder).build();
        //初始化状态机
        tradeOrderStateMachinePersister.restore(stateMachine,tradeOrder);
        stateMachine.sendEvent(message);
        log.debug("调用状态机后的订单状态为>>>>>>>>>>{}"+tradeOrder.getOrderState());
    }
}

这里说一下,为什么要用StateMachinePersister,这个主要作用是做持久化的,当然还有做redis持久化,我这边没用到就没写。而在这里引用是为了初始化状态机的状态,能让状态机一进来就是自己想要的状态的。不然触发不了流转规则。

四、出现的一些问题(自己踩过的坑)

4.1 使用withChoice,一定要在在初始化上加上choice,不然的话不生效。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VJjSM7rM-1631519370359)(D:\MyData\ligy112\AppData\Roaming\Typora\typora-user-images\image-20210913151753492.png)]

这里一定要记得配置对应的状态。

4.2 withChoice的触发

withChoice的生效,是又上一个withExternal流程后,如果直接设置withChoice里面的状态,是不能执行guard和action。所以我这边才会多出一个中间态,就是为了触发流转判断的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IN3DCLWI-1631519370361)(D:\MyData\ligy112\AppData\Roaming\Typora\typora-user-images\image-20210913152331982.png)]

4.3 guard里面的target问题。

在一般情况下,我们都可以switch来判断,不用新建一堆guard类,但是在withChoice的first,then,lat里面的状态是流转后,但是不能用这个判断,自己可以打印下日志试下。这也是我为什么在GuardFactory里现有许多内部类的原因。

4.4 withChoice的action事件,不能用注解触发。

其实这个问题跟上一个问题有点像,用注解方式的话,一般都会有target,但是在withChoice里的target是上一个withExternal的target,所以是不会生效的。下图是一般情况的action配置注解。

@OnTransition(source = "WAIT_FOR_PAY", target = "WAIT_FOR_AUDIT")

所以这也是为什么会有两种方式的Action。

五、总结

StateMachine还是挺好的,能够省掉很多ifelse,更重要是让业务解耦。当然以上是我个人实际开发遇到的问题,就想记录下来而已。有什么错误,请指出,毕竟我也是个菜鸟,这就当是我个人的笔记而已。

你可能感兴趣的:(stateMachine,spring,java,spring,boot)