SpringStateMachine状态机教程

1、概念

状态机是用于实现工作流控制和状态自动转换的程序。
(1)工作流是独立于业务部分外的内容,
(2)调用对应的业务触发事件,就会执行对应的工作流的信息。

2、实现逻辑

通过配置 
	① 状态机的状态 和 状态变更触发事件
	② 状态机内 业务监听事件 触发逻辑
	③ 实体类的状态属性(建议用枚举类)
	④ 状态变化触发事件属性(同 ③~)
	⑤ 业务逻辑中 API 设为:
	  当业务状态变更时,会传递变更的状态值到 状态机中,
	  状态机会调用它的内置接口 发送业务状态到监听器,
	  当监听器的配置信息中 关于事件状态 有对应的状态转换事务,
	  就会再触发 状态机监听器中的 对应 事务状态转换接口, 
	  内部代码编写修改 为新的状态值
实现事件触发状态变更机制。

3、业务场景示例

以 订单状态变更为例:
订单有
			4种状态:①等待支付 ②等待发货 ③等待收货 ④完成
			3种行为(状态变更触发事件):①支付 ②发货 ③收货

4、环境

①SpringBoot

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter</artifactId>
</dependency>

②SpringJUnit5(SB2.2之后内置,可以不显式写)

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-test</artifactId>
	<scope>test</scope>
</dependency>

③Lombok

<dependency>
	<groupId>org.projectlombok</groupId>
	<artifactId>lombok</artifactId>
	<version>1.18.26</version>
</dependency>

④SpringStateMachine

<dependency>
	<groupId>org.springframework.statemachine</groupId>
	<artifactId>spring-statemachine-core</artifactId>
	<version>3.2.1</version>
</dependency>

5、实现代码

① 实体类

@Slf4j
@Data
@TableComment("订单表")
@TableName("t_order")
public class OrderDTO {

    @TableId(value="id",type = IdType.ASSIGN_ID)
    @Column(comment = "主键_订单编号",length = 20,isKey = true,type = MySqlTypeConstant.BIGINT,isAutoIncrement = true)
    private Long id;

    @Column(comment = "订单状态")
    private OrderStatusEnum orderStatus;
}

② 状态枚举类

//订单状态枚举类
public enum OrderStatusEnum {
    /*
        待支付;待发货;待收货;完成
    */
    WAIT_PAYMENT,
    WAIT_DELIVER,
    WAIT_RECEIVE,
    FINISH;
}

③ 状态变更触发事件枚举类

//订单状态变化触发事件枚举类
public enum OrderStatusChangeEventEnum {
    /*
        支付,发货,收货
    */
    PAYED,
    DELIVERY,
    RECEIVED;
}

④ SpringStateMachine状态机配置类

/*订单状态机配置:配置状态机的订单状态、订单状态触发事件*/
@Configuration
@EnableStateMachine
public class OrderStatemachineConfig extends StateMachineConfigurerAdapter<OrderStatusEnum, OrderStatusChangeEventEnum> {

    /*订单状态*/
    @Override
    public void configure(StateMachineStateConfigurer<OrderStatusEnum, OrderStatusChangeEventEnum> states) throws Exception {
        states.withStates()
                .initial(OrderStatusEnum.WAIT_PAYMENT)
                .end(OrderStatusEnum.FINISH)
                .states(EnumSet.allOf(OrderStatusEnum.class));
    }

    @Override
    public void configure(StateMachineTransitionConfigurer<OrderStatusEnum, OrderStatusChangeEventEnum> transitions) throws Exception {
        transitions.withExternal().source(OrderStatusEnum.WAIT_PAYMENT).target(OrderStatusEnum.WAIT_DELIVER)
                .event(OrderStatusChangeEventEnum.PAYED)
                .and()
                .withExternal().source(OrderStatusEnum.WAIT_DELIVER).target(OrderStatusEnum.WAIT_RECEIVE)
                .event(OrderStatusChangeEventEnum.DELIVERY)
                .and()
                .withExternal().source(OrderStatusEnum.WAIT_RECEIVE).target(OrderStatusEnum.FINISH)
                .event(OrderStatusChangeEventEnum.RECEIVED);
    }
}

⑤ 状态机事件监听器

/*订单状态机监听器:监听订单的状态,触发就会输出信息*/
@Component
@WithStateMachine
@Transactional
public class OrderStatemachineListener {

    @OnTransition(source = "WAIT_PAYMENT",target="WAIT_DELIVER")
    public boolean payTransaction(Message message){
        OrderDTO order = (OrderDTO) message.getHeaders().get("order");
        order.setOrderStatus(OrderStatusEnum.WAIT_DELIVER);
        System.out.println("已支付,状态机反馈信息:"+message.getHeaders().toString());
        return true;
    }

    @OnTransition(source = "WAIT_DELIVER",target="WAIT_RECEIVE")
    public boolean deliveryTransaction(Message message){
        OrderDTO order = (OrderDTO) message.getHeaders().get("order");
        order.setOrderStatus(OrderStatusEnum.WAIT_RECEIVE);
        System.out.println("已发货,状态机反馈信息:"+message.getHeaders().toString());
        return true;
    }

    @OnTransition(source = "WAIT_RECEIVE",target="FINISH")
    public boolean receiveTransaction(Message message){
        OrderDTO order = (OrderDTO) message.getHeaders().get("order");
        order.setOrderStatus(OrderStatusEnum.FINISH);
        System.out.println("已收货,状态机反馈信息:"+message.getHeaders().toString());
        return true;
    }
}

⑥ 业务状态变更事件API

@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, OrderDTO> implements OrderService{

    /*创建以订单状态和触发事件为泛型的状态机,并注入IOC容器中,进行事件触发和状态转换*/
    @Resource
    private StateMachine<OrderStatusEnum, OrderStatusChangeEventEnum> orderStateMachine;

    private long id=1L;//订单初始编号

    private Map<Long,OrderDTO> orders= Maps.newConcurrentMap();

    //创建订单对象
    @Override
    public OrderDTO create() {
        OrderDTO orderDTO = new OrderDTO();
        orderDTO.setOrderStatus(OrderStatusEnum.WAIT_PAYMENT);//设置订单的初始状态
        orderDTO.setId(id++);//设置订单的编号,累加
        orders.put(orderDTO.getId(),orderDTO);//根据编号对应地存储订单对象,便于存取
        System.out.println("订单创建成功"+orderDTO.toString());
        return orderDTO;//返回创建好的订单对象
    }

    //获取订单列表
    @Override
    public Map<Long, OrderDTO> getOrders() {
        return orders;
    }

    //传递订单信息给状态机(发送状态转换事件)
    private synchronized boolean sendEvent(Message<OrderStatusChangeEventEnum> message){
        boolean result=false;
        try {
            orderStateMachine.start();
            result = orderStateMachine.sendEvent(message);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(Objects.nonNull(message)){
                OrderDTO order = (OrderDTO) message.getHeaders().get("order");
                if(Objects.nonNull(order)&&Objects.equals(order.getOrderStatus(),OrderStatusEnum.FINISH)){
                    orderStateMachine.stop();
                }
            }
        }
        return result;
    }


    //当指定订单 被支付、发货、收货 后,触发更改订单的状态值,返回变更状态后的订单
    @Override
    public OrderDTO pay(long id) {

        /*根据id获取订单集合的指定订单*/
        OrderDTO orderDTO = orders.get(id);
        /*使用spring内置api创建message对象,作为消息传递给状态机以及监听器*/
        Message message =
                MessageBuilder.withPayload(OrderStatusChangeEventEnum.PAYED).
                        setHeader("order", orderDTO).build();

        /*如果传递订单消息到状态机中失败,就抛出错误提示信息*/
        if(!sendEvent(message)){
            System.out.println("订单支付失败");
        }

        return orderDTO;
    }

    @Override
    public OrderDTO deliver(long id) {
        /*根据id获取订单集合的指定订单*/
        OrderDTO orderDTO = orders.get(id);
        /*使用spring内置api创建message对象,作为消息传递给状态机以及监听器*/
        Message<OrderStatusChangeEventEnum> message =
                MessageBuilder.withPayload(OrderStatusChangeEventEnum.DELIVERY).
                        setHeader("order", orderDTO).build();

        /*如果传递订单消息到状态机中失败,就抛出错误提示信息*/
        if(!sendEvent(message)){
            System.out.println("订单发货失败");
        }

        return orderDTO;
    }

    @Override
    public OrderDTO receive(long id) {
        /*根据id获取订单集合的指定订单*/
        OrderDTO orderDTO = orders.get(id);
        /*使用spring内置api创建message对象,作为消息传递给状态机以及监听器*/
        Message<OrderStatusChangeEventEnum> message =
                MessageBuilder.withPayload(OrderStatusChangeEventEnum.RECEIVED).
                        setHeader("order", orderDTO).build();

        /*如果传递订单消息到状态机中失败,就抛出错误提示信息*/
        if(!sendEvent(message)){
            System.out.println("订单收货失败");
        }

        return orderDTO;
    }

}

⑦ 单元测试

@SpringBootTest
class GenerateApplicationTests {

	@Resource
	private OrderService orderService;

	@Test
	void contextLoads() {
	}


	@Test
	public void testOrder(){
		orderService.create();
		orderService.pay(1L);
		orderService.deliver(1L);
		orderService.receive(1L);
		System.out.println("所有订单状态:"+orderService.getOrders());
	}
}

6、测试结果

在这里插入图片描述

7、调用逻辑分析图(未完待续)

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值