Spring状态机深度解析:从入门到生产实战
Spring State Machine是Spring生态系统中一个强大的状态机框架,它让复杂的状态流转变得优雅而简单。本文将带你从基础概念出发,逐步深入理解并掌握Spring状态机在实际生产环境中的应用。
一、状态机是什么?为什么要用它?
想象一下订单系统:用户下单后,订单会经历"待支付→已支付→待发货→已发货→已完成"等一系列状态变化。如果在代码里用if-else来处理这些状态流转,很快就会变成一团乱麻。
状态机(State Machine)就是解决这类问题的利器!它明确定义了:
- 状态(State):系统可能处于的状态
- 事件(Event):触发状态变化的动作
- 转换(Transition):状态之间的流转规则

二、Spring状态机核心概念
2.1 三大核心组件
java
// 1. 定义状态枚举
public enum OrderStatus {
WAIT_PAYMENT, // 待支付
PAID, // 已支付
WAIT_DELIVER, // 待发货
DELIVERED, // 已发货
COMPLETED, // 已完成
CANCELLED // 已取消
}
// 2. 定义事件枚举
public enum OrderEvent {
PAY, // 支付
DELIVER, // 发货
RECEIVE, // 收货
CANCEL // 取消
}
// 3. 配置状态机
@Configuration
@EnableStateMachine
public class OrderStateMachineConfig
extends StateMachineConfigurerAdapter<OrderStatus, OrderEvent> {
@Override
public void configure(StateMachineStateConfigurer<OrderStatus, OrderEvent> states)
throws Exception {
states.withStates()
.initial(OrderStatus.WAIT_PAYMENT)
.states(EnumSet.allOf(OrderStatus.class));
}
@Override
public void configure(StateMachineTransitionConfigurer<OrderStatus, OrderEvent> transitions)
throws Exception {
transitions
.withExternal()
.source(OrderStatus.WAIT_PAYMENT)
.target(OrderStatus.PAID)
.event(OrderEvent.PAY)
.and()
.withExternal()
.source(OrderStatus.PAID)
.target(OrderStatus.WAIT_DELIVER)
.event(OrderEvent.DELIVER)
.and()
.withExternal()
.source(OrderStatus.WAIT_DELIVER)
.target(OrderStatus.DELIVERED)
.event(OrderEvent.RECEIVE)
.and()
.withExternal()
.source(OrderStatus.DELIVERED)
.target(OrderStatus.COMPLETED)
.event(OrderEvent.RECEIVE);
}
}

2.2 状态持久化
生产环境中,状态必须持久化。Spring状态机支持多种持久化方式:
java
@Service
@RequiredArgsConstructor
public class OrderService {
private final StateMachineFactory<OrderStatus, OrderEvent> factory;
private final StateMachinePersist<OrderStatus, OrderEvent, String> persist;
public boolean pay(String orderId) {
StateMachine<OrderStatus, OrderEvent> sm = restoreStateMachine(orderId);
boolean result = sm.sendEvent(OrderEvent.PAY);
if (result) {
persistStateMachine(orderId, sm);
// 发送支付成功消息
publishPaymentSuccessEvent(orderId);
}
return result;
}
private StateMachine<OrderStatus, OrderEvent> restoreStateMachine(String orderId) {
try {
return persist.restore(factory.getStateMachine(), orderId);
} catch (Exception e) {
throw new RuntimeException("恢复状态机失败", e);
}
}
private void persistStateMachine(String orderId, StateMachine<OrderStatus, OrderEvent> sm) {
try {
persist.persist(sm, orderId);
} catch (Exception e) {
throw new RuntimeException("保存状态机失败", e);
}
}
}

三、生产实战:工作流引擎
让我们看一个更复杂的例子------审批工作流系统:
java
// 支持并行审批的复杂状态机
@Configuration
@EnableStateMachine(name = "workflowStateMachine")
public class WorkflowStateMachineConfig
extends EnumStateMachineConfigurerAdapter<WorkflowState, WorkflowEvent> {
@Override
public void configure(StateMachineStateConfigurer<WorkflowState, WorkflowEvent> states)
throws Exception {
states
.withStates()
.initial(WorkflowState.DRAFT)
.fork(WorkflowState.FORK)
.join(WorkflowState.JOIN)
.state(WorkflowState.FINISHED)
.and()
.withStates()
.parent(WorkflowState.FORK)
.initial(WorkflowState.DEPT_APPROVAL)
.state(WorkflowState.DEPT_APPROVED)
.and()
.withStates()
.parent(WorkflowState.FORK)
.initial(WorkflowState.FINANCE_APPROVAL)
.state(WorkflowState.FINANCE_APPROVED);
}
@Override
public void configure(StateMachineTransitionConfigurer<WorkflowState, WorkflowEvent> transitions)
throws Exception {
transitions
// 提交到并行审批
.withExternal()
.source(WorkflowState.DRAFT)
.target(WorkflowState.FORK)
.event(WorkflowEvent.SUBMIT)
// 部门审批分支
.and()
.withExternal()
.source(WorkflowState.DEPT_APPROVAL)
.target(WorkflowState.DEPT_APPROVED)
.event(WorkflowEvent.DEPT_APPROVE)
// 财务审批分支
.and()
.withExternal()
.source(WorkflowState.FINANCE_APPROVAL)
.target(WorkflowState.FINANCE_APPROVED)
.event(WorkflowEvent.FINANCE_APPROVE)
// 合并后完成
.and()
.withExternal()
.source(WorkflowState.JOIN)
.target(WorkflowState.FINISHED)
.event(WorkflowEvent.COMPLETE);
}
}

四、状态监听器:记录每一次变化

javan@Component
@WithStateMachine
public class OrderStateListener {
private static final Logger log = LoggerFactory.getLogger(OrderStateListener.class);
@OnTransition(target = "PAID")
public void onPay(Message<OrderEvent> message) {
String orderId = getHeader(message, "orderId");
log.info("订单{}支付成功,状态流转到已支付", orderId);
// 触发后续业务逻辑
paymentSuccessHandler.handle(orderId);
}
@OnTransition(target = "DELIVERED")
public void onDeliver(Message<OrderEvent> message) {
String orderId = getHeader(message, "orderId");
log.info("订单{}已发货,状态流转到已发货", orderId);
// 发送短信通知
smsService.sendDeliverySms(orderId);
}
@OnTransitionEnd
public void onTransitionEnd(StateContext<OrderStatus, OrderEvent> context) {
log.info("状态转换完成:{} -> {}, 事件:{}",
context.getSource().getId(),
context.getTarget().getId(),
context.getEvent()
);
// 持久化状态转换记录
transitionLogService.log(context);
}
private String getHeader(Message<OrderEvent> message, String headerName) {
return message.getHeaders().get(headerName, String.class);
}
}
五、Guards:智能的状态转换守卫

java
@Component
public class OrderGuard {
@Bean
public Guard<OrderStatus, OrderEvent> payGuard() {
return context -> {
String orderId = context.getMessageHeader("orderId");
BigDecimal amount = orderService.getOrderAmount(orderId);
// 检查订单金额
if (amount.compareTo(BigDecimal.ZERO) <= 0) {
log.warn("订单{}支付失败:金额为0", orderId);
return false;
}
// 检查库存
boolean hasStock = inventoryService.checkStock(orderId);
if (!hasStock) {
log.warn("订单{}支付失败:库存不足", orderId);
return false;
}
return true;
};
}
}
// 在状态机配置中使用guard
@Override
public void configure(StateMachineTransitionConfigurer<OrderStatus, OrderEvent> transitions)
throws Exception {
transitions
.withExternal()
.source(OrderStatus.WAIT_PAYMENT)
.target(OrderStatus.PAID)
.event(OrderEvent.PAY)
.guard(payGuard()); // 添加守卫条件
}
六、实战技巧与最佳实践
6.1 状态机可视化
javan@RestController
@RequestMapping("/state-machine")
public class StateMachineVisualController {
@GetMapping("/diagram/{orderId}")
public ResponseEntity<String> getStateDiagram(@PathVariable String orderId) {
// 获取当前状态
OrderStatus currentStatus = orderService.getOrderStatus(orderId);
// 生成PlantUML格式的状态图
String diagram = generatePlantUMLDiagram(currentStatus);
return ResponseEntity.ok()
.contentType(MediaType.TEXT_PLAIN)
.body(diagram);
}
private String generatePlantUMLDiagram(OrderStatus currentStatus) {
StringBuilder sb = new StringBuilder();
sb.append("@startuml\n");
sb.append("[*] --> WAIT_PAYMENT\n");
sb.append("WAIT_PAYMENT --> PAID : PAY\n");
sb.append("PAID --> WAIT_DELIVER : DELIVER\n");
sb.append("WAIT_DELIVER --> DELIVERED : RECEIVE\n");
sb.append("DELIVERED --> COMPLETED : RECEIVE\n");
// 高亮当前状态
sb.append("skinparam state {\n");
sb.append(" BackgroundColor<<Current>> LightBlue\n");
sb.append("}\n");
sb.append("state ").append(currentStatus).append(" <<Current>>\n");
sb.append("@enduml\n");
return sb.toString();
}
}
6.2 分布式状态一致性

java
// 使用分布式锁确保状态转换的原子性
@Service
public class DistributedOrderService {
private final RedissonClient redisson;
private final StateMachineFactory<OrderStatus, OrderEvent> factory;
public boolean transition(String orderId, OrderEvent event) {
RLock lock = redisson.getLock("order:state:" + orderId);
try {
// 最多等待3秒,持锁10秒
if (lock.tryLock(3, 10, TimeUnit.SECONDS)) {
// 恢复状态机
StateMachine<OrderStatus, OrderEvent> sm = restoreStateMachine(orderId);
// 发送事件并处理结果
boolean result = sm.sendEvent(event);
if (result) {
// 持久化新状态
persistStateMachine(orderId, sm);
// 发布领域事件
publishDomainEvent(orderId, event, sm.getState().getId());
}
return result;
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("状态转换被中断", e);
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
return false;
}
}

七、性能优化建议
- 状态机缓存:频繁使用的状态机实例可以缓存,避免重复创建
- 异步事件处理:使用Spring的事件驱动模型异步处理状态变化
- 批量持久化:多个状态变化可以合并为一次数据库操作
- 读写分离:状态查询走从库,状态更新走主库
八、总结
Spring状态机的优势在于:
- 代码清晰:将复杂的状态流转从业务代码中分离
- 易于维护:状态转换规则集中管理
- 可测试性强:可以单独测试状态机逻辑
- 生产就绪:支持持久化、监听、分布式等高级特性
当你的业务涉及复杂的状态流转时,Spring状态机绝对是你的得力助手。它让状态管理变得优雅,让代码更容易理解和维护。