spring状态机

要用必须看完踩坑,不然祖坟都给你刨出来

一、使用

1. 导包

    <dependency>
        <groupId>org.springframework.statemachinegroupId>
        <artifactId>spring-statemachine-starterartifactId>
        <version>2.0.1.RELEASEversion>
    dependency>

2. 代码

枚举类

package com.zhk.study.state.test;


/**
 * 这是状态机传参就是执行到哪一步的枚举类
 */
public enum OrderStatusChangeEvent {
        // 支付,发货,确认收货
        PAYED, DELIVERY, RECEIVED;
}
package com.zhk.study.state.test;

/**
 * 订单状态枚举类
 */
public enum OrderStatus {
        // 待支付,待发货,待收货,已完成
        WAIT_PAYMENT(1, "待支付"),
        WAIT_DELIVER(2, "待发货"),
        WAIT_RECEIVE(3, "待收货"),
        FINISH(4, "已完成");
        private Integer key;
        private String desc;
        OrderStatus(Integer key, String desc) {
            this.key = key;
            this.desc = desc;
        }
        public Integer getKey() {
            return key;
        }
        public String getDesc() {
            return desc;
        }
        public static OrderStatus getByKey(Integer key) {
            for (OrderStatus e : values()) {
                if (e.getKey().equals(key)) {
                    return e;
                }
            }
            throw new RuntimeException("enum not exists.");
        }
    }

业务代码

package com.zhk.study.state.test;

import lombok.extern.slf4j.Slf4j;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.persist.StateMachinePersister;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;


@Service("orderService")
@Slf4j
public class OrderServiceImpl{
    @Resource
    private StateMachine<OrderStatus, OrderStatusChangeEvent> orderStateMachine;
    @Resource
    private StateMachinePersister<OrderStatus, OrderStatusChangeEvent, String> stateMachineMemPersister;



    /**
     * 对订单进行支付
     *
     * @return
     */
    public Order pay() {
        Order order = OrderMapper.getOrder();
        if (!sendEvent(OrderStatusChangeEvent.PAYED, order)) {
            throw new RuntimeException("支付失败, 订单状态异常");
        }
        return order;
    }
    /**
     * 对订单进行发货
     *
     * @param id
     * @return
     */
    public Order deliver() {
        Order order = OrderMapper.getOrder();
        if (!sendEvent(OrderStatusChangeEvent.DELIVERY, order)) {
            throw new RuntimeException("发货失败, 订单状态异常");
        }
        return order;
    }
    /**
     * 对订单进行确认收货
     *
     * @param id
     * @return
     */
    public Order receive() {
        Order order = OrderMapper.getOrder();
        if (!sendEvent(OrderStatusChangeEvent.RECEIVED, order)) {
            throw new RuntimeException("收货失败, 订单状态异常");
        }
        return order;
    }
    /**
     * 发送订单状态转换事件
     * synchronized修饰保证这个方法是线程安全的
     *
     * @param changeEvent
     * @param order
     * @return
     */
    private synchronized boolean sendEvent(OrderStatusChangeEvent changeEvent, Order order) {
        boolean result = false;
        try {
            //启动状态机
            orderStateMachine.start();
            //尝试恢复状态机状态
            stateMachineMemPersister.restore(orderStateMachine, String.valueOf(order.getId()));
            Message message = MessageBuilder.withPayload(changeEvent).setHeader("order", order).build();
            result = orderStateMachine.sendEvent(message);
            //持久化状态机状态
            stateMachineMemPersister.persist(orderStateMachine, String.valueOf(order.getId()));
        } catch (Exception e) {
            log.error("订单操作失败:{}", e);
        } finally {
            orderStateMachine.stop();
        }
        return result;
    }
}

package com.zhk.study.state.test;

public class OrderMapper {
    private static Order order = new Order(1, OrderStatus.WAIT_PAYMENT.getKey());

    public static Order getOrder() {
        return order;
    }


    public static void update(Integer status) {
        order.setStatus(status);
    }



}


package com.zhk.study.state.test;

import com.zhk.study.state.config.Persist;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.Map;


@RestController
@RequestMapping("/order")
public class OrderController {
    @Resource(name = "orderService")
    private OrderServiceImpl orderService;


    /**
     * 对订单进行支付
     *
     * @param id
     * @return
     */
    @RequestMapping("/pay")
    public Order pay() {
        //对订单进行支付
        return orderService.pay();
    }

    /**
     * 对订单进行发货
     *
     * @param id
     * @return
     */
    @RequestMapping("/deliver")
    public Order deliver() {
        //对订单进行确认收货

        return orderService.deliver();
    }
    /**
     * 对订单进行确认收货
     *
     * @param id
     * @return
     */
    @RequestMapping("/receive")
    public Order receive() {
        //对订单进行确认收货
        return orderService.receive();
    }

    @RequestMapping("/map")
    public Map get() {
        return Persist.map;
    }
}

实体类

package com.zhk.study.state.test;

import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
public class Order {
    private Integer id;

    private Integer status;
}

配置类

package com.zhk.study.state.config;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.statemachine.StateMachineContext;
import org.springframework.statemachine.StateMachinePersist;
import org.springframework.statemachine.persist.DefaultStateMachinePersister;
import org.springframework.statemachine.persist.StateMachinePersister;
import java.util.HashMap;
import java.util.Map;


@Configuration
@Slf4j
public class Persist<E, S> {
    public static Map map = new HashMap();
    /**
     * 持久化到内存map中
     *
     * @return
     */
    @Bean(name = "stateMachineMemPersister")
    public static StateMachinePersister getPersister() {
        return new DefaultStateMachinePersister(new StateMachinePersist() {
            @Override
            public void write(StateMachineContext context, Object contextObj) throws Exception {
                log.info("持久化状态机,context:{},contextObj:{}", JSON.toJSONString(context), JSON.toJSONString(contextObj));
                map.put(contextObj, context);
            }
            @Override
            public StateMachineContext read(Object contextObj) throws Exception {
                log.info("获取状态机,contextObj:{}", JSON.toJSONString(contextObj));
                StateMachineContext stateMachineContext = (StateMachineContext) map.get(contextObj);
                log.info("获取状态机结果,stateMachineContext:{}", JSON.toJSONString(stateMachineContext));
                return stateMachineContext;
            }

        });
    }

//    @Resource
//    private RedisConnectionFactory redisConnectionFactory;
//    /**
//     * 持久化到redis中,在分布式系统中使用
//     *
//     * @return
//     */
//    @Bean(name = "stateMachineRedisPersister")
//    public RedisStateMachinePersister getRedisPersister() {
//        RedisStateMachineContextRepository repository = new RedisStateMachineContextRepository<>(redisConnectionFactory);
//        RepositoryStateMachinePersist p = new RepositoryStateMachinePersist<>(repository);
//        return new RedisStateMachinePersister<>(p);
//    }
}
package com.zhk.study.state.config;

import com.zhk.study.state.test.OrderStatus;
import com.zhk.study.state.test.OrderStatusChangeEvent;
import org.springframework.context.annotation.Configuration;
import org.springframework.statemachine.config.EnableStateMachine;
import org.springframework.statemachine.config.StateMachineConfigurerAdapter;
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;

import java.util.EnumSet;

@Configuration
    @EnableStateMachine(name = "orderStateMachine")
    public class OrderStateMachineConfig extends StateMachineConfigurerAdapter<OrderStatus, OrderStatusChangeEvent> {
        /**
         * 配置状态
         *
         * @param states
         * @throws Exception
         */
        public void configure(StateMachineStateConfigurer<OrderStatus, OrderStatusChangeEvent> states) throws Exception {
            states
                    .withStates()
                    .initial(OrderStatus.WAIT_PAYMENT)
                    .states(EnumSet.allOf(OrderStatus.class));
        }
        /**
         * 配置状态转换事件关系
         *
         * @param transitions
         * @throws Exception
         */
        public void configure(StateMachineTransitionConfigurer<OrderStatus, OrderStatusChangeEvent> transitions) throws Exception {
            transitions
                    //支付事件:待支付-》待发货
                    .withExternal().source(OrderStatus.WAIT_PAYMENT).target(OrderStatus.WAIT_DELIVER).event(OrderStatusChangeEvent.PAYED)
                    .and()
                    //发货事件:待发货-》待收货
                    .withExternal().source(OrderStatus.WAIT_DELIVER).target(OrderStatus.WAIT_RECEIVE).event(OrderStatusChangeEvent.DELIVERY)
                    .and()
                    //收货事件:待收货-》已完成
                    .withExternal().source(OrderStatus.WAIT_RECEIVE).target(OrderStatus.FINISH).event(OrderStatusChangeEvent.RECEIVED);
        }
    }

package com.zhk.study.state.config;

import com.zhk.study.state.test.Order;
import com.zhk.study.state.test.OrderMapper;
import com.zhk.study.state.test.OrderStatus;
import com.zhk.study.state.test.OrderStatusChangeEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.messaging.Message;
import org.springframework.statemachine.annotation.OnTransition;
import org.springframework.statemachine.annotation.WithStateMachine;
import org.springframework.stereotype.Component;


@Component("orderStateListener")
@WithStateMachine(name = "orderStateMachine")
@Slf4j
public class OrderStateListenerImpl {

    @OnTransition(source = "WAIT_PAYMENT", target = "WAIT_DELIVER")
    public void payTransition(Message<OrderStatusChangeEvent> message) {
        Order order = (Order)message.getHeaders().get("order");
        System.out.println("修改前订单状态为" + order.toString());

        OrderMapper.update(OrderStatus.WAIT_DELIVER.getKey());
        System.out.println("待支付变成代发货状态");

    }
    @OnTransition(source = "WAIT_DELIVER", target = "WAIT_RECEIVE")
    public void deliverTransition(Message<OrderStatusChangeEvent> message) {
        Order order = (Order)message.getHeaders().get("order");
        System.out.println("修改前订单状态为" + order.toString());
    
        OrderMapper.update(OrderStatus.WAIT_RECEIVE.getKey());
        System.out.println("发货变成待收货状态");

    }
    @OnTransition(source = "WAIT_RECEIVE", target = "FINISH")
    public void receiveTransition(Message<OrderStatusChangeEvent> message) {
        Order order = (Order)message.getHeaders().get("order");
        System.out.println("修改前订单状态为" + order.toString());

        OrderMapper.update(OrderStatus.FINISH.getKey());
        System.out.println("确认收货变成已完成状态");
    }
}

二、持久化

1. 导包

    <dependency>
        <groupId>org.springframework.statemachinegroupId>
        <artifactId>spring-statemachine-redisartifactId>
        <version>1.2.9.RELEASEversion>
    dependency>

2. 配置

spring:
  redis:
    database: 0
    host: localhost
    jedis:
      pool:
        max-active: 8
        max-idle: 8
        max-wait: ''
        min-idle: 0
    password: ''
    port: 6379
    timeout: 0


3、代码

  • Persist类中持久化到redis中,在分布式系统中使用注释取消掉
  • OrderServiceImpl中stateMachineMemPersister替换成redis持久化的bean即可

三、踩坑

状态机监听里面去修改状态出现异常就会导致事务回滚但是状态机还是返回的true,往下面走状态,但是事务已经回滚

3.1 例子

你可以在OrderStateListenerImpl将支付payTransition改成,使用下就知道了

    @OnTransition(source = "WAIT_PAYMENT", target = "WAIT_DELIVER")
    public void payTransition(Message<OrderStatusChangeEvent> message) {
        Order order = (Order)message.getHeaders().get("order");
        System.out.println("修改前订单状态为" + order.toString());
        if (true) {
            throw new RuntimeException("xxx");
        }
        OrderMapper.update(OrderStatus.WAIT_DELIVER.getKey());
        System.out.println("待支付变成代发货状态");

    }

3.2 解决

可以存到内存中,然后通过内存来判断是否成功
改造监听状态:把业务的执行结果进行保存,1成功,0失败

枚举类是为了区分对应步骤出现问题

public interface CommonConstants {
	String orderHeader="order";
    String payTransition="payTransition";
    String deliverTransition="deliverTransition";
    String receiveTransition="receiveTransition";
}

OrderStateListenerImpl监听中修改

@Resource
private StateMachine<OrderStatus, OrderStatusChangeEvent> orderStateMachine;


@OnTransition(source = "WAIT_PAYMENT", target = "WAIT_DELIVER")
@Transactional(rollbackFor = Exception.class)
public void payTransition(Message<OrderStatusChangeEvent> message) {
    Order order = (Order) message.getHeaders().get("order");
    log.info("支付,状态机反馈信息:{}",  message.getHeaders().toString());
    try {
        //更新订单
        orderMapper.updateById(OrderStatus.WAIT_DELIVER.getKey());
        //TODO 其他业务
        //模拟异常
        if(true){
            throw new RuntimeException("执行业务异常");
        }
        //成功 则为1
        orderStateMachine.getExtendedState().getVariables().put(CommonConstants.payTransition+order.getId(),1);
    } catch (Exception e) {
        //如果出现异常,则进行回滚
        log.error("payTransition 出现异常:{}",e);
        //将异常信息变量信息中,失败则为0
        orderStateMachine.getExtendedState().getVariables().put(CommonConstants.payTransition+order.getId(), 0);
        throw e;
    }

OrderServiceImpl不持久化状态机中

@Resource
private StateMachine<OrderStatus, OrderStatusChangeEvent> orderStateMachine;
@Resource
private StateMachinePersister<OrderStatus, OrderStatusChangeEvent, String> stateMachineMemPersister;

/**
 * 发送订单状态转换事件
 * synchronized修饰保证这个方法是线程安全的
 *
 * @param changeEvent
 * @param order
 * @return
 */
private synchronized boolean sendEvent(OrderStatusChangeEvent changeEvent, Order order){
    boolean result = false;
    try {
        //启动状态机
        orderStateMachine.start();
        //尝试恢复状态机状态
        stateMachineMemPersister.restore(orderStateMachine, String.valueOf(order.getId()));
        Message message = MessageBuilder.withPayload(changeEvent).setHeader("order", order).build();
        result = orderStateMachine.sendEvent(message);
        if(!result){
            return false;
        }
        //获取到监听的结果信息
        Integer o = (Integer) orderStateMachine.getExtendedState().getVariables().get(CommonConstants.payTransition + order.getId());
        //操作完成之后,删除本次对应的key信息
        orderStateMachine.getExtendedState().getVariables().remove(CommonConstants.payTransition+order.getId());
        //如果事务执行成功,则持久化状态机
        if(Objects.equals(1,Integer.valueOf(o))){
            //持久化状态机状态
            stateMachineMemPersister.persist(orderStateMachine, String.valueOf(order.getId()));
        }else {
            //订单执行业务异常
            return false;
        }
    } catch (Exception e) {
        log.error("订单操作失败:{}", e);
    } finally {
        orderStateMachine.stop();
    }
    return result;
}

四、封装

由于每次都进行put使用aop加注解的方式替代这边给个链接直接看他最后面
链接

你可能感兴趣的:(spring,java,数据库)