StateMachine教程

1. Statemachine简介

Spring Statemachine是应用程序开发人员在Spring应用程序中使用状态机概念的框架。

  • 从设计层面分析:状态机的目的是解决复杂的状态管理流程,保证程序单一原则和开闭原则;
  • 从业务角度分析:状态机应有初始化状态、加载所有已有状态、事件、转移、动作、触发下一个状态为驱动,并解决了业务逻辑和状态管理之间的强耦合。

1. Spring Statemachine原理

Spring状态机建立在有限状态机(FSM)的概念之上,提供了一种简洁且灵活的方式来定义、管理和执行状态机。它将状态定义为Java对象,并通过配置来定义状态之间的转换规则。状态转换通常由外部事件触发,我们可以根据业务逻辑定义不通的事件类型,并与状态转换关联。

Spring状态机还提供了状态监听器,用于状态变化时执行特定的逻辑。同时,状态机的状态可以持久化到数据库或者其他存储介质中,以便在系统重启或故障恢复时保持状态的一致性。

Spring状态机核心主要包含以下几个关键元素:

  • 状态state: 定义了系统可能处于的各种状态,如订单状态中的待支付、已支付等
  • 事件Event:触发状态转换的动作或者消息,它是引起状态机从当前状态迁移到新状态的原因

  • 转换Transition:描述了在何种条件下,当接收到特定事件时,系统可以中一个状态转移到另一个状态。例如:接收到”支付成功”事件时,订单状态从”待支付”转变为”已支付”。

  • 动作Action:事件发生以后要执行东做。例如事件时”按开门按钮”,动作是开门。变成的时候,一个Action一般对应一个函数。
  • 守卫Guard:一种条件逻辑,用于决定是否可以进行某个状态转换。守卫可以基于应用程序的当前状态或其他条件来确定转换是否应该发生

状态机可归纳为4个要素,现态、条件、动作、次态。“现态”和“条件”是因,“动作”和“次态”是果。

1 现态:指当前所处的状态
2 条件:又称“事件”,当一个条件被满足,将会触发一个动作,或者执行一次状态的迁移
3 动作:条件满足后执行的动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。动作不是必须的,当条件满足后,也可以不执行任何动作,直接迁移到新的状态。
4 次态:条件满足后要迁往的新状态。“次态”是相对于“现态”而言的,“次态”一旦被激活,就转换成“现态”。

2. 代码工程

Spring statemachine是使用Spring框架下的状态机概念创建的一种应用程序开发框架,它使得状态结构层次化,简化了配置状态机的过程

官方文档在这!!!

2.1 简单订单流程

使用springboot基于statemachine完成简单的订单流程,流程图如下所示:

  1. 引入依赖

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.example</groupId>
    <artifactId>statemachine_demo</artifactId>
    <version>1.0</version>
    <name>statemachine_demo</name>
    <url>http://www.example.com</url>
    <parent>
    <artifactId>spring-boot-starter-parent</artifactId>
    <groupId>org.springframework.boot</groupId>
    <version>2.3.5.RELEASE</version>
    </parent>
    <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

    <dependencies>
    <dependency>
    <groupId>org.springframework.statemachine</groupId>
    <artifactId>spring-statemachine-core</artifactId>
    <version>2.0.1.RELEASE</version>
    </dependency>
    <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>RELEASE</version>
    <scope>compile</scope>
    </dependency>
    <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    </dependency>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-test</artifactId>
    </dependency>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-autoconfigure</artifactId>
    </dependency>
    </dependencies>
    </project>
  2. 编写状态枚举类

    定义状态States: 状态时状态机的核心组成单元,代表了系统或对象在某一个时刻可能存在的条件或模式。在状态机中,每一个状态都是系统可能处于的一种明确的条件或者阶段。

    定义状态Transitions: 转换则是指状态之间的转变过程,它是状态机模型的动态体现。当一个外部事件触发时,状态机会从当前状态转移到另一个状态

    1
    2
    3
    4
    5
    6
    7
    8
    /**
    * 订单状态枚举类
    * @author xiaoyuge
    */
    public enum OrderStatus {
    //待支付,待发货,待收货,订单完成
    WAIT_PAYMENT, WAIT_DELIVER, WAIT_RECEIVE,FINISH;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    /**
    * 状态转换枚举
    * @author xiaoyuge
    */
    public enum OrderStatusChangeEvent {
    //支付、发货、确认收货
    PAYED, DELIVERY, RECEIVED;
    }
  3. 订单实例类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    package org.example.entity;

    import lombok.Data;
    import org.example.enums.OrderStatus;
    @Data
    public class Order {
    private Long orderId;
    private OrderStatus orderStatus;
    }
  4. 定义状态机以及状态流转规则

    状态机配置累在使用Spring State machine或者其他状态机框架时的一个重要步骤,这个类主要用于定义状态机的核心结构,包括状态states、事件events、状态之间的转换规则tranistions,以及可能的状态迁移动作和决策逻辑。

    在Spring State Machine中,创建状态机配置类通常是通过继承StateMachineCOnfigureAdapter类来实现的。这个适配器类提供了几个模板方法,允许开发者重写它们来配置状态机的各种组成部分:

    1. 配置状态: configureStates(…), 在这个方法中可以定义状态机所有的状态,包括初始状态initial state和结束状态terminal states

    2. 配置转换: configureTransitions(…), 描述状态之间的转化规则,也就是当某个事件event放生时,状态机如何从一个状态到另一个状态

    3. 配置初始状态: configureInitialState(…), 如果需要显式指定状态机启东市的初始状态,可以在该方法中设置。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    package org.example.config;

    import org.example.enums.OrderStatus;
    import org.example.enums.OrderStatusChangeEvent;
    import org.springframework.beans.factory.annotation.Configurable;
    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;

    /**
    * @author xiaoyuge
    */
    @Configuration
    @EnableStateMachine
    public class OrderStateMachineConfig extends StateMachineConfigurerAdapter<OrderStatus, OrderStatusChangeEvent> {
    @Override
    public void configure(StateMachineTransitionConfigurer<OrderStatus, OrderStatusChangeEvent> transitions) throws Exception {
    //1. 订单从待支付 -> 待发货,触发事件:支付
    transitions.withExternal().source(OrderStatus.WAIT_PAYMENT).target(OrderStatus.WAIT_DELIVER).event(OrderStatusChangeEvent.PAYED)
    .and()
    //2. 订单从待发货 -> 待收货,触发事件:发货
    .withExternal().source(OrderStatus.WAIT_DELIVER).target(OrderStatus.WAIT_RECEIVE).event(OrderStatusChangeEvent.DELIVERY)
    .and()
    //3. 订单从待收货 -> 订单结束,触发事件:收货
    .withExternal().source(OrderStatus.WAIT_RECEIVE).target(OrderStatus.FINISH).event(OrderStatusChangeEvent.RECEIVED);
    }

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

    /**
    * 持久化配置
    * 实际使用中,可以配合redis等,进行持久化操作
    */
    // @Bean
    // public StateMachinePersist<OrderStatus, OrderStatusChangeEvent, Order> persist(){
    // return (StateMachinePersist<OrderStatus, OrderStatusChangeEvent, Order>) new DefaultStateMachinePersister<>(new StateMachinePersist<OrderStatus, OrderStatusChangeEvent, Order>() {
    // @Override
    // public void write(StateMachineContext<OrderStatus, OrderStatusChangeEvent> context, Order order) throws Exception {
    // //此处并没有进行持久化操作
    // }
    //
    // @Override
    // public StateMachineContext<OrderStatus, OrderStatusChangeEvent> read(Order order) throws Exception {
    // //此处直接获取order中的状态,其实并没有进行持久化读取操作
    // return new DefaultStateMachineContext<>(order.getStatus(), null, null, null);
    // }
    // });
    // }
    }

  5. 定义状态机监听器

    状态机监听器(State Machine Listener)可以监听并响应状态机在运行中的各种事件,例如状态变迁、进入或者退出状态、转换被拒绝等。在Spring Statemachine中,监听起可以通过实现StateMachineListener接口来定义。

    该接口提供了一系列的回调方法,例如:transitionTriggered、stateEntered、stateExited等,当状态机触发转换、进入新状态或者离开旧状态时,这些方法都会被调用。

    注解方式可以在类的方法上直接声明该方法应该在何种状态下别调用,例如:@OnTransition@OnTransitionEnd@OnTransitionStart

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    package org.example.listener;

    import org.example.entity.Order;
    import org.example.enums.OrderStatus;
    import org.example.enums.OrderStatusChangeEvent;
    import org.springframework.messaging.Message;
    import org.springframework.statemachine.annotation.OnTransition;
    import org.springframework.statemachine.annotation.WithStateMachine;
    import org.springframework.stereotype.Component;

    /**
    * @author xiaoyuge
    */
    @Component
    @WithStateMachine(name = "orderStateMachine")
    public class OrderStateListener {
    @OnTransition(source = "WAIT_PAYMENT", target = "WAIT_DELIVER")
    public boolean payTransition(Message<OrderStatusChangeEvent> message) {
    Order order = (Order) message.getHeaders().get("order");
    order.setOrderStatus(OrderStatus.WAIT_DELIVER);
    System.out.println("支付 headers=" + message.getHeaders().toString());
    return true;
    }

    @OnTransition(source = "WAIT_DELIVER", target = "WAIT_RECEIVE")
    public boolean deliverTransition(Message<OrderStatusChangeEvent> message) {
    Order order = (Order) message.getHeaders().get("order");
    order.setOrderStatus(OrderStatus.WAIT_RECEIVE);
    System.out.println("发货 headers=" + message.getHeaders().toString());
    return true;
    }

    @OnTransition(source = "WAIT_RECEIVE", target = "FINISH")
    public boolean receiveTransition(Message<OrderStatusChangeEvent> message){
    Order order = (Order) message.getHeaders().get("order");
    order.setOrderStatus(OrderStatus.FINISH);
    System.out.println("收货 headers=" + message.getHeaders().toString());
    return true;
    }
    }
  6. 定义service

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    package org.example.service;

    import org.example.entity.Order;
    import org.example.enums.OrderStatus;
    import org.example.enums.OrderStatusChangeEvent;
    import org.mockito.internal.matchers.Or;
    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;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Objects;

    /**
    * @author xiaoyuge
    */
    @Service("orderService")
    public class OrderServiceImpl implements OrderService {

    @Resource
    private StateMachine<OrderStatus, OrderStatusChangeEvent> orderStateMachine;

    private Long id = 1L;
    public Map<Long, Order> orders = new HashMap<>();


    @Override
    public Order create() {
    System.out.println("threadName=" + Thread.currentThread().getName() + " 创建订单 id=" + id);
    Order order = new Order();
    order.setOrderStatus(OrderStatus.WAIT_PAYMENT);
    order.setOrderId(id++);
    orders.put(order.getOrderId(), order);
    return order;
    }

    @Override
    public Order pay(Long id) {
    Order order = orders.get(id);
    System.out.println("threadName=" + Thread.currentThread().getName() + " 尝试支付 id=" + id);
    Message message = MessageBuilder.withPayload(OrderStatusChangeEvent.PAYED).setHeader("order", order).build();
    if (!sendEvent(message, order)) {
    System.out.println("threadName=" + Thread.currentThread().getName() + " 支付失败, 状态异常 id=" + id);
    }
    return orders.get(id);
    }

    @Override
    public Order deliver(Long id) {
    Order order = orders.get(id);
    System.out.println("threadName=" + Thread.currentThread().getName() + " 尝试发货 id=" + id);
    final Message<OrderStatusChangeEvent> message = MessageBuilder.withPayload(OrderStatusChangeEvent.DELIVERY).setHeader("order", order).build();
    if (!sendEvent(message, order)) {
    System.out.println("threadName=" + Thread.currentThread().getName() + " 发货失败,状态异常 id=" + id);
    }
    return orders.get(id);
    }

    @Override
    public Order receive(Long id) {
    Order order = orders.get(id);
    System.out.println("threadName=" + Thread.currentThread().getName() + " 尝试收货 id=" + id);
    final Message<OrderStatusChangeEvent> message = MessageBuilder.withPayload(OrderStatusChangeEvent.RECEIVED).setHeader("order", order).build();
    if (!sendEvent(message, order)) {
    System.out.println("threadName=" + Thread.currentThread().getName() + " 收货失败,状态异常 id=" + id);
    }
    return orders.get(id);
    }

    @Override
    public Map<Long, Order> getOrders() {
    return orders;
    }

    /**
    * 发送订单状态转换事件
    *
    * @param message
    * @return
    */
    private synchronized boolean sendEvent(Message<OrderStatusChangeEvent> message, Order order) {
    boolean result = false;
    try {

    orderStateMachine.start();
    //todo 这里可以使用StateMachinePersister恢复状态机状态
    //添加延迟用于线程安全测试
    Thread.sleep(1000);
    result = orderStateMachine.sendEvent(message);
    } catch (Exception e) {
    e.printStackTrace();
    } finally {
    if (Objects.nonNull(message)) {
    if (Objects.nonNull(order) && Objects.equals(order.getOrderStatus(), OrderStatus.FINISH)) {
    orderStateMachine.stop();
    }
    }
    }
    return result;
    }
    }
  7. 测试

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    package org.example;
    import org.example.service.OrderServiceImpl;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.test.context.junit4.SpringRunner;

    @SpringBootTest
    @RunWith(SpringRunner.class)
    public class StateMachineTest {

    @Autowired
    private OrderServiceImpl orderService;

    @Test
    public void testStateMachine() {
    orderService.create();
    orderService.create();
    orderService.pay(1L);
    orderService.deliver(1L);
    orderService.receive(1L);
    orderService.pay(2L);
    orderService.deliver(2L);
    orderService.receive(2L);
    }
    }

    输出结果:

    1
    2
    3
    4
    5
    6
    7
    8
    threadName=main 创建订单 id=1
    threadName=main 创建订单 id=2
    threadName=main 尝试支付 id=1
    threadName=main 尝试发货 id=1
    threadName=main 尝试收货 id=1
    threadName=main 尝试支付 id=2
    threadName=main 尝试发货 id=2
    threadName=main 尝试收货 id=2

2.2 状态机工厂

有些时候,一个状态机不够用,我们可能要处理多个订单,这个时候就要用到状态机工厂

  1. 配置修改

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    package org.example.config;

    import org.example.entity.Order;
    import org.example.enums.OrderStatus;
    import org.example.enums.OrderStatusChangeEvent;
    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.config.EnableStateMachineFactory;
    import org.springframework.statemachine.config.StateMachineConfigurerAdapter;
    import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
    import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
    import org.springframework.statemachine.persist.DefaultStateMachinePersister;
    import org.springframework.statemachine.persist.StateMachinePersister;
    import org.springframework.statemachine.support.DefaultStateMachineContext;

    import java.util.EnumSet;

    /**
    * @author xiaoyuge
    */
    @Configuration
    @EnableStateMachineFactory
    public class OrderStateMachineFactoryConfig extends StateMachineConfigurerAdapter<OrderStatus, OrderStatusChangeEvent> {

    /**
    * 定义状态机ID
    */
    public static final String ORDER_STATEMACHINE_ID = "orderStateMachineId";
    /**
    * 配置订单初始状态
    */
    @Override
    public void configure(StateMachineStateConfigurer<OrderStatus, OrderStatusChangeEvent> states) throws Exception {
    states.withStates()
    .initial(OrderStatus.WAIT_PAYMENT)
    // .end(OrderStatus.FINISH)
    .states(EnumSet.allOf(OrderStatus.class));
    }

    @Override
    public void configure(StateMachineTransitionConfigurer<OrderStatus, OrderStatusChangeEvent> transitions) throws Exception {
    //1. 订单从待支付 -> 待发货,触发事件:支付
    transitions.withExternal().source(OrderStatus.WAIT_PAYMENT).target(OrderStatus.WAIT_DELIVER).event(OrderStatusChangeEvent.PAYED)
    .and()
    //2. 订单从待发货 -> 待收货,触发事件:发货
    .withExternal().source(OrderStatus.WAIT_DELIVER).target(OrderStatus.WAIT_RECEIVE).event(OrderStatusChangeEvent.DELIVERY)
    .and()
    //3. 订单从待收货 -> 订单结束,触发事件:收货
    .withExternal().source(OrderStatus.WAIT_RECEIVE).target(OrderStatus.FINISH).event(OrderStatusChangeEvent.RECEIVED);
    }

    }
  2. service使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    package org.example.service;

    import org.example.entity.Order;
    import org.example.enums.OrderStatus;
    import org.example.enums.OrderStatusChangeEvent;
    import org.springframework.messaging.Message;
    import org.springframework.messaging.support.MessageBuilder;
    import org.springframework.statemachine.StateMachine;
    import org.springframework.statemachine.config.StateMachineFactory;
    import org.springframework.stereotype.Service;

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

    /**
    * @author xiaoyuge
    */
    @Service("orderService")
    public class OrderServiceImpl implements OrderService {

    // @Resource
    // private StateMachine<OrderStatus, OrderStatusChangeEvent> orderStateMachine;

    @Resource
    private StateMachineFactory<OrderStatus, OrderStatusChangeEvent> orderStateMachineFactory;

    private Long id = 1L;
    public Map<Long, Order> orders = new HashMap<>();
    public static final String stateMachineId = "orderStateMachine";



    @Override
    public Order create() {
    Order order = new Order();
    order.setOrderStatus(OrderStatus.WAIT_PAYMENT);
    System.out.println("threadName=" + Thread.currentThread().getName() + " 创建订单, id=" + id);
    order.setOrderId(id++);
    orders.put(order.getOrderId(), order);
    return order;
    }

    @Override
    public Order pay(Long id) {
    Order order = orders.get(id);
    System.out.println("threadName=" + Thread.currentThread().getName() + " 尝试支付 id=" + id);
    Message message = MessageBuilder.withPayload(OrderStatusChangeEvent.PAYED).setHeader("order", order).build();
    if (!sendEvent(message, order)) {
    System.out.println("threadName=" + Thread.currentThread().getName() + " 支付失败, 状态异常 id=" + id);
    }
    return orders.get(id);
    }

    @Override
    public Order deliver(Long id) {
    Order order = orders.get(id);
    System.out.println("threadName=" + Thread.currentThread().getName() + " 尝试发货 id=" + id);
    final Message<OrderStatusChangeEvent> message = MessageBuilder.withPayload(OrderStatusChangeEvent.DELIVERY).setHeader("order", order).build();
    if (!sendEvent(message, order)) {
    System.out.println("threadName=" + Thread.currentThread().getName() + " 发货失败,状态异常 id=" + id);
    }
    return orders.get(id);
    }

    @Override
    public Order receive(Long id) {
    Order order = orders.get(id);
    System.out.println("threadName=" + Thread.currentThread().getName() + " 尝试收货 id=" + id);
    final Message<OrderStatusChangeEvent> message = MessageBuilder.withPayload(OrderStatusChangeEvent.RECEIVED).setHeader("order", order).build();
    if (!sendEvent(message, order)) {
    System.out.println("threadName=" + Thread.currentThread().getName() + " 收货失败,状态异常 id=" + id);
    }
    return orders.get(id);
    }

    @Override
    public Map<Long, Order> getOrders() {
    return orders;
    }

    /**
    * 发送订单状态转换事件
    *
    * @param message
    * @return
    */
    private synchronized boolean sendEvent(Message<OrderStatusChangeEvent> message, Order order) {
    synchronized (String.valueOf(order.getOrderId()).intern()) {
    boolean result = false;
    StateMachine<OrderStatus, OrderStatusChangeEvent> orderStateMachine = orderStateMachineFactory.getStateMachine(stateMachineId);
    System.out.println("id=" + order.getOrderId() + " 状态机 orderStateMachine" + orderStateMachine);
    try {
    orderStateMachine.start();
    System.out.println("id=" + order.getOrderId() + " 状态机 orderStateMachine id=" + orderStateMachine.getId());
    //添加延迟用于线程安全测试
    Thread.sleep(1000);
    result = orderStateMachine.sendEvent(message);
    } catch (Exception e) {
    e.printStackTrace();
    } finally {
    orderStateMachine.stop();
    }
    return result;
    }
    }
    }
  3. 监听起listener配置ID

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    package org.example.listener;

    import org.example.config.OrderStateMachineFactoryConfig;
    import org.example.entity.Order;
    import org.example.enums.OrderStatus;
    import org.example.enums.OrderStatusChangeEvent;
    import org.springframework.messaging.Message;
    import org.springframework.statemachine.annotation.OnTransition;
    import org.springframework.statemachine.annotation.WithStateMachine;
    import org.springframework.stereotype.Component;

    /**
    * @author xiaoyuge
    */
    @Component
    //这里配置ID
    @WithStateMachine(id= OrderStateMachineFactoryConfig.ORDER_STATEMACHINE_ID)
    public class OrderStateListener {
    @OnTransition(source = "WAIT_PAYMENT", target = "WAIT_DELIVER")
    public boolean payTransition(Message<OrderStatusChangeEvent> message) {
    Order order = (Order) message.getHeaders().get("order");
    order.setOrderStatus(OrderStatus.WAIT_DELIVER);
    System.out.println("支付 headers=" + message.getHeaders().toString()+" event="+message.getPayload());
    return true;
    }

    @OnTransition(source = "WAIT_DELIVER", target = "WAIT_RECEIVE")
    public boolean deliverTransition(Message<OrderStatusChangeEvent> message) {
    Order order = (Order) message.getHeaders().get("order");
    order.setOrderStatus(OrderStatus.WAIT_RECEIVE);
    System.out.println("发货 headers=" + message.getHeaders().toString()+" event="+message.getPayload());
    return true;
    }

    @OnTransition(source = "WAIT_RECEIVE", target = "FINISH")
    public boolean receiveTransition(Message<OrderStatusChangeEvent> message){
    Order order = (Order) message.getHeaders().get("order");
    order.setOrderStatus(OrderStatus.FINISH);
    System.out.println("收货 headers=" + message.getHeaders().toString()+" event="+message.getPayload());
    return true;
    }
    }
  4. 测试

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @Test
    public void testStateMachine() {
    orderService.create();
    orderService.create();
    orderService.pay(1L);
    orderService.deliver(1L);
    orderService.receive(1L);
    orderService.pay(2L);
    orderService.deliver(2L);
    orderService.receive(2L);
    }

    输出日志如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    threadName=main 创建订单, id=1
    threadName=main 创建订单, id=2
    threadName=main 尝试支付 id=1
    id=1 状态机 orderStateMachineWAIT_DELIVER WAIT_RECEIVE WAIT_PAYMENT FINISH / / uuid=55d567b2-f7e5-45b7-a801-fa2acdbad86e / id=orderStateMachine
    id=1 状态机 orderStateMachine id=orderStateMachine
    支付 headers={order=Order(orderId=1, orderStatus=WAIT_DELIVER), id=1edf26ff-5de3-1335-c77c-02d553aae845, timestamp=1720254288593}
    threadName=main 尝试发货 id=1
    id=1 状态机 orderStateMachineWAIT_DELIVER WAIT_RECEIVE WAIT_PAYMENT FINISH / / uuid=fb7e9609-7f4b-47f1-9b87-6f5b27a229fd / id=orderStateMachine
    id=1 状态机 orderStateMachine id=orderStateMachine
    threadName=main 发货失败,状态异常 id=1
    threadName=main 尝试收货 id=1
    id=1 状态机 orderStateMachineWAIT_DELIVER WAIT_RECEIVE WAIT_PAYMENT FINISH / / uuid=82c09087-26e2-4ef1-9f8f-4468bfb4d3a3 / id=orderStateMachine
    id=1 状态机 orderStateMachine id=orderStateMachine
    threadName=main 收货失败,状态异常 id=1
    threadName=main 尝试支付 id=2
    id=2 状态机 orderStateMachineWAIT_DELIVER WAIT_RECEIVE WAIT_PAYMENT FINISH / / uuid=393980dc-3d89-4e83-814b-ab9ae7ab96ad / id=orderStateMachine
    id=2 状态机 orderStateMachine id=orderStateMachine
    支付 headers={order=Order(orderId=2, orderStatus=WAIT_DELIVER), id=501c6558-0cdc-2ed0-bb6a-7ff58b193286, timestamp=1720254291658}
    threadName=main 尝试发货 id=2
    id=2 状态机 orderStateMachineWAIT_DELIVER WAIT_RECEIVE WAIT_PAYMENT FINISH / / uuid=0f08473d-3d90-4d23-bb30-a3ebdf75b707 / id=orderStateMachine
    id=2 状态机 orderStateMachine id=orderStateMachine
    threadName=main 发货失败,状态异常 id=2
    threadName=main 尝试收货 id=2
    id=2 状态机 orderStateMachineWAIT_DELIVER WAIT_RECEIVE WAIT_PAYMENT FINISH / / uuid=9aa81cc3-4c7f-4240-8b02-45cd3011a999 / id=orderStateMachine
    id=2 状态机 orderStateMachine id=orderStateMachine
    threadName=main 收货失败,状态异常 id=2