摘要

并没有深入学习,根据网上一些搜到的demo简单上手写了一下

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

正文

一、入门案例

参考demo

  1. sunbufu的源码(主要还是参照这个大佬的源码和博客)
  2. sunbufu的博客
  3. qq

案例流程图如下所示,本案例源码

1.png

下面放置关键代码

OrderStateMachineConfig

java
 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
@Configuration
@EnableStateMachine(name="orderStateMachine")
public class OrderStateMachineConfig extends StateMachineConfigurerAdapter<OrderState, OrderEvent> {
    public OrderStateMachineConfig() {

    }

    /**
     * 配置状态
     *
     * @param states
     * @throws Exception
     */
    @Override
    public void configure(StateMachineStateConfigurer<OrderState, OrderEvent> states) throws Exception {
        states.withStates()
                .initial(OrderState.WAIT_PAY)
                .states(EnumSet.allOf(OrderState.class));
    }

    /**
     * 配置转换关系
     *
     * @param transitions
     * @throws Exception
     */
    @Override
    public void configure(StateMachineTransitionConfigurer<OrderState, OrderEvent> transitions) throws Exception {
        transitions
                .withExternal().source(OrderState.WAIT_PAY).target(OrderState.WAIT_DELIVER).event(OrderEvent.PAY)
                .and()
                .withExternal().source(OrderState.WAIT_DELIVER).target(OrderState.WAIT_RECEIVE).event(OrderEvent.DELIVER)
                .and()
                .withExternal().source(OrderState.WAIT_RECEIVE).target(OrderState.FINISH).event(OrderEvent.RECEIVE);

    }
}

OrderStateMachinePersisterConfig

java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
@Configuration
public class OrderStateMachinePersisterConfig {
    @Bean
    public StateMachinePersister<OrderState, OrderEvent, Order> orderStateMachinePersist() {
        return new DefaultStateMachinePersister<>(new StateMachinePersist<OrderState, OrderEvent, Order>() {
            @Override
            public StateMachineContext<OrderState, OrderEvent> read(Order order) {
                return new DefaultStateMachineContext<>(order.getState(), null, null, null);
            }

            @Override
            public void write(StateMachineContext<OrderState, OrderEvent> context, Order order) throws Exception {
                order.setState(context.getState());
            }
        });
    }
}

OrderStateMachineHandler

java
 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
@Component
public class OrderStateMachineHandler {
    @Autowired
    private StateMachine<OrderState, OrderEvent> stateMachine;
    @Autowired
    private StateMachinePersister<OrderState, OrderEvent, Order> stateMachinePersister;

    public synchronized boolean sendEvent(OrderEvent event, Order order) {
        boolean result = false;
        try {
            stateMachine.start();
            //设置状态机状态
            stateMachinePersister.restore(stateMachine, order);
            Message<OrderEvent> eventMessage = MessageBuilder.withPayload(event).setHeader("order", order).build();
            result = stateMachine.sendEvent(eventMessage);
            //保存状态机状态
            stateMachinePersister.persist(stateMachine, order);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            stateMachine.stop();
        }
        return result;
    }
}

OrderStateMachineListener

java
 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
@Component
@WithStateMachine(name = "orderStateMachine")
public class OrderStateMachineListener {
    private final static Logger log= LoggerFactory.getLogger(OrderStateMachineListener.class);
    @OnTransition(source = "WAIT_PAY", target = "WAIT_DELIVER")
    public boolean pay(Message<OrderEvent> message) {
        OrderEvent payload = message.getPayload();
        String name = payload.name();
        log.info("完成动作---"+name);
        return true;
    }

    @OnTransition(source = "WAIT_DELIVER", target = "WAIT_RECEIVE")
    public boolean deliver(Message<OrderEvent> message) {
        OrderEvent payload = message.getPayload();
        String name = payload.name();
        log.info("完成动作---"+name);
        return true;
    }

    @OnTransition(source = "WAIT_RECEIVE", target = "FINISH")
    public boolean receive(Message<OrderEvent> message) {
        OrderEvent payload = message.getPayload();
        String name = payload.name();
        log.info("完成动作---"+name);
        return true;
    }
}

二、状态机

我理解的状态机的作用

  1. 在允许的不同状态间进行切换
  2. 防止在不允许的状态间切换导致的问题

Spring Statemachine各模块作用

模块描述
spring-statemachine-coreSpring 状态机的核心系统。
spring-statemachine-recipes-common不需要核心框架之外的依赖项的常见配方。
spring-statemachine-kryoSpring Statemachine的Kryo序列化器
spring-statemachine-data-commonSpring Data的通用支持模块
spring-statemachine-data-jpaSpring Data JPA的支持模块
spring-statemachine-data-redisSpring Data Redis的支持模块
spring-statemachine-data-mongodbSpring Data MongoDB的支持模块
spring-statemachine-zookeeper分布式状态机的 Zookeeper 集成
spring-statemachine-test状态机测试的支持模块
spring-statemachine-clusterSpring Cloud Cluster 的支持模块。请注意,Spring Cloud Cluster 已被 Spring Integration 取代
spring-statemachine-uml使用 Eclipse Papyrus 进行 UI UML 建模的支持模块
spring-statemachine-autoconfigureSpring Boot 的支持模块
spring-statemachine-bom材料清单pom
spring-statemachine-starterSpring Boot 启动器

2.1 需求

实现一个审批功能,时序图如下。

分为四个模块来完成,启动模块公共模块信息提供者信息管理者。参照IDEA创建多模块SpringBoot项目

导入依赖,版本的选用可以参考官网doc

xml
 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
<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>top.meethigher.spring.statemachine.enhanced</groupId>
    <artifactId>spring-statemachine-enhanced</artifactId>
    <version>1.0</version>
    <name>spring-statemachine-enhanced</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

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

        <!-- 核心模块  -->
        <dependency>
            <groupId>org.springframework.statemachine</groupId>
            <artifactId>spring-statemachine-core</artifactId>
            <version>2.2.1.RELEASE</version>
        </dependency>
        <!-- spring data 公共模块 -->
        <dependency>
            <groupId>org.springframework.statemachine</groupId>
            <artifactId>spring-statemachine-data-common</artifactId>
            <version>2.2.1.RELEASE</version>
        </dependency>
        <!-- spring statemachine kryo序列化 -->
        <dependency>
            <groupId>org.springframework.statemachine</groupId>
            <artifactId>spring-statemachine-kryo</artifactId>
            <version>2.2.1.RELEASE</version>
        </dependency>
        <!-- spring data jpa -->
        <dependency>
            <groupId>org.springframework.statemachine</groupId>
            <artifactId>spring-statemachine-data-jpa</artifactId>
            <version>2.2.1.RELEASE</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>