Spring状态机的使用解读

 更新时间:2026年01月01日 17:03:16   作者:雨中飘荡的记忆  
本文详细介绍了Spring状态机的基本概念、核心组件以及在生产环境中的应用,通过订单系统和审批工作流系统的例子,展示了状态机如何简化状态流转处理,此外,还介绍了状态监听器、Guards、可视化、分布式状态一致性、性能优化等技巧,并总结了Spring状态机的优势

Spring状态机深度解析:从入门到生产实战

Spring State Machine是Spring生态系统中一个强大的状态机框架,它让复杂的状态流转变得优雅而简单。本文将带你从基础概念出发,逐步深入理解并掌握Spring状态机在实际生产环境中的应用。

一、状态机是什么?为什么要用它?

想象一下订单系统:用户下单后,订单会经历"待支付→已支付→待发货→已发货→已完成"等一系列状态变化。如果在代码里用if-else来处理这些状态流转,很快就会变成一团乱麻。

状态机(State Machine)就是解决这类问题的利器!它明确定义了:

  • 状态(State):系统可能处于的状态
  • 事件(Event):触发状态变化的动作
  • 转换(Transition):状态之间的流转规则

二、Spring状态机核心概念

2.1 三大核心组件

// 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状态机支持多种持久化方式:

@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);
        }
    }
}

三、生产实战:工作流引擎

让我们看一个更复杂的例子——审批工作流系统:

// 支持并行审批的复杂状态机
@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);
    }
}

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

@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:智能的状态转换守卫

@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 状态机可视化

@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 分布式状态一致性

// 使用分布式锁确保状态转换的原子性
@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;
    }
}

七、性能优化建议

  1. 状态机缓存:频繁使用的状态机实例可以缓存,避免重复创建
  2. 异步事件处理:使用Spring的事件驱动模型异步处理状态变化
  3. 批量持久化:多个状态变化可以合并为一次数据库操作
  4. 读写分离:状态查询走从库,状态更新走主库

八、总结

Spring状态机的优势在于:

  • 代码清晰:将复杂的状态流转从业务代码中分离
  • 易于维护:状态转换规则集中管理
  • 可测试性强:可以单独测试状态机逻辑
  • 生产就绪:支持持久化、监听、分布式等高级特性

当你的业务涉及复杂的状态流转时,Spring状态机绝对是你的得力助手。它让状态管理变得优雅,让代码更容易理解和维护。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Java语言描述存储结构与邻接矩阵代码示例

    Java语言描述存储结构与邻接矩阵代码示例

    这篇文章主要介绍了Java语言描述存储结构与邻接矩阵代码示例,涉及Java存储结构,邻接矩阵,邻接表的介绍与比较,然后分享了邻接矩阵的Java实现等相关内容,具有一定借鉴价值,需要的朋友可以参考。
    2017-11-11
  • JAVA8 lambda表达式权威教程

    JAVA8 lambda表达式权威教程

    本文主要给大家讲解Java8中最重要的一个特征之一lambda表达式,本文通过实例图文解说给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友跟随小编一起学习下吧
    2021-05-05
  • SpringBoot与Postman实现REST模拟请求的操作

    SpringBoot与Postman实现REST模拟请求的操作

    这篇文章主要介绍了SpringBoot与Postman实现REST模拟请求的操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-06-06
  • Java中创建线程的四种方式的最佳实践

    Java中创建线程的四种方式的最佳实践

    这篇文章主要为大家详细介绍了Java中创建线程的四种方式的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2025-08-08
  • Flink流处理引擎零基础速通之数据的抽取篇

    Flink流处理引擎零基础速通之数据的抽取篇

    今天不分享基础概念知识了,来分享一个马上工作需要的场景,要做数据的抽取,不用kettle,想用flink。实际就是flink的sql、table层级的api
    2022-05-05
  • 详解Gradle安装并配置到IDEA的方法

    详解Gradle安装并配置到IDEA的方法

    这篇文章主要介绍了Gradle安装并配置到IDEA的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-09-09
  • 超详细讲解Java线程池

    超详细讲解Java线程池

    本文主要介绍了Java线程池,本文运用大量代码和图片讲解相关知识,感兴趣的小伙伴一起来看看吧
    2021-09-09
  • java多线程导入excel的方法

    java多线程导入excel的方法

    最近项目写了poi导入excel数据到数据库,想把学到的知识用于实践,于是使用多线程方式导入excel,需要的朋友们下面随着小编来一起学习学习吧
    2021-05-05
  • Java for-each循环(遍历循环)详解

    Java for-each循环(遍历循环)详解

    本文详解Java for-each循环,涵盖其简化遍历、类型安全、不可变性、空指针防护等特性,适用场景及限制,错误案例与修正,性能对比,高级技巧(如Java14 Records),并提供最佳实践指南
    2025-07-07
  • 基于springboot实现redis分布式锁的方法

    基于springboot实现redis分布式锁的方法

    这篇文章主要介绍了基于springboot实现redis分布式锁的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-11-11

最新评论