You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring StateMachine配置及版本兼容问题排查求助

问题描述

使用Spring StateMachine实现业务状态持久化,编写了ClaimService代码后应用启动失败。核心错误为创建名为stateMachine的Bean失败,嵌套两个关键异常:

  1. java.lang.IllegalStateException: This object has already been built
  2. java.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.x3.2.x.RELEASE
2.6.x3.1.x.RELEASE
2.x早期版本2.x.RELEASE
1.x1.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
相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 23:54:20