Spring Boot 源码研读之 SpringApplication 对象的创建

📅 2026/7/2 1:16:29
Spring Boot 源码研读之 SpringApplication 对象的创建
SpringApplication 对象创建一般 Spring Boot 项目都是通过在main函数中执行SpringApplication.run(XXXX.class,args);来启动的而run方法的内部执行首先会进行SpringApplication对象的创建让我们来看看SpringApplication对象创建做了哪些事。通过代码跟踪最终 SpringApplication 对象创建调用的是如下构造函数public SpringApplication(ResourceLoader resourceLoader, Class?... primarySources) { this.resourceLoader resourceLoader; Assert.notNull(primarySources, PrimarySources must not be null); this.primarySources new LinkedHashSet(Arrays.asList(primarySources)); this.webApplicationType WebApplicationType.deduceFromClasspath(); this.bootstrapRegistryInitializers new ArrayList( getSpringFactoriesInstances(BootstrapRegistryInitializer.class)); setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass deduceMainApplicationClass(); }通过代码分析我可以看到 SpringApplication 对象的创建主要有如下操作resourceLoader默认赋值nullprimarySourcesSetClass? 存放当前启动类 XXXX.class 集合注意启动类可能在其他位置webApplicationType通过判断相关类是否加载确定web服务类型是 REACTIVE还是 SERVLET。bootstrapRegistryInitializers通过加载并读取META-INF/spring.factories文件中的配置并结合反射的方式来实例化所有BootstrapRegistryInitializer接口的实现类。setInitializers通过加载并读取META-INF/spring.factories文件中的配置并结合反射的方式来实例化所有ApplicationContextInitializer接口的实现类。setListeners通过加载并读取META-INF/spring.factories文件中的配置并结合反射的方式来实例化所有ApplicationListener接口的实现类。mainApplicationClass通过当前线程的堆栈来找到main()函数所在的类即启动类getSpringFactoriesInstances(Clazz.class)实现getSpringFactoriesInstances方法的实现如下图所示核心逻辑就是读取META-INF/spring.factories配置文件并通过反射的方式创建指定接口类型的实现类实例。具体使用也可以参考另外一篇文章 《Spring 源码之 SpringFactoriesLoader 类简介-CSDN博客》通过这一步我们就获取到了所有BootstrapRegistryInitializer、ApplicationContextInitializer、ApplicationListener接口的实现类。补充spring.factories加载机制核心机制SpringFactoriesLoader.loadSpringFactories()走classpath*:/META-INF/spring.factories解析key全限定接口名value实现类全限定名用反射newInstance()批量实例化。那为什么SpringApplication 在容器还没创建时就能拿到这些扩展点因为它走的是classpath 静态扫描 反射不依赖 BeanFactory。这是 Spring Boot启动期扩展点机制和运行期的BeanPostProcessor是两套体系。演进史Spring Boot 2.7引入META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports一行一个自动配置类逐步替代 spring.factories 里的 auto-configuration 部分Spring Boot 3.0spring.factories 里的 auto-configuration 段正式废弃仅保留少量非 auto-configuration 段Spring Boot 3.2spring.factories 文件完全废弃实际并没有完全废弃原因spring.factories 是一个 key-value 巨型文件所有扩展点都堆在一起加载时全量解析改成*.imports后按需加载启动更快、更易维护总结一句话SpringApplication 构造函数用 classpath 扫描 反射加载扩展点绕开 Spring 容器让扩展点在容器起来之前就能用。2.7 之后 Spring 把这条路从 spring.factories 拆成 .imports3.2 完全废弃本质是从全量加载变按需加载。WebApplicationType补充三种类型REACTIVEWebFlux/SERVLETSpring MVC/NONE非 Web 应用判断逻辑deduceFromClasspath()用ClassUtils.isPresent依次探测存在DispatcherServlet→SERVLET但这个判断有问题见下存在WebFlux相关类且没有DispatcherServlet→REACTIVE都不存在 →NONE⚠️ 误判场景项目引了spring-web因为某个工具包传递依赖没真用 Spring MVC可能被误判为 SERVLET启动报找不到 DispatcherServlet或加载多余 MVC Bean解法手动setWebApplicationType(WebApplicationType.NONE)或在 SpringApplication 启动前排除多余依赖deduceMainApplicationClass 链式启动支持实现new RuntimeException().getStackTrace()拿堆栈遍历找到main方法所在类为什么用堆栈而不是参数传入为了支持SpringApplicationBuilder链式/嵌套启动——用户可能从SpringBootServletInitializer或别的启动类拉起主类在运行期才确定Trade-off实现简单不传参 vs 启动类可动态决定——选后者注spring boot 版本为3.2.3