news 2026/9/26 7:31:36

SpringBoot集成Spring Statemachine(状态机)实战教程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SpringBoot集成Spring Statemachine(状态机)实战教程

背景

本文将基于借款订单状态流转这个场景来实现。如果使用if-else或者switch语句来处理这些状态,代码会变得非常臃肿且难以维护。而状态机提供了一种更加结构化和可维护的方式来管理这些状态转换。

示例中涉及到:状态机的配置、数据持久化、状态恢复查询、同一事件由同一sourceStatus流转到不同targetStatus

SpringBoot集成状态机

1、首先,需要添加Spring Statemachine的依赖到Spring Boot项目的pom.xml文件中:

<dependency> <groupId>org.springframework.statemachine</groupId> <artifactId>spring-statemachine-core</artifactId> <version>4.0.0</version> </dependency>

2、定义系统中订单存在的状态

public enum OrderStatusEnum { // 待审核 APPROVE_PENDING, // 审核中 APPROVE_ING, // 审核失败 APPROVE_FAILED, // 审核成功 APPROVE_SUCCESS; }

3、定义系统中触发状态变更的事件

public enum OrderEvent { // 开始审核 APPROVE_START, // 审核通过 APPROVE_SUCCESS, // 审核失败 APPROVE_FAILED; }

4、状态机-状态流转配置

@Configuration @EnableStateMachine(name = "OrderStateMachine") @Slf4j publicclass OrderStateMachineConfig extends EnumStateMachineConfigurerAdapter<OrderStatusEnum, OrderEvent> { @Resource private OrderMapper orderMapper; /** * 配置状态 * * @param states * @throws Exception */ @Override public void configure(StateMachineStateConfigurer<OrderStatusEnum, OrderEvent> states) throws Exception { states.withStates() .initial(OrderStatusEnum.LOAN_PENDING) // 设置初始状态为[待审核] .states(EnumSet.allOf(OrderStatusEnum.class)); } /** * 配置状态转换事件关系 * * @param transitions * @throws Exception */ @Override public void configure(StateMachineTransitionConfigurer<OrderStatusEnum, OrderEvent> transitions) throws Exception { transitions //当执行 【开始审核】操作时,将订单状态由待审核 -> 审核中 .withExternal().source(OrderStatusEnum.APPROVE_PENDING).target(OrderStatusEnum.APPROVE_ING).event(OrderEvent.APPROVE_START) .and() //当执行 【审核失败】操作时,将订单状态由审核中 -> 审核失败 .withExternal().source(OrderStatusEnum.APPROVE_ING).target(OrderStatusEnum.APPROVE_FAILED).event(OrderEvent.APPROVE_FAILED) .and() //当执行 【审核成功】操作时,将订单状态由审核中 -> 审核成功 .withExternal().source(OrderStatusEnum.APPROVE_ING).target(OrderStatusEnum.APPROVE_SUCCESS).event(OrderEvent.APPROVE_SUCCESS); } /** * 持久化配置 * * @return */ @Bean public DefaultStateMachinePersister persister() { returnnew DefaultStateMachinePersister<>(new StateMachinePersist<OrderStatusEnum, OrderEvent, BizOrder>() { @Override public void write(StateMachineContext<OrderStatusEnum, OrderEvent> context, BizOrder order) throws Exception { OrderStatusEnum orderStatus = context.getState(); log.info("订单状态持久化,订单ID:{},目标状态:{}", order.getId(), orderStatus); orderMapper.updateOrderStatus(order.getId(), orderStatus); } @Override public StateMachineContext<OrderStatusEnum, OrderEvent> read(BizOrder order) throws Exception { log.info("恢复订单状态机状态"); returnnew DefaultStateMachineContext<>(order.getStatus(), null, null, null); } }); } }

5、新建一个变更订单状态的服务

public interface BizOrderStatusService { /** * * 通用状态变更处理器 * @param incomingId * @param event */ void eventHandler(Long orderId, OrderEvent event); }
@Service publicclass BizOrderStatusServiceImpl implements BizOrderStatusService { @Resource private OrderMapper orderMapper; @Resource private StateMachine<OrderStatusEnum, OrderEvent> orderStateMachine; @Resource private StateMachinePersister<OrderStatusEnum, OrderEvent, BizOrder> persister; /** * * * @param orderId 订单id * @param event 事件类型 */ @Override public void eventHandler(Long orderId, OrderEvent event) { BizOrder order = orderMapper.getOrderById(orderId); Assert.notNull(order, "订单不存在"); // 自定义状态机参数对象(可以在此对象中定义后续需要用到的字段参数,状态配置那里如果需要做业务逻辑判断) StateMachineParam param = new StateMachineParam(); param.setBizOrder(order); Message message = MessageBuilder.withPayload(event).build(); if (!sendEvent(message, param)) { thrownew ApplicationBizException("订单状态流转异常"); } } /** * 发送订单状态转换事件 这里不要使用synchronized锁方法,效率比较低, * 分布式系统优先采用分布式锁,下单锁userId,订单状态流转锁orderId根据业务考虑使用什么。 * * @param message * @param param * @return */ private synchronized boolean sendEvent(Message<OrderEvent> message, StateMachineParam param) { boolean result = false; try { orderStateMachine.start(); //尝试恢复状态机状态 persister.restore(orderStateMachine, param.getBizOrder()); orderStateMachine.getExtendedState().getVariables().put("param", param); result = orderStateMachine.sendEvent(message); //持久化状态机状态 persister.persist(orderStateMachine, param.getBizOrder()); } catch (Exception e) { e.printStackTrace(); } finally { orderStateMachine.stop(); } return result; } }

6、调用方法执行订单状态变更,并持久化到数据库

@RestController @RequiredArgsConstructor publicclass ApproveController { privatefinal OrderStatusService orderStatusService; /** * 前端调用start方法将订单状态改为审核中,并自动持久化到数据库 * @param orderId 订单id * @return */ @PostMapping("/start") public void start(Long orderId) { orderStatusService.eventHandler(orderId,OrderEvent.APPROVE_START); } /** * 前端调用start方法将订单状态改为审核成功,并自动持久化到数据库 * @param orderId 订单id * @return */ @PostMapping("/approveSuccess") public void approveSuccess(Long orderId) { orderStatusService.eventHandler(orderId,OrderEvent.APPROVE_SUCCESS); } /** * 前端调用start方法将订单状态改为审核失败,并自动持久化到数据库 * @param orderId 订单id * @return */ @PostMapping("/approveFailed") public void approveFailed(Long orderId) { orderStatusService.eventHandler(orderId,OrderEvent.APPROVE_FAILED); } }

现在,我们已经配置了状态机并创建了服务来操作它。接下来,你可以在应用的任何部分注入OrderStatusService,并传入相应的事件来改变订单的状态。

7、总结:以上就是状态机的基础用法,一个事件对应一种来源状态(sourceStatus)和目标状态(targetStatus)。在我自己使用到的场景中还包含一个事件需要根据不同的条件将同一来源状态流转到不同的目标状态。这时我们就需要在状态映射配置中增加业务逻辑判断。

8、扩展(新增一个放款事件,该事件会将订单状态由【审核成功】流转到【放款成功】或者【部分放款成功】,具体流流转哪一个状态是由订单的放款金额决定的,如果申请金额和放款金额一致就是【放款成功】,放款金额小于申请金额就是【部分放款成功】)

8.1 我们在订单状态枚举中新增(LOAN_SUCCESS,PARTIALLY_LOAN_SUCCESS)

// 待审核 APPROVE_PENDING, // 审核中 APPROVE_ING, // 审核失败 APPROVE_FAILED, // 审核成功 APPROVE_SUCCESS, // 放款成功 LOAN_SUCCESS, // 部分放款成功 PARTIALLY_LOAN_SUCCESS;

8.2 我们在事件枚举中新增(LOAN)

// 开始审核 APPROVE_START, // 审核通过 APPROVE_SUCCESS, // 审核失败 APPROVE_FAILED, // 操作放款 LOAN;

8.3 优化一下上面的【配置状态转换事件关系】,需要在事件后面增加条件判断(通过guard()实现)

/** * 配置状态转换事件关系 * * @param transitions * @throws Exception */ @Override public void configure(StateMachineTransitionConfigurer<OrderStatusEnum, OrderEvent> transitions) throws Exception { transitions //当执行 【开始审核】操作时,将订单状态由待审核 -> 审核中 .withExternal().source(OrderStatusEnum.APPROVE_PENDING).target(OrderStatusEnum.APPROVE_ING).event(OrderEvent.APPROVE_START) .and() //当执行 【审核失败】操作时,将订单状态由审核中 -> 审核失败 .withExternal().source(OrderStatusEnum.APPROVE_ING).target(OrderStatusEnum.APPROVE_FAILED).event(OrderEvent.APPROVE_FAILED) .and() //当执行 【审核成功】操作时,将订单状态由审核中 -> 审核成功 .withExternal().source(OrderStatusEnum.APPROVE_ING).target(OrderStatusEnum.APPROVE_SUCCESS).event(OrderEvent.APPROVE_SUCCESS) .and() //当执行 【放款】操作时,将订单状态由审核成功 -> 放款成功 .withExternal().source(OrderStatusEnum.APPROVE_SUCCESS).target(OrderStatusEnum.LOAN_SUCCESS).event(OrderEvent.LOAN).guard(guardForLoanSuccessByLoan()) .and() //当执行 【放款】操作时,将订单状态由审核成功 -> 部分放款成功 .withExternal().source(OrderStatusEnum.APPROVE_SUCCESS).target(OrderStatusEnum.PARTIALLY_LOAN_SUCCESS).event(OrderEvent.LOAN).guard(guardForPartiallyLoanSuccessByLoan()); } /** * 订单状态由审核通过 -> 放款成功 * 触发条件:订单申请金额=放款金额 * * @return */ @Bean public Guard<OrderStatusEnum, OrderEvent> guardForLoanSuccessByLoan() { return context -> { // 从扩展信息中获取参数 StateMachineParam param = (StateMachineParam) context.getExtendedState().getVariables().get("param"); BizOrder order = param.getBizOrder(); // 如果申请金额=放款金额 ,返回true,状态机就会流转到调用此方法的目标状态 if (order.getApplyAmount().compareTo(order.getLoanAmlunt) == 0) { returntrue; } returnfalse; }; } /** * 订单状态由审核通过 -> 部分放款成功 * 触发条件:订单申请金额<放款金额 * * @return */ @Bean public Guard<OrderStatusEnum, OrderEvent> guardForPartiallyLoanSuccessByLoan() { return context -> { // 从扩展信息中获取参数 StateMachineParam param = (StateMachineParam) context.getExtendedState().getVariables().get("param"); BizOrder order = param.getBizOrder(); // 如果申请金额<放款金额 ,返回true,状态机就会流转到调用此方法的目标状态 if (order.getApplyAmount().compareTo(order.getLoanAmlunt) < 0) { returntrue; } returnfalse; }; }

通过以上操作我们就可以实现业务中某些需要根据不同条件流转到不同状态的场景。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/25 22:30:28

AI智能体:完整课程(初级)

如果你在2025年关注过AI&#xff0c;你可能已经注意到&#xff0c;每个人都在谈论智能体。这是有充分理由的。AI智能体可以处理从简单的日常任务到企业级复杂的多智能体工作流程的所有事务。而这仅仅是个开始。我们即将见证这个领域涌现更多创新。如果您是初次来到这里&#xf…

作者头像 李华
网站建设 2026/9/25 22:30:45

GSE宏编译器:魔兽世界玩家的技能循环自动化神器

GSE宏编译器&#xff1a;魔兽世界玩家的技能循环自动化神器 【免费下载链接】GSE-Advanced-Macro-Compiler GSE is an alternative advanced macro editor and engine for World of Warcraft. It uses Travis for UnitTests, Coveralls to report on test coverage and the Cur…

作者头像 李华
网站建设 2026/9/25 16:28:59

DeeplxFile:突破文件翻译限制的终极解决方案

还在为文档翻译发愁吗&#xff1f;&#x1f629; 当其他翻译工具告诉你"文件太大"或"不支持Excel"时&#xff0c;DeeplxFile正在默默打破这些限制&#xff01;这款基于Deeplx和Playwright的开源工具&#xff0c;让免费、无限制的文件翻译成为现实。 【免费…

作者头像 李华
网站建设 2026/9/25 21:54:57

针对机械设备行业一体化项目制管理解决方案

对于机械设备工厂&#xff0c;管理中的挑战主要体现在内部流程的协调与效率、成本的控制&#xff0c;以及对定制化生产的管理上。机械设备行业专业的管理软件&#xff0c;正是为了针对性解决这些问题而设计的。机械设备工厂的管理痛点机械设备工厂的管理挑战复杂且具体&#xf…

作者头像 李华
网站建设 2026/9/25 21:47:09

别再问资质认证怎么查了!看这家公司如何用“大模型搜索”帮客户7天拿下高新认证

在数字化转型浪潮与政策红利双重叠加的今天&#xff0c;资质认证已成为企业提升核心竞争力、获取税收优惠、赢得市场先机的“硬通货”。无论是高新技术企业认定、专精特新申报&#xff0c;还是各类行业许可&#xff0c;其办理过程却常让企业主们头疼不已&#xff1a;办理流程不…

作者头像 李华