状态机(State Machine)是一种数学模型,用于描述系统在不同状态之间转换的行为。它通过定义一系列有限的状态以及状态之间的转换规则,来模拟现实世界或抽象系统的动态行为。每个状态代表系统可能存在的条件或阶段,而状态间的转换则是由特定的输入(即事件)触发的。
状态机可以分为有限状态机(Finite State Machine,FSM)和无限状态机(Infinite State Machine)两种。有限状态机是指状态的数量是有限的,而无限状态机则可以有无限多个状态。在系统设计中,有限状态机更为常见。
Spring State Machine是Spring生态系统中的一个框架,它提供了实现状态机模式的完整解决方案。它建立在有限状态机(FSM)的概念之上,提供了一种简洁且灵活的方式来定义、管理和执行状态机。
Spring State Machine的核心主要包括以下三个关键元素:
Spring State Machine将状态定义为Java对象,并通过配置来定义状态之间的转换规则。状态转换通常由外部事件触发,可以根据业务逻辑定义不同的事件类型,并与状态转换关联。它还提供了状态监听器,用于在状态变化时执行特定的逻辑。同时,状态机的状态可以持久化到数据库或其他存储介质中,以便在系统重启或故障恢复时保持状态的一致性。
接下来,我们将通过一个具体的例子来说明如何使用Spring State Machine来实现订单状态的流转。
首先,需要引入Spring State Machine的依赖:
<dependency>
<groupId>org.springframework.statemachinegroupId>
<artifactId>spring-statemachine-starterartifactId>
<version>2.2.1.RELEASEversion>
dependency>
定义订单状态和触发状态转换的事件:
/**
* 订单状态
*/
public enum OrderStatusEnum {
/**待提交*/
DRAFT,
/**待出库*/
SUBMITTED,
/**已出库*/
DELIVERING,
/**已签收*/
SIGNED,
/**已完成*/
FINISHED,
;
}
/**
* 订单状态流转事件
*/
public enum OrderStatusOperateEventEnum {
/**确认,已提交*/
CONFIRMED,
/**发货*/
DELIVERY,
/**签收*/
RECEIVED,
/**完成*/
CONFIRMED_FINISH,
;
}
配置状态机的状态和转换规则:
@Configuration
@EnableStateMachine(name = "orderStateMachine")
public class OrderStatusMachineConfig extends StateMachineConfigurerAdapter<OrderStatusEnum, OrderStatusOperateEventEnum> {
/**
* 设置状态机的状态
*/
@Override
public void configure(StateMachineStateConfigurer<OrderStatusEnum, OrderStatusOperateEventEnum> states) throws Exception {
states.withStates()
.initial(OrderStatusEnum.DRAFT)
.end(OrderStatusEnum.FINISHED)
.states(EnumSet.allOf(OrderStatusEnum.class));
}
/**
* 设置状态机与订单状态操作事件绑定
*/
@Override
public void configure(StateMachineTransitionConfigurer<OrderStatusEnum, OrderStatusOperateEventEnum> transitions) throws Exception {
transitions.withExternal().source(OrderStatusEnum.DRAFT).target(OrderStatusEnum.SUBMITTED)
.event(OrderStatusOperateEventEnum.CONFIRMED)
.and()
.withExternal().source(OrderStatusEnum.SUBMITTED).target(OrderStatusEnum.DELIVERING)
.event(OrderStatusOperateEventEnum.DELIVERY)
.and()
.withExternal().source(OrderStatusEnum.DELIVERING).target(OrderStatusEnum.SIGNED)
.event(OrderStatusOperateEventEnum.RECEIVED)
.and()
.withExternal().source(OrderStatusEnum.SIGNED).target(OrderStatusEnum.FINISHED)
.event(OrderStatusOperateEventEnum.CONFIRMED_FINISH);
}
}
配置状态机的持久化,保证状态信息不会丢失:
@Configuration
public class OrderPersist {
/**
* 持久化配置
* 在实际使用中,可以配合数据库或者Redis等进行持久化操作
*/
@Bean
public DefaultStateMachinePersister<OrderStatusEnum, OrderStatusOperateEventEnum, OrderDO> stateMachinePersister(){
Map<OrderDO, StateMachineContext<OrderStatusEnum, OrderStatusOperateEventEnum>> map = new HashMap();
return new DefaultStateMachinePersister<>(new StateMachinePersist<OrderStatusEnum, OrderStatusOperateEventEnum, OrderDO>() {
@Override
public void write(StateMachineContext<OrderStatusEnum, OrderStatusOperateEventEnum> context, OrderDO order) throws Exception {
//持久化操作
map.put(order, context);
}
@Override
public StateMachineContext<OrderStatusEnum, OrderStatusOperateEventEnum> read(OrderDO order) throws Exception {
//从库中或者redis中读取order的状态信息
return map.get(order);
}
});
}
}
实现状态监听器,监听状态变化并执行相应的业务逻辑:
@Component
@WithStateMachine(name = "orderStateMachine")
public class OrderStatusListener {
@OnTransition(source = "DRAFT", target = "SUBMITTED")
public boolean payTransition(Message<OrderStatusOperateEventEnum> message) {
OrderDO order = (OrderDO) message.getHeaders().get("order");
order.setOrderStatusEnum(OrderStatusEnum.SUBMITTED);
System.out.println(String.format("出库订单[%s]确认,状态机信息:%s", order.getOrderNo(), message.getHeaders()));
return true;
}
@OnTransition(source = "SUBMITTED", target = "DELIVERING")
public boolean deliverTransition(Message<OrderStatusOperateEventEnum> message) {
OrderDO order = (OrderDO) message.getHeaders().get("order");
order.setOrderStatusEnum(OrderStatusEnum.DELIVERING);
System.out.println(String.format("出库订单[%s]发货出库,状态机信息:%s", order.getOrderNo(), message.getHeaders()));
return true;
}
@OnTransition(source = "DELIVERING", target = "SIGNED")
public boolean receiveTransition(Message<OrderStatusOperateEventEnum> message){
OrderDO order = (OrderDO) message.getHeaders().get("order");
order.setOrderStatusEnum(OrderStatusEnum.SIGNED);
System.out.println(String.format("出库订单[%s]签收,状态机信息:%s", order.getOrderNo(), message.getHeaders()));
return true;
}
@OnTransition(source = "SIGNED", target = "FINISHED")
public boolean finishTransition(Message<OrderStatusOperateEventEnum> message){
OrderDO order = (OrderDO) message.getHeaders().get("order");
order.setOrderStatusEnum(OrderStatusEnum.FINISHED);
System.out.println(String.format("出库订单[%s]完成,状态机信息:%s", order.getOrderNo(), message.getHeaders()));
return true;
}
}
创建状态事件的工具类,用于发送状态转换事件:
@Component
public class StateEventUtil {
private StateMachine<OrderStatusEnum, OrderStatusOperateEventEnum> orderStateMachine;
private StateMachinePersister<OrderStatusEnum, OrderStatusOperateEventEnum, OrderDO> stateMachinePersister;
/**
* 发送状态转换事件
* synchronized修饰保证这个方法是线程安全的
*/
public synchronized boolean sendEvent(Message<OrderStatusOperateEventEnum> message) {
boolean result = false;
try {
//启动状态机
orderStateMachine.start();
OrderDO order = (OrderDO) message.getHeaders().get("order");
//尝试恢复状态机状态
stateMachinePersister.restore(orderStateMachine, order);
result = orderStateMachine.sendEvent(message);
//持久化状态机状态
stateMachinePersister.persist(orderStateMachine, order);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (Objects.nonNull(message)) {
OrderDO order = (OrderDO) message.getHeaders().get("order");
if (Objects.nonNull(order) && Objects.equals(order.getOrderStatusEnum(), OrderStatusEnum.FINISHED)) {
orderStateMachine.stop();
}
}
}
return result;
}
@Autowired
public void setOrderStateMachine(StateMachine<OrderStatusEnum, OrderStatusOperateEventEnum> orderStateMachine) {
this.orderStateMachine = orderStateMachine;
}
@Autowired
public void setStateMachinePersister(StateMachinePersister<OrderStatusEnum, OrderStatusOperateEventEnum, OrderDO> stateMachinePersister) {
this.stateMachinePersister = stateMachinePersister;
}
}
实现订单服务,处理订单状态流转的业务逻辑:
@Service
public class OrderServiceImpl implements IOrderService {
private StateEventUtil stateEventUtil;
private static final AtomicInteger ID_COUNTER = new AtomicInteger(0);
private static final Map<Long, OrderDO> ORDER_MAP = new ConcurrentHashMap<>();
/**
* 创建新订单
*/
@Override
public Long createOrder(OrderDO orderDO) {
long orderId = ID_COUNTER.incrementAndGet();
orderDO.setOrderId(orderId);
orderDO.setOrderNo("OC20240306" + orderId);
orderDO.setOrderStatusEnum(OrderStatusEnum.DRAFT);
ORDER_MAP.put(orderId, orderDO);
System.out.println(String.format("订单[%s]创建成功:", orderDO.getOrderNo()));
return orderId;
}
/**
* 确认订单
*/
@Override
public void confirmOrder(Long orderId) {
OrderDO order = ORDER_MAP.get(orderId);
System.out.println("确认订单,订单号:" + order.getOrderNo());
Message message = MessageBuilder.withPayload(OrderStatusOperateEventEnum.CONFIRMED).
setHeader("order", order).build();
if (!stateEventUtil.sendEvent(message)) {
System.out.println(" 确认订单失败, 状态异常,订单号:" + order.getOrderNo());
}
}
/**
* 订单发货
*/
@Override
public void deliver(Long orderId) {
OrderDO order = ORDER_MAP.get(orderId);
System.out.println("订单出库,订单号:" + order.getOrderNo());
Message message = MessageBuilder.withPayload(OrderStatusOperateEventEnum.DELIVERY).
setHeader("order", order).build();
if (!stateEventUtil.sendEvent(message)) {
System.out.println(" 订单出库失败, 状态异常,订单号:" + order.getOrderNo());
}
}
/**
* 签收订单
*/
@Override
public void signOrder(Long orderId) {
OrderDO order = ORDER_MAP.get(orderId);
System.out.println("订单签收,订单号:" + order.getOrderNo());
Message message = MessageBuilder.withPayload(OrderStatusOperateEventEnum.RECEIVED).
setHeader("order", order).build();
if (!stateEventUtil.sendEvent(message)) {
System.out.println(" 订单签收失败, 状态异常,订单号:" + order.getOrderNo());
}
}
/**
* 确认完成
*/
@Override
public void finishOrder(Long orderId) {
OrderDO order = ORDER_MAP.get(orderId);
System.out.println("订单完成,订单号:" + order.getOrderNo());
Message message = MessageBuilder.withPayload(OrderStatusOperateEventEnum.CONFIRMED_FINISH).
setHeader("order", order).build();
if (!stateEventUtil.sendEvent(message)) {
System.out.println(" 订单完成失败, 状态异常,订单号:" + order.getOrderNo());
}
}
/**
* 获取所有订单信息
*/
@Override
public List<OrderDO> listOrders() {
return new ArrayList<>(ORDER_MAP.values());
}
@Autowired
public void setStateEventUtil(StateEventUtil stateEventUtil) {
this.stateEventUtil = stateEventUtil;
}
}
定义接口控制器,提供测试接口:
@RestController
public class OrderController {
private IOrderService orderService;
@GetMapping("testOrderStatusMachine")
public void testOrderStatusMachine(){
Long orderId1 = orderService.createOrder(new OrderDO());
Long orderId2 = orderService.createOrder(new OrderDO());
orderService.confirmOrder(orderId1);
new Thread("客户线程"){
@Override
public void run() {
orderService.deliver(orderId1);
orderService.signOrder(orderId1);
orderService.finishOrder(orderId1);
}
}.start();
orderService.confirmOrder(orderId2);
orderService.deliver(orderId2);
orderService.signOrder(orderId2);
orderService.finishOrder(orderId2);
System.out.println("全部订单状态:" + orderService.listOrders());
}
@Autowired
public void setOrderService(IOrderService orderService) {
this.orderService = orderService;
}
}
调用接口后,可以看到订单状态在状态机的控制下流转的过程。日志输出表明订单状态的变化是符合预期的,状态机工作正常。
除了使用Spring State Machine这样的专门工具外,还可以使用其他方法实现状态机功能:
状态的变更通过发布和消费消息来驱动。每当发生状态变更所需的事件时,生产者将事件作为一个消息发布到特定的消息队列(Topic),而消费者则监听这些消息,根据消息内容和业务规则对订单状态进行更新。这种方式有利于解耦各个服务,实现异步处理,同时增强系统的伸缩性和容错能力。
使用定时任务定期检查系统中的订单状态,根据预设的业务规则判断是否满足状态变迁条件。例如,每隔一段时间执行一次Job,查询数据库中处于特定状态的订单,并决定是否进行状态更新。这种方法适用于具有一定时效性的状态变迁,但实时性相对较低,对于瞬时响应要求高的场景不太适用。
利用规则引擎(如Drools、LiteFlow等)实现状态机,业务团队可以直接在规则引擎中定义状态及状态之间的转换规则,当新的事实数据(如订单信息)输入到规则引擎时,引擎会自动匹配并执行相应的规则,触发状态改变。这种方式的优点在于业务规则高度集中,易于管理和修改,同时也具备较高的灵活性,能够快速应对业务规则的变化。
以下是完整的项目结构,方便理解各个组件之间的关系:
src/main/java/com/example/statemachine/
├── config
│ ├── OrderPersist.java // 状态机持久化配置
│ └── OrderStatusMachineConfig.java // 状态机状态和转换配置
├── controller
│ └── OrderController.java // 接口控制器
├── entity
│ └── OrderDO.java // 订单实体
├── enums
│ ├── OrderStatusEnum.java // 订单状态枚举
│ └── OrderStatusOperateEventEnum.java // 订单状态操作事件枚举
├── listener
│ └── OrderStatusListener.java // 状态监听器
├── service
│ ├── IOrderService.java // 订单服务接口
│ └── impl
│ └── OrderServiceImpl.java // 订单服务实现
└── util
└── StateEventUtil.java // 状态事件工具类
状态机是一种强大的设计模式,它可以帮助我们有效地管理系统中的状态转换逻辑。Spring State Machine提供了一个完善的框架来实现这种模式,特别适合于订单处理、工作流引擎等需要复杂状态管理的场景。
通过本文的实践,我们了解了如何使用Spring State Machine来实现订单状态的流转,包括状态机的配置、状态转换的触发、状态的持久化以及状态监听器的使用。同时,我们也探讨了其他实现状态机功能的方法,如消息队列、定时任务和规则引擎等,这些方法各有优缺点,可以根据实际业务需求选择合适的实现方式。
无论选择哪种实现方式,关键是保持代码的清晰结构和良好的可维护性,确保状态转换逻辑的正确性和一致性,这样才能够在复杂的业务场景中有效地管理系统状态。