Spring Boot偶发用默认构造器实例化AutoConfiguration启动报错
问题特征
- 故障概率:仅约1%的启动场景触发,剩余99%场景应用可正常启动运行
- 故障表现:Spring容器刷新阶段抛出异常,直接取消上下文初始化,典型异常日志如下:
WARN o.s.b.w.s.c.AnnotationConfigServletWebServerApplicationContext - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'errorPageFilterRegistration' defined in org.springframework.boot.web.servlet.support.ErrorPageFilterConfiguration: Unsatisfied dependency expressed through method 'errorPageFilterRegistration' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'errorPageFilter' defined in org.springframework.boot.web.servlet.support.ErrorPageFilterConfiguration: Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration': Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration]: No default constructor found; nested exception is java.lang.NoSuchMethodException: org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration.<init>() []
- 异常共性:
- Spring尝试调用无参默认构造器实例化自动配置类失败,但目标自动配置类本身仅提供了用于依赖注入的带参构造器,不存在无参构造器
- 异常涉及的自动配置类不固定,偶发场景下可能抛出
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration等其他内置自动配置类的同类实例化错误 - 异常栈无业务自定义代码栈帧,问题无法稳定复现,排查难度高
- 业务配置背景:应用规模较大,主要通过
@Configuration类完成Bean注册,错误页相关配置仅通过如下代码实现:
@Bean public WebServerFactoryCustomizer<TomcatServletWebServerFactory> webServerFactoryCustomizer() { return webServerFactory -> { ErrorPage errorPage = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error"); webServerFactory.addErrorPages(errorPage); }; }
- 参考的Spring官方源码
ErrorPageFilterConfiguration实现如下:
@Configuration(proxyBeanMethods = false) class ErrorPageFilterConfiguration { @Bean ErrorPageFilter errorPageFilter() { return new ErrorPageFilter(); } @Bean FilterRegistrationBean<ErrorPageFilter> errorPageFilterRegistration(ErrorPageFilter filter) { FilterRegistrationBean<ErrorPageFilter> registration = new FilterRegistrationBean<>(filter); registration.setOrder(filter.getOrder()); registration.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC); return registration; } }
问题解答
1. Spring为什么会尝试使用默认构造器初始化AutoConfiguration Bean?
根因是ClassPath下Spring核心包版本冲突,偶发的本质是类加载时Jar包遍历顺序存在随机性:
Spring实例化Bean的标准逻辑是:先扫描类的构造器,如果类仅存在一个带参构造器(Spring 4.3+版本无需显式添加@Autowired注解),就直接走构造器参数依赖注入流程;如果没有识别到可用的注入构造器,才会回退尝试调用无参默认构造器。
当ClassPath里混入了不同版本的Spring核心Jar(比如第三方依赖间接引入了和当前Spring Boot版本不匹配的低版本spring-beans、高版本spring-context),负责解析构造器注入注解的后置处理器和实例化Bean的工厂类版本不兼容,就会偶发出现“识别不到带参构造器的注入标记”的问题,直接走到无参构造器调用逻辑,自然抛出找不到无参构造的异常。
你贴的官方ErrorPageFilterConfiguration源码本身没有任何问题,它出现在异常栈里只是因为异常触发时刚好走到Web服务相关Bean的创建链路,不是这个配置类本身的逻辑问题。
2. 创建errorPageFilter时为什么会把ErrorMvcAutoConfiguration的初始化作为前置依赖?
这个依赖链路的错乱也是版本冲突导致的连锁反应。
正常情况下errorPageFilter是ErrorPageFilterConfiguration定义的独立Bean,和ErrorMvcAutoConfiguration没有初始化依赖关系。但当Spring的后置处理器解析逻辑因为版本不兼容出现错乱时,会在Bean初始化的回调阶段错误地将同Web模块下的自动配置类标记为当前Bean的依赖,触发了无关类的提前实例化。
你业务侧写的WebServerFactoryCustomizer错误页配置完全符合官方规范,不会触发这个依赖绑定问题,和故障没有关系。
修复方案
- 先拉取全量依赖树排查:Maven环境执行
mvn dependency:tree,Gradle环境执行./gradlew dependencies,过滤所有spring-*开头的依赖,确认所有Spring核心包、Spring Boot自动配置包的版本完全对齐,和当前使用的Spring Boot版本官方绑定的依赖版本一致,直接排除所有冲突的间接依赖 - 检查是否存在自定义类加载器、fat jar打包配置错误,避免同一Spring类被不同类加载器重复加载
- 如果使用Spring Boot官方打包插件,确认provided级别的Servlet API、Spring依赖没有被重复打进最终可执行Jar
内容的提问来源于stack exchange,提问作者Kristof Depypere

