Spring StateMachine配置及版本兼容问题排查求助
问题描述
使用Spring StateMachine实现业务状态持久化,编写了ClaimService代码后应用启动失败。核心错误为创建名为stateMachine的Bean失败,嵌套两个关键异常:
java.lang.IllegalStateException: This object has already been builtjava.lang.NullPointerException: 无法调用"org.springframework.statemachine.config.StateMachineConfig.getTransitions()",因为"stateMachineConfig"为null
业务代码(ClaimService)
@Service public class ClaimService { @Autowired ClaimRepository claimRepository; @Autowired StateMachineFactory factory; public boolean selectDate(ClaimRequest claimRequest) { StateMachine<ClaimStates, ClaimEvents> sm = build(claimRequest.getId()); Message<ClaimEvents> addDate = MessageBuilder.withPayload(ClaimEvents.SELECTDATE) .setHeader("claimId", claimRequest.getId()).build(); return sm.sendEvent(ClaimEvents.SELECTDATE); } private StateMachine<ClaimStates, ClaimEvents> build(Long claimId) { Claims claims = claimRepository.findById(claimId).get(); String claimIdKey = Long.toString(claims.getId()); StateMachine<ClaimStates, ClaimEvents> sm = factory.getStateMachine(claimIdKey); sm.stop(); sm.getStateMachineAccessor().doWithAllRegions(sma -> { sma.addStateMachineInterceptor(new StateMachineInterceptorAdapter<ClaimStates, ClaimEvents>() { // @Override public void preStateChange(State<ClaimStates, ClaimEvents> state, Message<ClaimEvents> message, Transition<ClaimStates, ClaimEvents> transition, StateMachine<ClaimStates, ClaimEvents> stateMachine) { Optional.ofNullable(message).ifPresent(msg -> { Long claimId = Long.class.cast(msg.getHeaders().getOrDefault("claimId", -1)); Claims claims = claimRepository.findById(claimId).get(); claims.setClaimState(state.getId().name()); claimRepository.save(claims); }); } }); sma.resetStateMachine(new DefaultStateMachineContext<ClaimStates, ClaimEvents>( ClaimStates.valueOf(claims.getClaimState()), null, null, null)); }); sm.start(); return sm; } }
完整报错日志
java.lang.IllegalStateException: This object has already been built at org.springframework.statemachine.config.common.annotation.AbstractAnnotationBuilder.build(AbstractAnnotationBuilder.java:43) ~[spring-statemachine-core-1.2.3.RELEASE.jar:1.2.3.RELEASE] at org.springframework.statemachine.config.builders.StateMachineConfigBuilder.performBuild(StateMachineConfigBuilder.java:46) ~[spring-statemachine-core-1.2.3.RELEASE.jar:1.2.3.RELEASE] at org.springframework.statemachine.config.builders.StateMachineConfigBuilder.performBuild(StateMachineConfigBuilder.java:34) ~[spring-statemachine-core-1.2.3.RELEASE.jar:1.2.3.RELEASE] at org.springframework.statemachine.config.common.annotation.AbstractConfiguredAnnotationBuilder.doBuild(AbstractConfiguredAnnotationBuilder.java:112) ~[spring-statemachine-core-1.2.3.RELEASE.jar:1.2.3.RELEASE] at org.springframework.statemachine.config.common.annotation.AbstractAnnotationBuilder.build(AbstractAnnotationBuilder.java:40) ~[spring-statemachine-core-1.2.3.RELEASE.jar:1.2.3.RELEASE] at org.springframework.statemachine.config.common.annotation.AbstractConfiguredAnnotationBuilder.getOrBuild(AbstractConfiguredAnnotationBuilder.java:129) ~[spring-statemachine-core-1.2.3.RELEASE.jar:1.2.3.RELEASE] at org.springframework.statemachine.config.configuration.StateMachineConfiguration$StateMachineDelegatingFactoryBean.afterPropertiesSet(StateMachineConfiguration.java:170) ~[spring-statemachine-core-1.2.3.RELEASE.jar:1.2.3.RELEASE] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1863) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1800) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:620) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:955) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:920) ~[spring-context-5.3.27.jar:5.3.27] at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583) ~[spring-context-5.3.27.jar:5.3.27] at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147) ~[spring-boot-2.7.12.jar:2.7.12] at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:731) ~[spring-boot-2.7.12.jar:2.7.12] at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408) ~[spring-boot-2.7.12.jar:2.7.12] at org.springframework.boot.SpringApplication.run(SpringApplication.java:307) ~[spring-boot-2.7.12.jar:2.7.12] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1303) ~[spring-boot-2.7.12.jar:2.7.12] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1292) ~[spring-boot-2.7.12.jar:2.7.12] at com.example.springstatemachinechatgpt.SpringstatemachinechatgptApplication.main(SpringstatemachinechatgptApplication.java:18) ~[classes/:na] 2023-06-26 10:45:48.225 WARN 24556 --- [ main] ConfigServletWebServerApplicationContext : 上下文初始化期间遇到异常 - 取消刷新尝试:org.springframework.beans.factory.BeanCreationException: 创建名为'stateMachine'的Bean失败;初始化方法调用失败;嵌套异常为java.lang.NullPointerException: 无法调用"org.springframework.statemachine.config.StateMachineConfig.getTransitions()",因为"stateMachineConfig"为null 2023-06-26 10:45:48.227 INFO 24556 --- [ main] j.LocalContainerEntityManagerFactoryBean : 关闭持久化单元'default'的JPA EntityManagerFactory 2023-06-26 10:45:48.238 INFO 24556 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - 开始关闭... 2023-06-26 10:45:48.275 INFO 24556 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - 关闭完成 2023-06-26 10:45:48.281 INFO 24556 --- [ main] o.apache.catalina.core.StandardService : 停止服务[Tomcat] 2023-06-26 10:45:48.320 INFO 24556 --- [ main] ConditionEvaluationReportLoggingListener : ApplicationContext启动失败。要显示条件报告,请启用'debug'模式重新运行应用。 2023-06-26 10:45:48.377 ERROR 24556 --- [ main] o.s.boot.SpringApplication : 应用运行失败 org.springframework.beans.factory.BeanCreationException: 创建名为'stateMachine'的Bean失败;初始化方法调用失败;嵌套异常为java.lang.NullPointerException: 无法调用"org.springframework.statemachine.config.StateMachineConfig.getTransitions()",因为"stateMachineConfig"为null at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1804) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:620) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:955) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:920) ~[spring-context-5.3.27.jar:5.3.27] at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583) ~[spring-context-5.3.27.jar:5.3.27] at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147) ~[spring-boot-2.7.12.jar:2.7.12] at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:731) ~[spring-boot-2.7.12.jar:2.7.12] at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408) ~[spring-boot-2.7.12.jar:2.7.12] at org.springframework.boot.SpringApplication.run(SpringApplication.java:307) ~[spring-boot-2.7.12.jar:2.7.12] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1303) ~[spring-boot-2.7.12.jar:2.7.12] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1292) ~[spring-boot-2.7.12.jar:2.7.12] at com.example.springstatemachinechatgpt.SpringstatemachinechatgptApplication.main(SpringstatemachinechatgptApplication.java:18) ~[classes/:na] Caused by: java.lang.NullPointerException: 无法调用"org.springframework.statemachine.config.StateMachineConfig.getTransitions()",因为"stateMachineConfig"为null at org.springframework.statemachine.config.configuration.StateMachineConfiguration$StateMachineDelegatingFactoryBean.afterPropertiesSet(StateMachineConfiguration.java:171) ~[spring-statemachine-core-1.2.3.RELEASE.jar:1.2.3.RELEASE] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1863) ~[spring-beans-5.3.27.jar:5.3.27] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1800) ~[spring-beans-5.3.27.jar:5.3.27] ... 16 common frames omitted
问题分析与解决方案
1. 核心问题:版本不兼容
当前使用的Spring Statemachine版本为1.2.3.RELEASE,该版本适配Spring 4.x;而项目使用的Spring Boot为2.7.12(对应Spring 5.3.27),版本完全不匹配,这是导致Bean创建失败、stateMachineConfig为空的根本原因。
版本适配规则
| Spring Boot版本 | Spring Statemachine版本 |
|---|---|
| 2.7.x | 3.2.x.RELEASE |
| 2.6.x | 3.1.x.RELEASE |
| 2.x早期版本 | 2.x.RELEASE |
| 1.x | 1.x.RELEASE |
版本调整
修改Maven/Gradle依赖,替换为兼容版本:
Maven
<dependency> <groupId>org.springframework.statemachine</groupId> <artifactId>spring-statemachine-core</artifactId> <version>3.2.0.RELEASE</version> </dependency>
Gradle
implementation 'org.springframework.statemachine:spring-statemachine-core:3.2.0.RELEASE'
2. 代码修正
2.1 规范依赖注入
使用构造函数注入替代@Autowired,并指定StateMachineFactory的泛型类型:
private final ClaimRepository claimRepository; private final StateMachineFactory<ClaimStates, ClaimEvents> factory; public ClaimService(ClaimRepository claimRepository, StateMachineFactory<ClaimStates, ClaimEvents> factory) { this.claimRepository = claimRepository; this.factory = factory; }
2.2 修复拦截器与状态重置逻辑
- 补全拦截器方法的
@Override注解 - 重置状态机前移除原有拦截器,避免重复添加
- 优化Optional处理,避免
get()方法抛出空指针:
private StateMachine<ClaimStates, ClaimEvents> build(Long claimId) { Claims claims = claimRepository.findById(claimId) .orElseThrow(() -> new IllegalArgumentException("Claim not found with id: " + claimId)); String claimIdKey = Long.toString(claims.getId()); StateMachine<ClaimStates, ClaimEvents> sm = factory.getStateMachine(claimIdKey); sm.stop(); sm.getStateMachineAccessor().doWithAllRegions(sma -> { sma.removeStateMachineInterceptorsIf(i -> i instanceof StatePersistenceInterceptor); sma.addStateMachineInterceptor(new StatePersistenceInterceptor()); sma.resetStateMachine(new DefaultStateMachineContext<>( ClaimStates.valueOf(claims.getClaimState()), null, null, null, null, null)); }); sm.start(); return sm; } private class StatePersistenceInterceptor extends StateMachineInterceptorAdapter<ClaimStates, ClaimEvents> { @Override public void preStateChange(State<ClaimStates, ClaimEvents> state, Message<ClaimEvents> message, Transition<ClaimStates, ClaimEvents> transition, StateMachine<ClaimStates, ClaimEvents> stateMachine) { Optional.ofNullable(message).ifPresent(msg -> { Long claimId = Long.class.cast(msg.getHeaders().getOrDefault("claimId", -1)); Claims claims = claimRepository.findById(claimId) .orElseThrow(() -> new IllegalArgumentException("Claim not found with id: " + claimId)); claims.setClaimState(state.getId().name()); claimRepository.save(claims); }); } }
2.3 确保状态机配置类正确
使用@EnableStateMachineFactory而非@EnableStateMachine,保证通过工厂获取多实例状态机:
@Configuration @EnableStateMachineFactory public class StateMachineConfig extends StateMachineConfigurerAdapter<Claim
相关产品推荐
相关产品推荐

