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]
问题根源与解决方案
核心问题
- Servlet实例未被Spring容器管理:你通过
new DataSourceMonitoringServlet()手动创建实例,该实例不在Spring的Bean生命周期中,Spring无法完成@Autowired注入。 - 重复注册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
相关产品推荐
相关产品推荐

