SpringBoot 作为现代 Java 开发的核心框架,其启动流程的复杂性常让开发者感到困惑。本文将以源码视角,深入剖析从 main 方法到容器初始化的完整路径,帮助您理解 SpringBoot 的启动机制与事件监听扩展点,从而更高效地掌控应用生命周期。

启动流程的本质:容器初始化与 Bean 管理

SpringBoot 的启动流程,本质上仍是 Spring 框架的容器初始化过程,但通过自动配置与参数化设计,大幅简化了开发者的手动配置负担。无论是传统 Spring 还是 SpringBoot,其核心目标都是将 Bean 实例化并注册到容器中。SpringBoot 的自动配置机制预先定义了大量 Bean 的初始化流程,结合多样化的参数配置,使得启动过程呈现出一定的复杂性。开发者需要理解这些参数配置环节,以及容器初始化过程中执行的各项操作。

核心要点:SpringBoot 启动流程可视为对 Spring 启动流程的优化与增强,其本质仍是容器的创建与 Bean 的注册。

初始化参数分类:三大来源

SpringBoot 初始化参数根据其提供方可划分为三个主要类别,每个类别的参数均被封装为相应的对象:

  • 环境属性:来自 application.properties/yml、系统环境变量、命令行参数等,用于配置应用行为。
  • 配置类:由 @Configuration 标注的类,定义 Bean 的组装规则。
  • 自动配置类:由 spring.factories 或 AutoConfiguration.imports 加载,提供默认的 Bean 定义。

这些参数在启动时被加载为对象,供容器初始化阶段使用。下图展示了参数分类的详细结构:

参数类别说明
环境属性(Environment)系统环境变量、JVM 参数等
系统配置(spring.factories)SpringBoot 自动配置机制的核心配置
参数(Arguments、application.properties)应用程序参数和配置文件

启动流程全景图:从 main 到容器就绪

SpringBoot 启动流程可概括为以下步骤:

  1. 入口方法调用:主类 main 方法调用 SpringApplication.run,进行参数转换处理。
  2. 创建 SpringApplication 实例:初始化资源加载器、转换配置类信息、确定容器类型、获取系统配置引导信息、初始化上下文初始化器与监听器。
  3. 执行 run 方法:启动计时器、创建引导上下文、配置系统属性、触发启动事件、准备环境配置、打印启动横幅、创建应用上下文、准备上下文、刷新上下文、后置处理、记录启动日志、触发启动完成事件、调用运行器、触发就绪事件。

下图展示了完整的启动流程图解:

Springboot30StartupApplication10->SpringApplication.run(Springboot30StartupApplication.class, args);
SpringApplication1332->return run(new Class<?>[] { primarySource }, args);
  SpringApplication1343->return new SpringApplication(primarySources).run(args);

源码级追踪:SpringApplication 实例创建与 run 执行

在实例创建阶段,SpringApplication 构造函数会执行一系列初始化操作:

  • 初始化资源加载器(ResourceLoader)
  • 转换配置类信息(将主类转换为配置类)
  • 确定容器类型(Servlet/Reactive)
  • 获取系统配置引导信息(如 spring.factories)
  • 初始化上下文初始化器(ApplicationContextInitializer)
  • 初始化监听器(ApplicationListener)
  • 记录引导类信息

以下代码展示了实例创建的核心逻辑:

SpringApplication1343->SpringApplication(primarySources)
# 加载各种配置信息,初始化各种配置对象
SpringApplication266->this(null, primarySources);
SpringApplication280->public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources)
  SpringApplication281->this.resourceLoader = resourceLoader;
  # 初始化资源加载器
  SpringApplication283->this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    # 初始化配置类的类名信息(格式转换)
    SpringApplication284->this.webApplicationType = WebApplicationType.deduceFromClasspath();
    # 确认当前容器加载的类型
    SpringApplication285->this.bootstrapRegistryInitializers = getBootstrapRegistryInitializersFromSpringFactories();
    # 获取系统配置引导信息
    SpringApplication286->setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    # 获取ApplicationContextInitializer.class对应的实例
    SpringApplication287->setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    # 初始化监听器,对初始化过程及运行过程进行干预
    SpringApplication288->this.mainApplicationClass = deduceMainApplicationClass();
    # 初始化了引导类类名信息,备用

在 run 方法执行阶段,SpringBoot 会按照预定义的事件顺序触发各个阶段,并调用相应的监听器。关键步骤包括环境准备、上下文创建与刷新,以及运行器(Runner)的调用。以下代码展示了 run 方法的核心流程:

SpringApplication1343->new SpringApplication(primarySources).run(args)
# 初始化容器,得到ApplicationContext对象
SpringApplication323->StopWatch stopWatch = new StopWatch();
# 设置计时器
SpringApplication324->stopWatch.start();
# 计时开始
SpringApplication325->DefaultBootstrapContext bootstrapContext = createBootstrapContext();
# 系统引导信息对应的上下文对象
SpringApplication327->configureHeadlessProperty();
# 模拟输入输出信号,避免出现因缺少外设导致的信号传输失败,进而引发错误(模拟显示器,键盘,鼠标...)
# 配置项:java.awt.headless=true
SpringApplication328->SpringApplicationRunListeners listeners = getRunListeners(args);
# 获取当前注册的所有监听器
SpringApplication329->listeners.starting(bootstrapContext, this.mainApplicationClass);
# 监听器执行了对应的操作步骤
SpringApplication331->ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
# 获取参数
SpringApplication333->ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
# 将前期读取的数据加载成了一个环境对象,用来描述信息
SpringApplication333->configureIgnoreBeanInfo(environment);
# 做了一个配置,备用
SpringApplication334->Banner printedBanner = printBanner(environment);
# 初始化logo
SpringApplication335->context = createApplicationContext();
# 创建容器对象,根据前期配置的容器类型进行判定并创建
SpringApplication363->context.setApplicationStartup(this.applicationStartup);
# 设置启动模式
SpringApplication337->prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
# 对容器进行设置,参数来源于前期的设定
SpringApplication338->refreshContext(context);
# 刷新容器环境
SpringApplication339->afterRefresh(context, applicationArguments);
# 刷新完毕后做后处理
SpringApplication340->stopWatch.stop();
# 计时结束
SpringApplication341->if (this.logStartupInfo) {
# 判定是否记录启动时间的日志
SpringApplication342->    new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
# 创建日志对应的对象,输出日志信息,包含启动时间
SpringApplication344->listeners.started(context);
# 监听器执行了对应的操作步骤
SpringApplication345->callRunners(context, applicationArguments);
# 调用运行器
SpringApplication353->listeners.running(context);
# 监听器执行了对应的操作步骤

⚠️ 注意:刷新上下文(refreshContext)是容器初始化的核心,它负责 Bean 的实例化、自动配置的生效以及依赖注入的完成。

监听器模式与事件机制:优雅的扩展点

在实际开发中,我们常常需要在启动流程的特定阶段执行自定义逻辑,例如数据库环境检测、配置验证、资源预加载等。SpringBoot 采用了监听器模式(Listener Pattern),将启动过程抽象为一系列子事件,通过事件机制统一管理扩展点。

传统接口方案(如 Spring Bean 生命周期接口)存在接口数量过多、流程管理分散、扩展性受限等问题。例如,以下代码展示了传统接口方案的局限性:

public class Abc implements InitializingBean, DisposableBean {
public void destroy() throws Exception {
//销毁操作
}
public void afterPropertiesSet() throws Exception {
//初始化操作
}
}

而监听器模式的优势在于:

  • 统一管理:所有启动阶段的扩展点通过事件机制统一管理。
  • 松耦合:监听器与启动流程解耦,便于独立开发和测试。
  • 灵活扩展:开发者可以针对任意事件注册多个监听器,实现灵活的功能组合。

SpringBoot 启动事件类型与自定义监听器

SpringBoot 定义了多种启动事件类型,以下是部分关键事件:

事件类型触发时机说明
ApplicationStartingEvent应用启动开始在应用程序运行但尚未进行任何处理时发送
ApplicationEnvironmentPreparedEvent环境准备完成当 Environment 被使用且上下文创建之前发送
ApplicationContextInitializedEvent上下文初始化完成在 ApplicationContext 准备完成但尚未刷新时发送
ApplicationPreparedEvent应用准备完成在容器刷新之前、Bean 定义加载之后发送
ContextRefreshedEvent上下文刷新完成在 ApplicationContext 初始化或刷新时发送
ApplicationStartedEvent应用启动完成在上下文刷新之后且所有应用和命令行运行器被调用之前发送
ApplicationReadyEvent应用准备就绪在应用程序和命令行运行器被调用之后发送,用于通知应用已准备好处理请求
ContextClosedEvent上下文关闭在容器关闭时发送

开发者可以通过实现 ApplicationListener 接口或使用 @EventListener 注解来定义自定义监听器。以下是一个自定义监听器的示例:

// 定义监听器,在应用启动开始事件时执行自定义功能
public class MyListener implements ApplicationListener<ApplicationStartingEvent> {
  public void onApplicationEvent(ApplicationStartingEvent event) {
  // 自定义事件处理逻辑
  System.out.println("应用正在启动,执行自定义初始化操作...");
  }
  }

监听器的注册方式包括:

  • META-INF/spring.factories 中配置(META-INF/spring.factories
  • 使用 @Component 注解标注监听器类(@Component
  • 通过 SpringApplication.addListeners 方法手动添加(SpringApplication.addListeners()

通过上述方案,开发者可以在启动流程的任意节点进行干预,实现业务系统中的特定功能扩展。这种设计使得 SpringBoot 具备了良好的可扩展性,无需修改框架源码即可插入自定义逻辑。

[AFFILIATE_SLOT_1]

总结与最佳实践

SpringBoot 启动流程是容器初始化与事件监听机制的结合体。通过理解其核心步骤与扩展点,开发者可以更自信地控制应用启动行为。在实际项目中,建议:

  • 优先使用 ApplicationRunnerCommandLineRunner 执行启动后逻辑。
  • 对于启动阶段的自定义操作,合理选择事件类型和监听器注册方式。
  • 避免在监听器中执行耗时操作,以免影响启动性能。

掌握这些知识,将助您在微服务架构与 AI 应用开发中,更好地利用 SpringBoot 的自动化能力,专注于业务逻辑的实现。

[AFFILIATE_SLOT_2]