在 Spring Boot 中实现状态机,通常使用 Spring State Machine 框架进行实现。该框架可以帮助我们定义、创建和管理复杂的状态机。
接下来给出一个demo,说明如何使用 Spring State Machine 框架实现状态机。
假设我们需要实现一个简单的订单状态机,订单的状态可以是已创建、已确认、已发货、已完成和已取消。在订单生命周期中,可以有以下事件:
- 确认订单(confirmOrder):从已创建状态转换到已确认状态。
- 发货(shipOrder):从已确认状态转换到已发货状态。
- 确认收货(receiveOrder):从已发货状态转换到已完成状态。
- 取消订单(cancelOrder):从已创建或已确认状态转换到已取消状态。
现在让我们来实现这个订单状态机。
在 pom.xml 文件中添加以下依赖:
```xml
```
对于该状态机,我们可以定义以下枚举:状态以及事件
```java
public enum States {
CREATED, CONFIRMED, SHIPPED, COMPLETED, CANCELLED
}
```
```java
public enum Events {
CONFIRM_ORDER, SHIP_ORDER, RECEIVE_ORDER, CANCEL_ORDER
}
```
创建一个状态机配置类,用于管理状态机的定义和创建。从 StateMachineConfigurerAdapter 继承,并添加 @Configuration 和 @EnableStateMachine 注解。
```java
@Configuration
@EnableStateMachine
public class OrderStateMachineConfig extends StateMachineConfigurerAdapter
@Override
public void configure(StateMachineStateConfigurer
states
.withStates()
.initial(States.CREATED)
.state(States.CONFIRMED)
.state(States.SHIPPED)
.state(States.COMPLETED)
.state(States.CANCELLED)
.end(States.COMPLETED)
.end(States.CANCELLED);
}
@Override
public void configure(StateMachineTransitionConfigurer
transitions
.withExternal()
.source(States.CREATED)
.target(States.CONFIRMED)
.event(Events.CONFIRM_ORDER)
.and()
.withExternal()
.source(States.CONFIRMED)
.target(States.SHIPPED)
.event(Events.SHIP_ORDER)
.and()
.withExternal()
.source(States.SHIPPED)
.target(States.COMPLETED)
.event(Events.RECEIVE_ORDER)
.and()
.withExternal()
.source(States.CREATED)
.target(States.CANCELLED)
.event(Events.CANCEL_ORDER)
.and()
.withExternal()
.source(States.CONFIRMED)
.target(States.CANCELLED)
.event(Events.CANCEL_ORDER);
}
}
```
在上述通过 Config 注解启用了 State Machine 的例子中,我们定义了状态机的状态和事件转换,分别是通过 configure 方法实现 StateMachineStateConfigurer 和 StateMachineTransitionConfigurer。我们定义了订单状态和事件,定义了每个状态可以发生的转换,其中 withExternal() 表示外部转换。
接下来,我们创建一个状态机服务,该服务将使用上述状态机配置进行初始化,然后使用状态机按照规定的转换流程进行状态转换。
```java
@Service
public class OrderStateMachineService {
@Autowired
private StateMachine
public boolean sendEvent(final String orderId, final Events event) {
stateMachine.start();
Message
.setHeader("orderId", orderId)
.build();
return stateMachine.sendEvent(message);
}
}
```
我们注入了 StateMachine
我们测试一下:
```java
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private OrderStateMachineService orderStateMachineService;
@PostMapping("/confirm")
public boolean confirmOrder(@RequestBody final Order order) {
return orderStateMachineService.sendEvent(order.getId(), Events.CONFIRM_ORDER);
}
@PostMapping("/ship")
public boolean shipOrder(@RequestBody final Order order) {
return orderStateMachineService.sendEvent(order.getId(), Events.SHIP_ORDER);
}
@PostMapping("/receive")
public boolean receiveOrder(@RequestBody final Order order) {
return orderStateMachineService.sendEvent(order.getId(), Events.RECEIVE_ORDER);
}
@PostMapping("/cancel")
public boolean cancelOrder(@RequestBody final Order order) {
return orderStateMachineService.sendEvent(order.getId(), Events.CANCEL_ORDER);
}
}
```
测试结果如下:
1、以上接口依次调用,程序正常;
2、不按顺序调用,会报错
1、这种实现方式会受集群部署影响,因为这些状态是存在内存中的,当然可以通过其它方式解决,比如持久化道数据库中;
2、如果订单周期比较长,并且数据量比较大,放在内存中,会引发oom;