Spring状态机

本文最后更新于:2025年6月27日 晚上

Spring状态机

传统if elseswitch在条件过多的情况下会导致耦合过高,难以维护。spring状态机通过将条件提取到外部实现解耦。通过定义状态和事件来触发对应的状态转换。若需要更高级的组件,推荐使用工作流框架(activiti等)。

整体流程

1、从redis恢复状态机状态,业务类发送事件

2、guard执行先前校验,通过则进入action(上一个结束的action与本次开始的action,共两个)

3、事件监听器接收事件,变更状态并设置我们自定义的执行上下文状态。

4、业务类收到事件发送回执,投递成功并且执行状态成功则将状态机持久化

Maven依赖

Spring Statemachine - 参考文档

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!-- spring状态机 -->
<dependency>
<groupId>org.springframework.statemachine</groupId>
<artifactId>spring-statemachine-starter</artifactId>
<version>3.2.0</version>
</dependency>
<!-- spring状态机 状态持久化到redis -->
<dependency>
<groupId>org.springframework.statemachine</groupId>
<artifactId>spring-statemachine-data-redis</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- redis连接池 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>

状态枚举类

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
import com.baomidou.mybatisplus.annotation.EnumValue;
import com.fasterxml.jackson.annotation.JsonValue;
import lombok.Getter;

/**
* 订单状态枚举
* @author peter
*/
@Getter
public enum OrderStates {
INITIAL(0, "初始"),
WAITING_PAYMENT(1, "待支付"),
PAID(2, "已支付"),
SHIPPED(3, "发货中"),
DELIVERED(4, "已送达"),
CANCELLED(5, "取消");

@EnumValue
private final Integer code;
@JsonValue
private final String desc;

OrderStates(int code, String desc) {
this.code = code;
this.desc = desc;
}

public static OrderStates getByKey(Integer key) {
for (OrderStates e : values()) {
if (e.getCode().equals(key)) {
return e;
}
}
throw new RuntimeException("enum not exists.");
}
}


事件枚举类

1
2
3
4
5
6
7
8
9
10
11
/**
* 订单事件枚举
* @author peter
*/
public enum OrderEvents {
CREATE_ORDER,
PAY,
SHIP,
DELIVER,
CANCEL
}

配置类

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
108
109
110
111
112
113
114
115
116
import com.event.domain.*;
import org.springframework.context.annotation.Configuration;
import org.springframework.statemachine.config.EnableStateMachine;
import org.springframework.statemachine.config.EnableStateMachineFactory;
import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter;
import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer;
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;


/**
* 订单状态机配置
* @author peter
*/
@Configuration
@EnableStateMachine(name= "orderStateMachine")
public class OrderStateMachineConfig extends EnumStateMachineConfigurerAdapter<OrderStates, OrderEvents> {


@Override
public void configure(StateMachineConfigurationConfigurer<OrderStates, OrderEvents> config) throws Exception {
config
.withConfiguration().machineId("orderStateMachine");
}

/**
* 状态配置
* @param states 状态
* @throws Exception
*/
@Override
public void configure(StateMachineStateConfigurer<OrderStates, OrderEvents> states) throws Exception {
states
.withStates()
.initial(OrderStates.INITIAL,stateContext -> {
// 订单创建和状态机启动时会执行一次,若多次启动会多次执行
System.out.println("订单进入并退出(一次性s)初始状态:INITIAL");
})
.state(OrderStates.WAITING_PAYMENT,stateContext -> {
System.out.println("订单进入待支付状态:WAITING_PAYMENT");
},stateContext -> {
System.out.println("订单退出待支付状态:WAITING_PAYMENT");
})
.state(OrderStates.PAID,stateContext -> {
System.out.println("订单进入已支付状态:PAID");
},stateContext -> {
System.out.println("订单退出已支付状态:PAID");
})
.state(OrderStates.SHIPPED,stateContext -> {
System.out.println("订单进入已发货状态:SHIPPED");
},stateContext -> {
System.out.println("订单退出已发货状态:SHIPPED");
})
.end(OrderStates.DELIVERED)
.end(OrderStates.CANCELLED);
}

/**
* 状态转换
* @param transitions 转换
* @throws Exception
*/
@Override
public void configure(StateMachineTransitionConfigurer<OrderStates, OrderEvents> transitions) throws Exception {
transitions
.withExternal()
// 订单状态:创建 -> 待支付
.source(OrderStates.INITIAL).target(OrderStates.WAITING_PAYMENT)
// 订单创建事件
.event(OrderEvents.CREATE_ORDER)
// 用于在状态转换发生前进行条件校验,只有通过检查才会执行转换。
.guard(context ->{
// 检查,例如id为偶数才允许创建
Order order = (Order) context.getMessageHeader("order");
if ((order.getId() & 1) == 0) {
System.out.println("订单id是偶数!");
return true;
}else {
System.out.println("订单id是奇数!");
return true;
}
})
.and()
.withExternal()
// 订单状态:待支付 -> 已支付
.source(OrderStates.WAITING_PAYMENT).target(OrderStates.PAID)
// 订单支付事件
.event(OrderEvents.PAY)
.and()
.withExternal()
// 订单状态:已支付 -> 已发货
.source(OrderStates.PAID).target(OrderStates.SHIPPED)
// 订单发货事件
.event(OrderEvents.SHIP)
.and()
.withExternal()
// 订单状态:已发货 -> 已送达
.source(OrderStates.SHIPPED).target(OrderStates.DELIVERED)
// 订单送达事件
.event(OrderEvents.DELIVER)
.and()
.withExternal()
// 订单状态:待支付 -> 取消
.source(OrderStates.WAITING_PAYMENT).target(OrderStates.CANCELLED)
// 订单取消事件
.event(OrderEvents.CANCEL)
.and()
.withExternal()
// 订单状态:已支付 -> 取消
.source(OrderStates.PAID).target(OrderStates.CANCELLED)
// 订单取消事件
.event(OrderEvents.CANCEL);
}


}

状态机持久化到Redis配置

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
import com.event.domain.OrderEvents;
import com.event.domain.OrderStates;
import com.event.domain.OtherEvents;
import com.event.domain.OtherStates;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.statemachine.StateMachinePersist;
import org.springframework.statemachine.data.redis.RedisStateMachineContextRepository;
import org.springframework.statemachine.data.redis.RedisStateMachinePersister;
import org.springframework.statemachine.persist.RepositoryStateMachinePersist;
import org.springframework.statemachine.persist.StateMachinePersister;

/**
* @author peter
*/
@Configuration
@Slf4j
public class StateMachinePersistConfig{

@Bean
public RedisConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory();
}

/**
* 官方封装好的持久化实现,相当于Mapper或者Dao
* @param connectionFactory
* @return
*/
@Bean
public StateMachinePersist<OrderStates, OrderEvents, String> stateMachinePersist(RedisConnectionFactory connectionFactory) {
// 使用官方提供的Redis存储实现
RedisStateMachineContextRepository<OrderStates, OrderEvents> repository =
new RedisStateMachineContextRepository<>(connectionFactory);
return new RepositoryStateMachinePersist<>(repository);
}

/**
* 状态机持久化模板
* @param stateMachinePersist
* @return
*/
@Bean
public StateMachinePersister<OrderStates, OrderEvents, String> stateMachinePersister(
StateMachinePersist<OrderStates, OrderEvents, String> stateMachinePersist) {
// 官方提供的持久化操作模板
return new RedisStateMachinePersister<>(stateMachinePersist);
}

/////////////////////////////////////////////////////////////////////
// 多个状态机,要多次配置,下面是多个状态机存储方面(第二个)的示例,仅供参考,使用时根据需要保留。

/**
* 官方封装好的持久化实现,相当于Mapper或者Dao
* @param connectionFactory
* @return
*/
@Bean
public StateMachinePersist<OtherStates, OtherEvents, String> otherStateMachinePersist(RedisConnectionFactory connectionFactory) {
// 使用官方提供的Redis存储实现
RedisStateMachineContextRepository<OtherStates, OtherEvents> repository =
new RedisStateMachineContextRepository<>(connectionFactory);
return new RepositoryStateMachinePersist<>(repository);
}

/**
* 状态机持久化模板
* @param stateMachinePersist
* @return
*/
@Bean
public StateMachinePersister<OtherStates, OtherEvents, String> otherStateMachinePersister(
StateMachinePersist<OtherStates, OtherEvents, String> stateMachinePersist) {
// 官方提供的持久化操作模板
return new RedisStateMachinePersister<>(stateMachinePersist);
}
}

上下文执行状态存储类

由于状态机在发送事件时,返回的只是事件是否投递成功,并不关心是否成功执行。这样对持久化就产生影响,持久化应当只在成功执行后才可执行,所以需要这样一个类来跨类使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.event.config.state;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
* 订单状态机上下文
* @author peter
*/
public class StateMachineContext {
private final static Map<Long, Integer> STATES_MECHINE_CONTEXT = new ConcurrentHashMap<>();

public static void setStateMachineContext(Long stateMachineId, Integer stateMachineContext) {
STATES_MECHINE_CONTEXT.put(stateMachineId, stateMachineContext);
}

public static Integer getStateMachineContext(Long stateMachineId) {
return STATES_MECHINE_CONTEXT.get(stateMachineId);
}

public static void removeStateMachineContext(Long stateMachineId) {
STATES_MECHINE_CONTEXT.remove(stateMachineId);
}
}

事件监听类

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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
import com.event.config.state.StateMachineContext;
import com.event.domain.Order;
import com.event.domain.OrderEvents;
import com.event.domain.OrderStates;
import com.event.mapper.OrderMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.messaging.Message;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.annotation.OnTransition;
import org.springframework.statemachine.annotation.WithStateMachine;
import org.springframework.statemachine.config.StateMachineFactory;
import org.springframework.statemachine.support.DefaultStateMachineContext;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
* 订单状态事件监听
*
* @author peter
* @date 2025/6/23
*/
@Component
@WithStateMachine
public class OrderStateListener {

@Autowired
private OrderMapper orderMapper;

@Transactional(rollbackFor = Exception.class)
@OnTransition(source = "INITIAL", target = "WAITING_PAYMENT")
public void onCreateEvent(Message<OrderEvents> event) {
Order order = (Order) event.getHeaders().get("order");
order.setState(OrderStates.WAITING_PAYMENT);
try {
// 版本号校验(防并发修改)
Order latest = orderMapper.selectById(order.getId());
if (!Objects.equals(latest.getVersion(), order.getVersion())) {
throw new OptimisticLockingFailureException("订单已被其他进程修改");
}

orderMapper.updateById(order);
System.out.println("订单状态机事件:" + event);
StateMachineContext.setStateMachineContext(order.getId(), 1);

}catch (Exception e){
StateMachineContext.setStateMachineContext(order.getId(), 0);

}


}

@Transactional(rollbackFor = Exception.class)
@OnTransition(source = "WAITING_PAYMENT", target = "PAID")
public void onPayEvent(Message<OrderEvents> event) {

Order order = (Order) event.getHeaders().get("order");
order.setState(OrderStates.PAID);
try {
// 版本号校验(防并发修改)
Order latest = orderMapper.selectById(order.getId());
if (!Objects.equals(latest.getVersion(), order.getVersion())) {
throw new OptimisticLockingFailureException("订单已被其他进程修改");
}
orderMapper.updateById(order);
System.out.println("订单状态机事件:" + event);
StateMachineContext.setStateMachineContext(order.getId(), 1);
}catch (Exception e){
StateMachineContext.setStateMachineContext(order.getId(), 0);
}

}

@Transactional(rollbackFor = Exception.class)
@OnTransition(source = "PAID", target = "SHIPPED")
public void onShipEvent(Message<OrderEvents> event) {
Order order = (Order) event.getHeaders().get("order");
order.setState(OrderStates.SHIPPED);
try {
// 版本号校验(防并发修改)
Order latest = orderMapper.selectById(order.getId());
if (!Objects.equals(latest.getVersion(), order.getVersion())) {
throw new OptimisticLockingFailureException("订单已被其他进程修改");
}
orderMapper.updateById(order);
System.out.println("订单状态机事件:" + event);
StateMachineContext.setStateMachineContext(order.getId(), 1);
}catch (Exception e){
StateMachineContext.setStateMachineContext(order.getId(), 0);
}
}

@Transactional(rollbackFor = Exception.class)
@OnTransition(source = "SHIPPED", target = "DELIVERED")
public void onDeliveredEvent(Message<OrderEvents> event) {
Order order = (Order) event.getHeaders().get("order");
order.setState(OrderStates.DELIVERED);
try {
// 版本号校验(防并发修改)
Order latest = orderMapper.selectById(order.getId());
if (!Objects.equals(latest.getVersion(), order.getVersion())) {
throw new OptimisticLockingFailureException("订单已被其他进程修改");
}
orderMapper.updateById(order);
System.out.println("订单状态机事件:" + event);
StateMachineContext.setStateMachineContext(order.getId(), 1);
}catch (Exception e){
StateMachineContext.setStateMachineContext(order.getId(), 0);
}
}

@Transactional(rollbackFor = Exception.class)
@OnTransition(source = "WAITING_PAYMENT", target = "CANCELLED")
public void onWaitPayToCancelEvent(Message<OrderEvents> event) {
Order order = (Order) event.getHeaders().get("order");
order.setState(OrderStates.CANCELLED);
try {
// 版本号校验(防并发修改)
Order latest = orderMapper.selectById(order.getId());
if (!Objects.equals(latest.getVersion(), order.getVersion())) {
throw new OptimisticLockingFailureException("订单已被其他进程修改");
}
orderMapper.updateById(order);
System.out.println("订单状态机事件:" + event);
StateMachineContext.setStateMachineContext(order.getId(), 1);
}catch (Exception e){
StateMachineContext.setStateMachineContext(order.getId(), 0);
}
}

@Transactional(rollbackFor = Exception.class)
@OnTransition(source = "PAID", target = "CANCELLED")
public void onPayToCancelEvent(Message<OrderEvents> event) {
Order order = (Order) event.getHeaders().get("order");
order.setState(OrderStates.CANCELLED);
try {
// 版本号校验(防并发修改)
Order latest = orderMapper.selectById(order.getId());
if (!Objects.equals(latest.getVersion(), order.getVersion())) {
throw new OptimisticLockingFailureException("订单已被其他进程修改");
}
orderMapper.updateById(order);
System.out.println("订单状态机事件:" + event);
StateMachineContext.setStateMachineContext(order.getId(), 1);
}catch (Exception e){
StateMachineContext.setStateMachineContext(order.getId(), 0);
}
}
}

订单类

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
package com.event.domain;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.util.Objects;

import com.baomidou.mybatisplus.annotation.Version;
import lombok.Data;

/**
* @author peter
* @TableName t_order order会与关键字冲突
*/
@TableName(value ="t_order")
@Data
public class Order implements Serializable {

@TableId(type = IdType.INPUT)
private Long id;

private OrderStates state;

// 自动+1需要配置乐观锁插件
@Version
private Integer version;

private static final long serialVersionUID = 1L;

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Order order = (Order) o;
return Objects.equals(id, order.id) && state == order.state && Objects.equals(version, order.version);
}

@Override
public int hashCode() {
return Objects.hash(id, state, version);
}
}

乐观锁插件

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
@MapperScan("com.event.mapper")
@SpringBootApplication
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}

@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();

// 添加乐观锁插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
// 添加防全表更新与删除插件
interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());


// 添加分页插件
// 如果有多数据源可以不配具体类型, 否则都建议配上具体的 DbType,如果配置多个插件, 切记分页最后添加
PaginationInnerInterceptor paginationInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
//设置分页最大容量
paginationInterceptor.setMaxLimit(500L);
interceptor.addInnerInterceptor(paginationInterceptor);


return interceptor;
}
}

订单业务类调用

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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
import com.event.domain.Order;
import com.event.domain.OrderEvents;
import com.event.domain.OrderStates;
import com.event.service.OrderService;
import com.event.mapper.OrderMapper;
import lombok.AllArgsConstructor;
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;

/**
* @author peter
* @description 针对表【order】的数据库操作Service实现
* @createDate 2025-06-23 01:37:11
*/
@Service
@Slf4j
@AllArgsConstructor
public class OrderServiceImpl implements OrderService{

private final OrderMapper orderMapper;
// 状态机操作模板
private final StateMachine<OrderStates, OrderEvents> stateMachine;
// 状态机持久化模板
private final StateMachinePersister<OrderStates, OrderEvents, String> stateMachinePersister;

// 状态机存储在内存中且不依赖外部存储时,可以用这种方式。
//private final Map<Order, StateMachine<OrderStates, OrderEvents>> stateMachines = new ConcurrentHashMap<>();

public boolean createOrder(Order order) {
orderMapper.insert(order);
boolean b = sendEvent(OrderEvents.CREATE_ORDER, order);
if (b) {
System.out.println("订单创建成功");
}else {
System.out.println("订单创建失败");
}

return b;
}

public boolean payOrder(Long orderId) {
Order order = orderMapper.selectById(orderId);

boolean b = sendEvent(OrderEvents.PAY, order);
if (b) {
System.out.println("订单状态修改成功 --> 支付状态");
}else {
System.out.println("订单状态修改失败");
}
return b;
}

public boolean shipOrder(Long orderId) {
Order order = orderMapper.selectById(orderId);
boolean b = sendEvent(OrderEvents.SHIP, order);
if (b) {
System.out.println("订单状态修改成功 --> 发货状态");
}else {
System.out.println("订单状态修改失败");
}
return b;
}

public boolean deliverOrder(Long orderId) {
Order order = orderMapper.selectById(orderId);
boolean b = sendEvent(OrderEvents.DELIVER, order);
if (b) {
System.out.println("订单状态修改成功 --> 送达状态");
}else {
System.out.println("订单状态修改失败");
}
return b;
}

public boolean cancelOrder(Long orderId) {
Order order = orderMapper.selectById(orderId);
boolean b = sendEvent(OrderEvents.CANCEL, order);
if (b) {
System.out.println("订单状态修改成功 --> 取消状态");
}else {
System.out.println("订单状态修改失败");
}
return b;
}

public OrderStates getCurrentState(Long orderId) {
Order order = orderMapper.selectById(orderId);
return order.getState();
}

private synchronized boolean sendEvent(OrderEvents changeEvent, Order order) {
AtomicBoolean result = new AtomicBoolean(false);
boolean flag = false;
try {
String key = "state_machine:order:" + order.getId();
// 启动状态机,启动时会执行一次初始化操作
//stateMachine.start();
System.out.println("格式before:"+ JSON.toJSON(stateMachine));

// 恢复状态机状态
stateMachinePersister.restore(stateMachine, key);

System.out.println("格式after:"+ JSON.toJSON(stateMachine));

Message<OrderEvents> message = MessageBuilder.withPayload(changeEvent).setHeader("order", order).build();

// 只要有事件接收了就返回 true
result.set(stateMachine.sendEvent(message));
// 获取状态机上下文
Integer stateMachineContext = StateMachineContext.getStateMachineContext(order.getId());
flag = Objects.equals(stateMachineContext, 1);
StateMachineContext.removeStateMachineContext(order.getId());
//System.out.println("result:"+result+" stateMachineContext:"+stateMachineContext);


if (result.get() && flag){
System.out.println("订单状态机事件:>>>>>>>>>");
// 持久化状态机状态
stateMachinePersister.persist(stateMachine,key);
}
} catch (Exception e) {
log.error("订单操作失败:{}", e);
} finally {
//stateMachine.stop();
}
return result.get() && flag;
}
}


Spring状态机
https://superlovelace.top/2025/06/27/Spring状态机/
作者
棱境
发布于
2025年6月27日
更新于
2025年6月27日
许可协议