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

Spring Boot 3.0.x迁移至3.1.x后Autowired注入失效求助

Spring Boot 3.1.4迁移后Servlet中@Autowired注入失效问题

我正在将Spring Boot从3.0.11版本迁移至3.1.4版本。此前Servlet注册仅触发警告,但3.1版本后变为错误,因此我修改代码以维持原有功能,却出现Autowired注入失效问题。

原有Spring Boot 3.0.11代码

@EnableConfigurationProperties({ DataSourceMonitoringProperties.class})
public class DataSourceServletConfiguration {
    
    public static final String DATASOURCE_MONITORING_PATH = "/datasource";

    @Bean(name = Const.DATASOURCE_SERVLET_BEAN_NAME)
    @ConditionalOnMissingBean(name = Const.DATASOURCE_SERVLET_BEAN_NAME)
    public Servlet monitoringDatasourceServlet(ServletContext servletContext, DataSourceMonitoringProperties dataSourceMonitoringProperties) {
        RequestDispatcher namedDispatcher = servletContext.getNamedDispatcher(Const.DATASOURCE_SERVLET_BEAN_NAME);
        if (namedDispatcher != null) {
            return new DataSourceMonitoringServlet();
        }

        DataSourceMonitoringServlet monitoringDatasourceServlet = new DataSourceMonitoringServlet();
        monitoringDatasourceServlet.setJavamelody(null != servletContext.getFilterRegistration("javamelody"));
        Dynamic monitoringDataSourceServletDynamic = servletContext.addServlet(Const.DATASOURCE_SERVLET_BEAN_NAME, monitoringDatasourceServlet);
        monitoringDataSourceServletDynamic.setLoadOnStartup(1);
        monitoringDataSourceServletDynamic.setAsyncSupported(true);
        monitoringDataSourceServletDynamic.addMapping(DATASOURCE_MONITORING_PATH);


        return monitoringDatasourceServlet;
    }
}

修改后的Spring Boot 3.1.4代码

@EnableConfigurationProperties({ DataSourceMonitoringProperties.class})
public class DataSourceServletConfiguration {
    
    public static final String DATASOURCE_MONITORING_PATH = "/datasource";

    @Bean(name = Const.DATASOURCE_SERVLET_BEAN_NAME)
    @ConditionalOnMissingBean(name = Const.DATASOURCE_SERVLET_BEAN_NAME)
    public ServletRegistrationBean<DataSourceMonitoringServlet> monitoringDatasourceServlet(ServletContext servletContext, DataSourceMonitoringProperties dataSourceMonitoringProperties) throws ServletException {
        RequestDispatcher namedDispatcher = servletContext.getNamedDispatcher(Const.DATASOURCE_SERVLET_BEAN_NAME);
        if (namedDispatcher != null) {
            ServletRegistrationBean<DataSourceMonitoringServlet> servletRegistrationBean = new ServletRegistrationBean<>(new DataSourceMonitoringServlet());
            servletRegistrationBean.onStartup(servletContext);
            return servletRegistrationBean;
        }

        DataSourceMonitoringServlet monitoringDatasourceServlet = new DataSourceMonitoringServlet();
        monitoringDatasourceServlet.setJavamelody(null != servletContext.getFilterRegistration("javamelody"));
        Dynamic monitoringDataSourceServletDynamic = servletContext.addServlet(Const.DATASOURCE_SERVLET_BEAN_NAME, monitoringDatasourceServlet);
        monitoringDataSourceServletDynamic.setLoadOnStartup(1);
        monitoringDataSourceServletDynamic.setAsyncSupported(true);
        monitoringDataSourceServletDynamic.addMapping(DATASOURCE_MONITORING_PATH);
        
        ServletRegistrationBean<DataSourceMonitoringServlet> monitoringDatasourceServletRegistration = new ServletRegistrationBean<>(monitoringDatasourceServlet, DATASOURCE_MONITORING_PATH);
        monitoringDatasourceServletRegistration.onStartup(servletContext);
        monitoringDatasourceServletRegistration.setIgnoreRegistrationFailure(true);
        monitoringDatasourceServletRegistration.setLoadOnStartup(1);
        monitoringDatasourceServletRegistration.setAsyncSupported(true);


        return monitoringDatasourceServletRegistration;
    }
}

对应的Servlet代码

public class DataSourceMonitoringServlet extends GenericServlet {

    @Autowired
    private DatasourceMetricsCollector datasourceMetricsCollector;
    private boolean javamelody;

    public HtmlReportData datasourceReport() {
        // curl -i -H "Accept: application/json" http://localhost:8080
        HtmlReportData data = new HtmlReportData();
        data.setCollectedDatasourceTraces(datasourceMetricsCollector.collectDatasourceMetrics());
        data.setFindLockRows(datasourceMetricsCollector.collectRecordLockInfo());
        return data;
    }
}

堆栈跟踪信息

java.lang.NullPointerException: Cannot invoke "de.mydomain.framework.monitoring.datasource.collector.DatasourceMetricsCollector.collectDatasourceMetrics()" because "this.datasourceMetricsCollector" is null
    at de.mydomian.framework.monitoring.datasource.html.report.DataSourceMonitoringServlet.datasourceReport(DataSourceMonitoringServlet.java:40) ~[classes/:na]
    at de.mydomain.framework.monitoring.datasource.html.report.DataSourceMonitoringServlet.service(DataSourceMonitoringServlet.java:48) ~[classes/:na]
    at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:764) ~[jetty-servlet-11.0.16.jar:11.0.16]
    ...
    at java.base/java.lang.Thread.run(Thread.java:833) ~[na:na]

问题根源与解决方案

核心问题

  1. Servlet实例未被Spring容器管理:你通过new DataSourceMonitoringServlet()手动创建实例,该实例不在Spring的Bean生命周期中,Spring无法完成@Autowired注入。
  2. 重复注册Servlet:同时调用servletContext.addServlet()和ServletRegistrationBean.onStartup(),导致注册逻辑冲突,进一步破坏了Spring的Bean管理机制。

修正后的配置类代码

@EnableConfigurationProperties({ DataSourceMonitoringProperties.class})
public class DataSourceServletConfiguration {
    
    public static final String DATASOURCE_MONITORING_PATH = "/datasource";

    // 定义Servlet实例,交由Spring管理
    @Bean(name = Const.DATASOURCE_SERVLET_BEAN_NAME)
    @ConditionalOnMissingBean(name = Const.DATASOURCE_SERVLET_BEAN_NAME)
    public DataSourceMonitoringServlet monitoringDatasourceServlet(ServletContext servletContext) {
        DataSourceMonitoringServlet servlet = new DataSourceMonitoringServlet();
        servlet.setJavamelody(null != servletContext.getFilterRegistration("javamelody"));
        return servlet;
    }

    // 单独定义Servlet注册Bean,由Spring自动处理注册流程
    @Bean
    @ConditionalOnMissingBean
    public ServletRegistrationBean<DataSourceMonitoringServlet> datasourceServletRegistration(DataSourceMonitoringServlet monitoringDatasourceServlet) {
        ServletRegistrationBean<DataSourceMonitoringServlet> registrationBean = new ServletRegistrationBean<>(monitoringDatasourceServlet, DATASOURCE_MONITORING_PATH);
        registrationBean.setName(Const.DATASOURCE_SERVLET_BEAN_NAME);
        registrationBean.setLoadOnStartup(1);
        registrationBean.setAsyncSupported(true);
        registrationBean.setIgnoreRegistrationFailure(true); // 解决3.1版本后的注册错误问题
        return registrationBean;
    }
}

关键修改说明

  • 拆分Bean定义:将Servlet实例和注册逻辑分开,确保DataSourceMonitoringServlet成为Spring管理的Bean,@Autowired注入才能生效。
  • 移除手动注册逻辑:不再直接操作servletContext或调用onStartup(),完全交给Spring处理Servlet的注册流程。
  • 指定注册Bean名称:通过registrationBean.setName()统一Servlet名称,避免冲突。
  • 保留ignoreRegistrationFailure:维持该配置以兼容Spring Boot 3.1版本的注册规则。

另外,需确保DatasourceMetricsCollector是Spring管理的Bean(如添加@Component/@Service注解),否则无法被注入到Servlet中。

内容的提问来源于stack exchange,提问作者Andrej Flieger

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 10:19:49