SpringBoot源码解析:SpringApplication启动流程
概述
SpringBoot的核心优势之一便是“约定优于配置”的自动化启动机制,而这一切的入口都源于 SpringApplication 类。无论是通过 SpringApplication.run() 静态方法启动,还是手动实例化 SpringApplication 后调用 run() 方法,其底层都遵循一套固定的启动流程。
SpringApplication的启动流程本质上是“初始化准备-环境构建-上下文创建-刷新启动-回调通知”的闭环过程,核心目标是完成Spring容器的初始化、Bean的扫描与实例化、自动化配置的激活,最终启动应用并对外提供服务。整个流程可划分为三大核心阶段:
- 初始化阶段 :实例化SpringApplication对象,加载核心组件(初始化器、监听器),判断应用类型(Servlet/Reactive/NONE)。
- 运行阶段 :优化环境准备流程、精简上下文创建链路,强化对原生Java特性的兼容,核心Spring容器启动逻辑保持稳定但性能有提升。
- 异常处理与结束阶段 :处理启动过程中的异常,触发启动完成/失败回调,释放资源。
本文将基于SpringBoot4.0.x版本源码角度出发,详细拆解 SpringApplication 的完整启动链路。
初始化阶段:SpringApplication对象的创建
初始化阶段的核心是构建SpringApplication实例,完成基础组件的加载与初始化。启动入口通常有两种写法,本质逻辑一致:
// 方式1:静态方法直接启动 public static void main(String[] args) { SpringApplication.run(Application.class, args); } // 方式2:手动实例化后启动(支持自定义配置) public static void main(String[] args) { SpringApplication app = new SpringApplication(Application.class); app.setBannerMode(Banner.Mode.CONSOLE); app.setLogStartupInfo(false); app.run(args); }
静态run()方法的逻辑
静态 run() 方法,用于实例化SpringApplication并调用其成员方法 run() ,源码如下:
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) { return run(new Class<?>[]{primarySource}, args); } public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { // 实例化SpringApplication并调用run方法 return new SpringApplication(primarySources).run(args); }
SpringApplication构造函数的核心逻辑
调用 new SpringApplication(primarySources) 时,会完成初始化工作,源码位于 SpringApplication 类的构造方法:
public SpringApplication(Class<?>... primarySources) { this(null, primarySources); } @SuppressWarnings({ "unchecked", "rawtypes" }) public SpringApplication(@Nullable ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "'primarySources' must not be null"); // 保存主配置类(如Application.class) this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); // 1. 推断应用类型 this.properties.setWebApplicationType(WebApplicationType.deduce()); // 2. 加载初始化器(ApplicationContextInitializer) this.bootstrapRegistryInitializers = new ArrayList<>(getSpringFactoriesInstances(BootstrapRegistryInitializer.class)); setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); // 3. 加载监听器(ApplicationListener) setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); // 4. 推断主启动类(通过堆栈信息找到main方法所在类) this.mainApplicationClass = deduceMainApplicationClass(); }
推断应用类型
WebApplicationType 用于标识应用类型,决定后续创建的上下文类型和是否启动 Web 服务器,主要分为三类:
SERVLET:传统Servlet应用。REACTIVE:响应式应用。NONE:非Web应用(既无Servlet也无Reactive相关类)。
SpringBoot 4.0.x 通过 WebApplicationType.deduce() 方法,遍历的 Deducer 接口的具体实现类,来推断应用类型。源码如下:
public static WebApplicationType deduce() { for (Deducer deducer : SpringFactoriesLoader.forDefaultResourceLocation().load(Deducer.class)) { WebApplicationType deduced = deducer.deduceWebApplicationType(); if (deduced != null) { return deduced; } } return isServletApplication() ? WebApplicationType.SERVLET : WebApplicationType.NONE; }
Deducer 是 SpringBoot 为扩展“应用类型推断逻辑”而设计的 SPI 接口,其设计目的是:将“Reactive 应用推断”和“Servlet 应用推断”解耦。 Deducer 接口的源码如下:
@FunctionalInterface public interface Deducer { /** * 推断应用类型,返回 null 表示当前 Deducer 无法推断,交给下一个 Deducer 或兜底逻辑 */ @Nullable WebApplicationType deduceWebApplicationType(); }
Deducer 接口默认的实现类如下:
WebFluxWebApplicationTypeDeducer:- 包名: spring-boot-webflux
- SPI配置文件:/META-INF/spring.factories
- 推断逻辑:存在
org.springframework.web.reactive.DispatcherHandler和reactor.core.publisher.Mono。
WebMvcWebApplicationTypeDeducer:- 包名:spring-boot-webmvc
- SPI配置文件::/META-INF/spring.factories
- 推断逻辑:存在
jakarta.servlet.Servlet、org.springframework.web.servlet.DispatcherServlet以及org.springframework.web.context.ConfigurableWebApplicationContext。
如果你需要自定义应用类型推断逻辑,可参考以上实现类来完成。
加载初始化器与监听器
通过 SpringFactoriesLoader 加载 META-INF/spring.factories 文件中配置的扩展类:
ApplicationContextInitializer:上下文初始化器,用于在上下文刷新前自定义上下文配置;ApplicationListener:应用监听器,用于监听启动过程中的各类事件(如启动开始、环境准备完成等)。
ApplicationContextInitializer 和 ApplicationListener 的加载流程相似,核心依赖 SpringBoot SPI 机制(SpringFactoriesLoader),且都经过「加载类名 → 反射实例化 → 排序」三个主要步骤。源码如下:
private <T> List<T> getSpringFactoriesInstances(Class<T> type) { return getSpringFactoriesInstances(type, null); } private <T> List<T> getSpringFactoriesInstances(Class<T> type, @Nullable ArgumentResolver argumentResolver) { return SpringFactoriesLoader.forDefaultResourceLocation(getClassLoader()).load(type, argumentResolver); }
具体的加载逻辑在 SpringFactoriesLoader 的 load 方法中:
public <T> List<T> load(Class<T> factoryType, @Nullable ArgumentResolver argumentResolver, @Nullable FailureHandler failureHandler) { Assert.notNull(factoryType, "'factoryType' must not be null"); // 1. 通过 SPI 加载所有配置的实现类全类名 List<String> implementationNames = loadFactoryNames(factoryType); logger.trace(LogMessage.format("Loaded [%s] names: %s", factoryType.getName(), implementationNames)); // 2. 反射实例化所有实现类 List<T> result = new ArrayList<>(implementationNames.size()); FailureHandler failureHandlerToUse = (failureHandler != null) ? failureHandler : THROWING_FAILURE_HANDLER; for (String implementationName : implementationNames) { T factory = instantiateFactory(implementationName, factoryType, argumentResolver, failureHandlerToUse); if (factory != null) { result.add(factory); } } // 3. 排序(按 @Order 注解或 Ordered 接口) AnnotationAwareOrderComparator.sort(result); return result; }
推断主类
通过遍历当前线程的调用栈,找到包含main方法的类,源码如下:
private @Nullable Class<?> deduceMainApplicationClass() { return StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE) .walk(this::findMainClass) .orElse(null); } private Optional<Class<?>> findMainClass(Stream<StackFrame> stack) { return stack.filter((frame) -> Objects.equals(frame.getMethodName(), "main")) .findFirst() .map(StackWalker.StackFrame::getDeclaringClass); }
运行阶段:核心启动流程
实例化SpringApplication后,调用 run() 方法进入核心运行阶段,该方法是启动流程的核心,源码如下:
public ConfigurableApplicationContext run(String... args) { // 1. 启动时间监控(用于打印启动耗时) Startup startup = Startup.create(); if (this.properties.isRegisterShutdownHook()) { SpringApplication.shutdownHook.enableShutdownHookAddition(); } DefaultBootstrapContext bootstrapContext = createBootstrapContext(); ConfigurableApplicationContext context = null; // 2. 配置headless模式(适配无图形界面的服务器环境) configureHeadlessProperty(); // 3. 加载SpringApplicationRunListener(运行监听器,监听启动全流程) SpringApplicationRunListeners listeners = getRunListeners(args); // 4. 发布“启动开始”事件 listeners.starting(bootstrapContext, this.mainApplicationClass); try { // 5. 封装命令行参数 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // 6. 初始化环境(加载配置、激活Profiles、绑定属性源) ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments); // 7. 打印启动Banner(SpringBoot Logo) Banner printedBanner = printBanner(environment); // 8. 创建应用上下文(根据WebApplicationType创建对应类型的上下文) context = createApplicationContext(); context.setApplicationStartup(this.applicationStartup); // 10. 上下文前置处理(绑定环境、执行初始化器、注册主类Bean等) prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); // 11. 刷新上下文(核心中的核心:Bean加载、Web服务器启动) refreshContext(context); // 12. 上下文后置处理(扩展点,默认空实现) afterRefresh(context, applicationArguments); // 13. 停止计时,打印启动耗时 Duration timeTakenToStarted = startup.started(); if (this.properties.isLogStartupInfo()) { new StartupInfoLogger(this.mainApplicationClass, environment).logStarted(getApplicationLog(), startup); } // 14. 发布“启动完成”事件 listeners.started(context, timeTakenToStarted); // 15. 执行ApplicationRunner/CommandLineRunner(启动后自定义逻辑) callRunners(context, applicationArguments); } catch (Throwable ex) { // 16. 启动失败处理(发布失败事件、生成异常报告) throw handleRunFailure(context, ex, listeners); } try { if (context.isRunning()) { // 17. 发布“应用就绪”事件 listeners.ready(context, startup.ready()); } } catch (Throwable ex) { throw handleRunFailure(context, ex, null); } // 18. 返回创建好的上下文 return context; }
下文拆解该方法中的关键步骤,重点分析环境准备、上下文创建与刷新、Runner调用等核心环节。
准备应用环境(prepareEnvironment)
prepareEnvironment 是 SpringBoot 启动流程中“环境准备阶段”的主要方法,此方法构建一个包含系统属性、环境变量、命令行参数、配置文件、Profile 配置等所有配置源的 ConfigurableEnvironment 对象,为后续上下文初始化和 Bean 加载提供统一的配置环境。源码如下:
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) { // 1. 创建/获取环境对象(根据WebApplicationType适配类型) ConfigurableEnvironment environment = getOrCreateEnvironment(); // 2. 配置环境:添加属性源、处理命令行参数、激活Profile configureEnvironment(environment, applicationArguments.getSourceArgs()); // 3. 绑定配置属性到Environment(处理占位符等) ConfigurationPropertySources.attach(environment); // 4. 触发环境准备事件(允许监听器扩展环境) listeners.environmentPrepared(bootstrapContext, environment); // 5. 将环境绑定到SpringApplication自身属性 ApplicationInfoPropertySource.moveToEnd(environment); DefaultPropertiesPropertySource.moveToEnd(environment); Assert.state(!environment.containsProperty("spring.main.environment-prefix"), "Environment prefix cannot be set via properties."); bindToSpringApplication(environment); // 6. 环境类型转换(适配WebApplicationType) if (!this.isCustomEnvironment) { EnvironmentConverter environmentConverter = new EnvironmentConverter(getClassLoader()); environment = environmentConverter.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass()); } ConfigurationPropertySources.attach(environment); return environment; }
创建环境 getOrCreateEnvironment
getOrCreateEnvironment 是 prepareEnvironment 流程的第一步,此步骤使用 ApplicationContextFactory 工厂模式解耦环境创建逻辑,核心目标是:
- 优先复用已自定义的 Environment(若有);
- 基于“应用类型(WebApplicationType)”+“ApplicationContextFactory 工厂”创建适配的
ConfigurableEnvironment; - 提供多层兜底逻辑,确保最终返回非空的环境对象。
源码如下:
private ConfigurableEnvironment getOrCreateEnvironment() { if (this.environment != null) { return this.environment; } WebApplicationType webApplicationType = this.properties.getWebApplicationType(); ConfigurableEnvironment environment = this.applicationContextFactory.createEnvironment(webApplicationType); if (environment == null && this.applicationContextFactory != ApplicationContextFactory.DEFAULT) { environment = ApplicationContextFactory.DEFAULT.createEnvironment(webApplicationType); } return (environment != null) ? environment : new ApplicationEnvironment(); }
配置环境 configureEnvironment
configureEnvironment 是 prepareEnvironment 流程的子步骤,调用时机在 getOrCreateEnvironment(创建环境对象)之后、ConfigurationPropertySources.attach(绑定配置属性源)之前。此步骤的主要作用为:
- 为环境添加统一的类型转换服务;
- 向环境中添加/排序各类属性源(PropertySource),明确配置优先级;
- 激活指定的 Profile(环境),处理 Profile 分组等特性;
- 最终让 ConfigurableEnvironment 具备“完整的配置加载能力”和“Profile 管理能力”。
源码如下:
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) { if (this.addConversionService) { environment.setConversionService(new ApplicationConversionService()); } configurePropertySources(environment, args); configureProfiles(environment, args); }
步骤 1:设置类型转换服务(ApplicationConversionService)
if (this.addConversionService) { environment.setConversionService(new ApplicationConversionService()); }
此步骤的核心逻辑为:
- this.addConversionService:SpringApplication 的成员变量,默认值为 true(开启类型转换),开发者可通过 SpringApplication.setAddConversionService(false) 关闭;
- ApplicationConversionService:SpringBoot 定制的类型转换服务,是 ConversionService 的实现类,核心作用是统一处理配置属性的类型转换(如将配置文件中的 String 类型值转换为 int/boolean/List/ 自定义类型)。
此步骤的主要作用为:
- 解决「配置值类型不匹配」问题:例如配置文件中 server.port=8080(String)自动转为 int,spring.main.banner-mode=off(String)自动转为 Banner.Mode 枚举;
- 内置丰富的转换器:支持时间(如 10s → Duration)、大小(如 10MB → DataSize)、集合(如 a,b,c → List<String>)等常用类型转换;
- 可扩展:开发者可自定义 Converter 注册到 ApplicationConversionService,例如:
// 自定义转换器:String → 自定义枚举 ApplicationConversionService.addConverter(String.class, MyEnum.class, MyEnum::valueOf);
步骤 2:配置属性源(configurePropertySources)
这是 configureEnvironment 的核心步骤,负责“添加属性源”、“调整属性源顺序”、“保证配置优先级”,是 SpringBoot 配置加载的核心逻辑。
源码如下:
protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) { MutablePropertySources sources = environment.getPropertySources(); if (!CollectionUtils.isEmpty(this.defaultProperties)) { DefaultPropertiesPropertySource.addOrMerge(this.defaultProperties, sources); } if (this.addCommandLineProperties && args.length > 0) { String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME; PropertySource<?> source = sources.get(name); if (source != null) { CompositePropertySource composite = new CompositePropertySource(name); composite .addPropertySource(new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args)); composite.addPropertySource(source); sources.replace(name, composite); } else { sources.addFirst(new SimpleCommandLinePropertySource(args)); } } environment.getPropertySources().addLast(new ApplicationInfoPropertySource(this.mainApplicationClass)); }
PropertySource 是 Spring 定义的抽象类,每个 PropertySource 对应一个配置来源(如命令行、配置文件、系统属性),MutablePropertySources 是其有序容器——排在前面的属性源优先级更高,会覆盖后面的同名配置。
PropertySource 默认的优先级,依次为:
- commandLineArgs:命令行参数(如 –server.port=8080),由 configurePropertySources 添加到首位
- systemProperties:JVM 系统属性(如 -Dspring.application.name=app),StandardEnvironment 内置,默认第二位
- systemEnvironment:操作系统环境变量(如 SPRINGAPPLICATIONNAME=app),StandardEnvironment 内置,默认第三位
- defaultProperties:SpringApplication 设置的默认属性,由 configurePropertySources 添加到末尾
- 配置文件(application.yml/properties):类路径/外部配置文件
开发者可重写 configurePropertySources 自定义属性源,例如:
@SpringBootApplication public class MyApp { public static void main(String[] args) { new SpringApplication(MyApp.class) { @Override protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) { super.configurePropertySources(environment, args); // 保留默认逻辑 // 添加自定义属性源,优先级高于系统属性 Map<String, Object> customProps = Map.of("custom.key", "custom.value"); environment.getPropertySources().addBefore("systemProperties", new MapPropertySource("customSource", customProps)); } }.run(args); } }
打印 Banner
Banner 是 SpringBoot 启动时打印的标识(默认是 SpringBoot logo),可通过spring.main.banner-mode配置关闭,或自定义 Banner(如在resources/banner.txt中配置)。
源码如下:
private @Nullable Banner printBanner(ConfigurableEnvironment environment) { // 步骤1:从环境中动态获取Banner模式,判断是否关闭Banner if (this.properties.getBannerMode(environment) == Banner.Mode.OFF) { return null; } // 步骤2:初始化资源加载器(用于加载类路径下的Banner文件) ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader : new DefaultResourceLoader(null); // 步骤3:创建Banner打印器(核心封装类) SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner); // 步骤4:判断Banner输出模式(LOG/默认CONSOLE) if (this.properties.getBannerMode(environment) == Mode.LOG) { return bannerPrinter.print(environment, this.mainApplicationClass, logger); } // 步骤5:默认模式(CONSOLE),输出到控制台 return bannerPrinter.print(environment, this.mainApplicationClass, System.out); }
步骤 1:动态判断是否关闭 Banner
if (this.properties.getBannerMode(environment) == Banner.Mode.OFF) { return null; }
步骤 2:初始化资源加载器
ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader : new DefaultResourceLoader(null);
- 复用自定义资源加载器 :若开发者通过 SpringApplication.setResourceLoader() 设置了自定义 ResourceLoader(如加载外部资源),则优先复用;
- 默认资源加载器 :创建 DefaultResourceLoader(null),null 表示使用「当前线程上下文类加载器」(Thread.currentThread().getContextClassLoader()),这是 Spring 加载类路径资源的默认方式;
- 核心作用 :用于加载类路径下的 banner.txt/banner.png 等文件(后续由 SpringApplicationBannerPrinter 调用)。
步骤 3:创建 SpringApplicationBannerPrinter
SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner);
- 入参说明:
resourceLoader:步骤 2 初始化的资源加载器,用于加载文件型 Banner;this.banner:开发者通过 SpringApplication.setBanner() 自定义的 Banner 实例(默认 null);- 核心作用:SpringApplicationBannerPrinter 是 Banner 加载 / 渲染 / 输出的唯一入口,封装了「自定义 Banner → 文件 Banner → 默认 Banner」的加载优先级,以及「控制台 / 日志」的输出逻辑。
步骤 4:以 LOG 模式输出Banner
if (this.properties.getBannerMode(environment) == Mode.LOG) { return bannerPrinter.print(environment, this.mainApplicationClass, logger); }
- Mode.LOG 输出逻辑:调用 bannerPrinter.print(…, logger) 重载方法,将 Banner 内容输出到日志框架(如 Logback/Log4j2),而非控制台;
- logger 说明:logger 是 SpringApplication 的内置日志器(类型为 org.slf4j.Logger),默认日志级别为 INFO,输出格式遵循应用的日志配置;
- print 方法(日志版)核心逻辑:
Banner print(Environment environment, @Nullable Class<?> sourceClass, Log logger) { Banner banner = getBanner(environment); try { logger.info(createStringFromBanner(banner, environment, sourceClass)); } catch (UnsupportedEncodingException ex) { logger.warn("Failed to create String for banner", ex); } return new PrintedBanner(banner, sourceClass); }
步骤5:默认模式(CONSOLE),输出到控制台
该步骤的触发需同时满足以下条件:
- getBannerMode(environment) 返回 Banner.Mode.CONSOLE(核心条件):
- 开发者未显式设置 bannerMode(代码 / 配置);
- 环境中未启用控制台结构化日志(spring.logging.console.structured.format 未配置);
- 排除 OFF(关闭)和 LOG(日志输出)模式 —— 这是 4.0.x 版本简化后的分支逻辑(仅保留 OFF/LOG/CONSOLE 三种模式)。
print方法的源码如下:
Banner print(Environment environment, @Nullable Class<?> sourceClass, PrintStream out) { Banner banner = getBanner(environment); banner.printBanner(environment, sourceClass, out); return new PrintedBanner(banner, sourceClass); }
创建应用上下文(createApplicationContext)
使用ApplicationContextFactory 工厂,根据 WebApplicationType 创建对应的上下文实例:
- NONE → AnnotationConfigApplicationContext;
- SERVLET → AnnotationConfigServletWebServerApplicationContext;
- REACTIVE → AnnotationConfigReactiveWebServerApplicationContext。
核心代码:
protected ConfigurableApplicationContext createApplicationContext() { ConfigurableApplicationContext context = this.applicationContextFactory .create(this.properties.getWebApplicationType()); Assert.state(context != null, "ApplicationContextFactory created null context"); return context; }
DefaultApplicationContextFactory 是默认的工厂实现类,默认工厂会根据应用类型创建适配的上下文:
| WebApplicationType | 创建的上下文实现类 |
|---|---|
| NONE(非 Web) | AnnotationConfigApplicationContext |
| SERVLET(Servlet Web | AnnotationConfigServletWebServerApplicationContext |
| REACTIVE(Reactive Web) | AnnotationConfigReactiveWebServerApplicationContext |
准备上下文(prepareContext)
这一阶段为上下文刷新做前置准备:
- 将Environment绑定到上下文;
- 执行ApplicationContextInitializer(初始化器);
- 发布ApplicationContextInitializedEvent事件;
- 注册单例 Bean(如命令行参数、主类);
- 加载主类的 Bean 定义(扫描@SpringBootApplication注解);
- 发布ApplicationPreparedEvent事件。
源码如下:
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, @Nullable Banner printedBanner) { // 步骤1:绑定上下文与环境(核心关联) context.setEnvironment(environment); // 步骤2:上下文后置处理(配置资源加载器、转换服务等) postProcessApplicationContext(context); // 步骤3:AOT适配——添加生成的初始化器 addAotGeneratedInitializerIfNecessary(this.initializers); // 步骤4:执行上下文初始化器(ApplicationContextInitializer) applyInitializers(context); // 步骤5:发布上下文准备完成事件(通知监听器) listeners.contextPrepared(context); // 步骤6:关闭引导上下文 bootstrapContext.close(context); // 步骤7:打印启动日志(绑定Properties配置,4.0.x调整) if (this.properties.isLogStartupInfo()) { logStartupInfo(context); logStartupProfileInfo(context); } // 步骤8:注册 boot 单例Bean(参数、Banner) ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner != null) { beanFactory.registerSingleton("springBootBanner", printedBanner); } // 步骤9:配置Bean工厂规则 if (beanFactory instanceof AbstractAutowireCapableBeanFactory autowireCapableBeanFactory) { autowireCapableBeanFactory.setAllowCircularReferences(this.properties.isAllowCircularReferences()); if (beanFactory instanceof DefaultListableBeanFactory listableBeanFactory) { listableBeanFactory.setAllowBeanDefinitionOverriding(this.properties.isAllowBeanDefinitionOverriding()); } } // 步骤10:懒加载配置 if (this.properties.isLazyInitialization()) { context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor()); } // 步骤11:保持应用存活配置 if (this.properties.isKeepAlive()) { context.addApplicationListener(new KeepAlive()); } // 步骤12:添加属性源排序后置处理器 context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context)); // 步骤13:条件化加载Bean定义(非AOT模式才执行,4.0.x新增) if (!AotDetector.useGeneratedArtifacts()) { // Load the sources Set<Object> sources = getAllSources(); Assert.state(!ObjectUtils.isEmpty(sources), "No sources defined"); load(context, sources.toArray(new Object[0])); } // 步骤14:发布上下文加载完成事件 listeners.contextLoaded(context); }
步骤 1:绑定上下文与环境
context.setEnvironment(environment);
将之前 prepareEnvironment 阶段准备好的 ConfigurableEnvironment 绑定到上下文。这是 Environment 与 ApplicationContext 的核心关联步骤,后续上下文内的所有 Bean 都能通过 @Value/@ConfigurationProperties 读取环境配置。
步骤 2:上下文后置处理
配置上下文的资源加载器、Bean 名称生成器、类型转换服务。
源码如下:
protected void postProcessApplicationContext(ConfigurableApplicationContext context) { // 配置Bean 名称生成器 if (this.beanNameGenerator != null) { context.getBeanFactory() .registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, this.beanNameGenerator); } // 配置上下文的资源加载器 if (this.resourceLoader != null) { if (context instanceof GenericApplicationContext genericApplicationContext) { genericApplicationContext.setResourceLoader(this.resourceLoader); } if (context instanceof DefaultResourceLoader defaultResourceLoader) { defaultResourceLoader.setClassLoader(this.resourceLoader.getClassLoader()); } } // 配置类型转换服务 if (this.addConversionService) { context.getBeanFactory().setConversionService(context.getEnvironment().getConversionService()); } }
步骤 3:AOT 适配 —— 添加生成的初始化器
addAotGeneratedInitializerIfNecessary(this.initializers);
- AOT(Ahead-of-Time):SpringBoot 4.0 强化的「提前编译」特性,可将运行时的 Bean 定义、初始化逻辑编译为字节码,提升启动速度;
- addAotGeneratedInitializerIfNecessary:若当前应用使用 AOT 编译产物(AotDetector.useGeneratedArtifacts() 为 true),则将 AOT 生成的 ApplicationContextInitializer 添加到初始化器列表;
- 设计意图:AOT 模式下复用提前编译的初始化逻辑,避免运行时重复解析注解,提升启动效率。
步骤 4:执行上下文初始化器
源码如下:
@SuppressWarnings({ "rawtypes", "unchecked" }) protected void applyInitializers(ConfigurableApplicationContext context) { // 遍历所有加载的 ApplicationContextInitializer(SPI/自定义) for (ApplicationContextInitializer initializer : getInitializers()) { // 适配上下文类型(泛型校验) Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(), ApplicationContextInitializer.class); Assert.state(requiredType != null, () -> "No generic type found for initializr of type " + initializer.getClass()); Assert.state(requiredType.isInstance(context), "Unable to call initializer"); // 执行初始化器的 initialize 方法 initializer.initialize(context); } }j
- 初始化器列表来自 SpringApplication 启动时通过 SPI 加载的 ApplicationContextInitializer(如 ContextIdApplicationContextInitializer);
- 作用:在上下文刷新前对其进行自定义初始化(如设置上下文 ID、添加属性源、注册 Bean 定义);
- 扩展场景:开发者可自定义初始化器,在该阶段修改上下文配置(如添加自定义 Bean 定义)。
步骤 5:发布上下文准备完成事件
listeners.contextPrepared(context);
- 触发 SpringApplicationRunListener 的 contextPrepared 方法,发布 ApplicationContextInitializedEvent;
- 作用:通知所有监听器「上下文已初始化但未加载 Bean 定义」,允许监听器在 Bean 加载前做最后调整;
- 典型场景:SpringBoot 内置监听器在此阶段做上下文基础配置(如设置日志上下文)。
步骤 6:关闭引导上下文
bootstrapContext.close(context);
DefaultBootstrapContext:SpringBoot 4.0 新增的「启动引导上下文」,用于管理启动阶段的临时资源(如 ApplicationContextFactory、EnvironmentPostProcessor 等)。
bootstrapContext.close(context) 方法的主要作用为:
- 将引导上下文中的临时资源「移交」给应用上下文(如绑定的配置、监听器);
- 关闭引导上下文,释放临时资源,避免内存泄漏;
- 触发引导上下文的关闭事件,通知临时资源完成清理;
步骤 7:打印启动日志(绑定 Properties 配置)
if (this.properties.isLogStartupInfo()) { logStartupInfo(context); logStartupProfileInfo(context); }
- logStartupInfo:打印应用启动基础信息(如“Starting MyApp on localhost with PID 1234”);
- logStartupProfileInfo:打印激活的 Profile 信息(如“The following 1 profile is active: 'dev'”);
可通过 spring.main.log-startup-info=false 关闭启动日志打印。
步骤 8:注册核心单例 Bean
注册命令行参数(springApplicationArguments)和 Banner(springBootBanner)为单例 Bean,源码如下:
beanFactory.registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner != null) { beanFactory.registerSingleton("springBootBanner", printedBanner); }
步骤 9:配置 Bean 工厂规则
if (beanFactory instanceof AbstractAutowireCapableBeanFactory autowireCapableBeanFactory) { autowireCapableBeanFactory.setAllowCircularReferences(this.properties.isAllowCircularReferences()); if (beanFactory instanceof DefaultListableBeanFactory listableBeanFactory) { listableBeanFactory.setAllowBeanDefinitionOverriding(this.properties.isAllowBeanDefinitionOverriding()); } }
核心逻辑:
setAllowCircularReferences:设置是否允许循环依赖,默认为true(允许循环依赖),可通过 spring.main.allow-circular-references=false 关闭;setAllowBeanDefinitionOverriding:设置是否允许Bean定义覆盖,默认false(禁止 Bean 定义覆盖),可通过 spring.main.allow-bean-definition-overriding=true 开启。
步骤 10:懒加载配置
if (this.properties.isLazyInitialization()) { context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor()); }
核心逻辑:
isLazyInitialization():从 SpringApplication.Properties 读取懒加载配置,默认 false。可通过 spring.main.lazy-initialization=true 开启全局懒加载;LazyInitializationBeanFactoryPostProcessor:为所有 Bean 设置默认懒加载模式(除非 Bean 显式标注 @Lazy(false));
步骤 11:保持应用存活配置
if (this.properties.isKeepAlive()) { context.addApplicationListener(new KeepAlive()); }
核心逻辑:
isKeepAlive():读取 spring.main.keep-alive 配置,默认 false;- KeepAlive 监听器:应用启动完成后阻止主线程退出,适用于「非 Web 应用但需要长期运行」的场景(如定时任务、消息消费者);
- 设计意图:替代传统的 Thread.sleep(Long.MAXVALUE),提供标准化的应用存活机制。
步骤 12:添加属性源排序后置处理器
context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));
核心逻辑:
PropertySourceOrderingBeanFactoryPostProcessor:保证上下文环境中的属性源按 SpringBoot 标准优先级排序(命令行 > 系统属性 > 配置文件 > 默认属性);- 设计意图:修复极端场景下属性源顺序混乱的问题,确保配置优先级规则生效。
步骤 13:条件化加载 Bean 定义
if (!AotDetector.useGeneratedArtifacts()) { Set<Object> sources = getAllSources(); Assert.state(!ObjectUtils.isEmpty(sources), "No sources defined"); load(context, sources.toArray(new Object[0])); }
核心逻辑:
- AotDetector.useGeneratedArtifacts():判断当前是否使用 AOT 编译产物(提前编译了 Bean 定义);
- 非 AOT 模式(默认):执行 load 方法,运行时解析启动类、扫描组件、加载自动配置类,生成 Bean 定义;
- AOT 模式:跳过 load 方法,直接使用提前编译好的 Bean 定义(存储在 AOT 产物中),提升启动速度;
- Assert.state:校验 sources 非空(默认是启动类),避免无 Bean 定义导致启动失败;
- 设计意图:适配 AOT 编译特性,区分「运行时解析」和「提前编译」两种场景。
步骤 14:发布上下文加载完成事件
listeners.contextLoaded(context);
触发 SpringApplicationRunListener 的 contextLoaded 方法,发布 ApplicationPreparedEvent;
刷新上下文(refreshContext)
private void refreshContext(ConfigurableApplicationContext context) { if (this.properties.isRegisterShutdownHook()) { shutdownHook.registerApplicationContext(context); } refresh(context); } protected void refresh(ConfigurableApplicationContext applicationContext) { applicationContext.refresh(); }
这是整个启动流程的核心中的核心,底层调用ApplicationContext的refresh()方法(继承自 Spring Framework 的AbstractApplicationContext),完成:
- BeanFactory 初始化;
- 加载所有 Bean 定义(扫描@Component/@Configuration等注解);
- 注册 BeanPostProcessor;
- 初始化 MessageSource(国际化);
- 初始化事件多播器;
- 创建 Web 服务器(ServletWebServerFactory 创建 Tomcat/Jetty/Undertow);
- 实例化所有非懒加载的单例 Bean;
- 发布ContextRefreshedEvent事件。
4.0.x 中,Web 服务器的创建是在onRefresh()方法中完成的(以 Servlet 应用为例):
@Override protected void onRefresh() { super.onRefresh(); try { createWebServer(); } catch (Throwable ex) { throw new ApplicationContextException("Unable to start web server", ex); } }
执行 ApplicationRunner 和 CommandLineRunner
这两个接口是 SpringBoot 提供的 “启动后执行逻辑” 的扩展点,区别在于:
- CommandLineRunner:接收原始命令行参数(String 数组);
- ApplicationRunner:接收封装后的ApplicationArguments(更友好的参数解析)。
核心执行逻辑:
private void callRunners(ApplicationContext context, ApplicationArguments args) { List<Object> runners = new ArrayList<>(); runners.addAll(context.getBeansOfType(ApplicationRunner.class).values()); runners.addAll(context.getBeansOfType(CommandLineRunner.class).values()); // 按@Order注解排序 AnnotationAwareOrderComparator.sort(runners); // 执行run方法 for (Object runner : new LinkedHashSet<>(runners)) { if (runner instanceof ApplicationRunner) { callRunner((ApplicationRunner) runner, args); } if (runner instanceof CommandLineRunner) { callRunner((CommandLineRunner) runner, args); } } }
异常处理
启动过程中若发生异常,会通过SpringBootExceptionReporter生成异常报告,并发布ApplicationFailedEvent事件,最终抛出异常终止启动。
总结
关键点回顾
- 核心入口:SpringApplication 的run()方法是启动流程的总入口,串联了环境准备、上下文创建、刷新等所有核心步骤。
- 核心扩展点:ApplicationContextInitializer(上下文初始化)、ApplicationListener(事件监听)、ApplicationRunner/CommandLineRunner(启动后执行逻辑)是扩展启动流程的关键。
- 核心特性:内嵌服务器的启动是在上下文刷新的onRefresh()阶段完成的,这是 SpringBoot 无需手动部署服务器的核心原因。
理解 SpringApplication 的启动流程,不仅能帮我们快速定位启动异常(如端口占用、配置解析失败),更能让我们基于扩展点定制启动逻辑(如自定义配置加载、启动前校验),真正掌握 SpringBoot 的底层设计思想。