Something

首页 / 文章 / RSS

SpringBoot源码解析:SpringApplication启动流程

概述

SpringBoot的核心优势之一便是“约定优于配置”的自动化启动机制,而这一切的入口都源于 SpringApplication 类。无论是通过 SpringApplication.run() 静态方法启动,还是手动实例化 SpringApplication 后调用 run() 方法,其底层都遵循一套固定的启动流程。

SpringApplication的启动流程本质上是“初始化准备-环境构建-上下文创建-刷新启动-回调通知”的闭环过程,核心目标是完成Spring容器的初始化、Bean的扫描与实例化、自动化配置的激活,最终启动应用并对外提供服务。整个流程可划分为三大核心阶段:

  1. 初始化阶段 :实例化SpringApplication对象,加载核心组件(初始化器、监听器),判断应用类型(Servlet/Reactive/NONE)。
  2. 运行阶段 :优化环境准备流程、精简上下文创建链路,强化对原生Java特性的兼容,核心Spring容器启动逻辑保持稳定但性能有提升。
  3. 异常处理与结束阶段 :处理启动过程中的异常,触发启动完成/失败回调,释放资源。

本文将基于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 接口默认的实现类如下:

  1. WebFluxWebApplicationTypeDeducer
    • 包名: spring-boot-webflux
    • SPI配置文件:/META-INF/spring.factories
    • 推断逻辑:存在 org.springframework.web.reactive.DispatcherHandlerreactor.core.publisher.Mono
  2. WebMvcWebApplicationTypeDeducer
    • 包名:spring-boot-webmvc
    • SPI配置文件::/META-INF/spring.factories
    • 推断逻辑:存在 jakarta.servlet.Servletorg.springframework.web.servlet.DispatcherServlet 以及 org.springframework.web.context.ConfigurableWebApplicationContext

如果你需要自定义应用类型推断逻辑,可参考以上实现类来完成。

加载初始化器与监听器

通过 SpringFactoriesLoader 加载 META-INF/spring.factories 文件中配置的扩展类:

  • ApplicationContextInitializer :上下文初始化器,用于在上下文刷新前自定义上下文配置;
  • ApplicationListener :应用监听器,用于监听启动过程中的各类事件(如启动开始、环境准备完成等)。

ApplicationContextInitializerApplicationListener 的加载流程相似,核心依赖 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);
}

具体的加载逻辑在 SpringFactoriesLoaderload 方法中:

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

getOrCreateEnvironmentprepareEnvironment 流程的第一步,此步骤使用 ApplicationContextFactory 工厂模式解耦环境创建逻辑,核心目标是:

  1. 优先复用已自定义的 Environment(若有);
  2. 基于“应用类型(WebApplicationType)”+“ApplicationContextFactory 工厂”创建适配的 ConfigurableEnvironment
  3. 提供多层兜底逻辑,确保最终返回非空的环境对象。

源码如下:

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

configureEnvironmentprepareEnvironment 流程的子步骤,调用时机在 getOrCreateEnvironment(创建环境对象)之后、ConfigurationPropertySources.attach(绑定配置属性源)之前。此步骤的主要作用为:

  1. 为环境添加统一的类型转换服务;
  2. 向环境中添加/排序各类属性源(PropertySource),明确配置优先级;
  3. 激活指定的 Profile(环境),处理 Profile 分组等特性;
  4. 最终让 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());
}

此步骤的核心逻辑为:

  1. this.addConversionService:SpringApplication 的成员变量,默认值为 true(开启类型转换),开发者可通过 SpringApplication.setAddConversionService(false) 关闭;
  2. ApplicationConversionService:SpringBoot 定制的类型转换服务,是 ConversionService 的实现类,核心作用是统一处理配置属性的类型转换(如将配置文件中的 String 类型值转换为 int/boolean/List/ 自定义类型)。

此步骤的主要作用为:

  1. 解决「配置值类型不匹配」问题:例如配置文件中 server.port=8080(String)自动转为 int,spring.main.banner-mode=off(String)自动转为 Banner.Mode 枚举;
  2. 内置丰富的转换器:支持时间(如 10s → Duration)、大小(如 10MB → DataSize)、集合(如 a,b,c → List<String>)等常用类型转换;
  3. 可扩展:开发者可自定义 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 默认的优先级,依次为:

  1. commandLineArgs:命令行参数(如 –server.port=8080),由 configurePropertySources 添加到首位
  2. systemProperties:JVM 系统属性(如 -Dspring.application.name=app),StandardEnvironment 内置,默认第二位
  3. systemEnvironment:操作系统环境变量(如 SPRINGAPPLICATIONNAME=app),StandardEnvironment 内置,默认第三位
  4. defaultProperties:SpringApplication 设置的默认属性,由 configurePropertySources 添加到末尾
  5. 配置文件(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),输出到控制台

该步骤的触发需同时满足以下条件:

  1. getBannerMode(environment) 返回 Banner.Mode.CONSOLE(核心条件):
    • 开发者未显式设置 bannerMode(代码 / 配置);
    • 环境中未启用控制台结构化日志(spring.logging.console.structured.format 未配置);
  2. 排除 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)

这一阶段为上下文刷新做前置准备:

  1. 将Environment绑定到上下文;
  2. 执行ApplicationContextInitializer(初始化器);
  3. 发布ApplicationContextInitializedEvent事件;
  4. 注册单例 Bean(如命令行参数、主类);
  5. 加载主类的 Bean 定义(扫描@SpringBootApplication注解);
  6. 发布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),完成:

  1. BeanFactory 初始化;
  2. 加载所有 Bean 定义(扫描@Component/@Configuration等注解);
  3. 注册 BeanPostProcessor;
  4. 初始化 MessageSource(国际化);
  5. 初始化事件多播器;
  6. 创建 Web 服务器(ServletWebServerFactory 创建 Tomcat/Jetty/Undertow);
  7. 实例化所有非懒加载的单例 Bean;
  8. 发布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 的底层设计思想。