Spring Boot 启动原理揭秘:从 main 方法到自动装配
引言
Spring Boot 作为 Java 领域最流行的开发框架之一,凭借其“开箱即用”的特性极大地简化了 Spring 应用的搭建和部署。然而,尽管开发者在日常工作中频繁使用 Spring Boot 的启动类(main
方法)和自动装配机制,真正理解其底层原理的人却并不多。本文将深入剖析 Spring Boot 的启动流程,从 main
方法入手,逐步揭示 Spring Boot 是如何通过自动装配实现零配置的奇迹的。
我们将按照以下结构展开:
- Spring Boot 简介与核心优势
- Spring Boot 应用的基本结构
- Spring Boot 的 main 方法分析
- Spring Boot 启动流程概述
- SpringApplication 类的作用与初始化过程
- Spring Boot 的上下文环境(ApplicationContext)创建过程
- Spring Boot 的自动装配机制详解
- Spring Boot 的条件化装配(Conditional On)机制
- Spring Boot 启动过程中的事件监听机制
- Spring Boot 启动过程中常见的扩展点与自定义配置
- 总结与最佳实践
接下来,我们将逐一解析这些内容,帮助你全面掌握 Spring Boot 的启动原理。
Spring Boot 简介与核心优势
什么是 Spring Boot?
Spring Boot 是由 Pivotal 团队推出的一个开源框架,旨在简化 Spring 应用的初始搭建和开发。它基于 Spring Framework 构建,提供了一种快速、便捷的方式来构建独立运行的、生产级的应用程序。Spring Boot 的核心理念是“约定优于配置”,这意味着开发者无需手动编写大量 XML 或 Java 配置代码即可快速启动项目。
Spring Boot 的核心优势
开箱即用(Opinionated Starter Dependencies)
Spring Boot 提供了一系列预配置的依赖项(称为 “Starter”),例如spring-boot-starter-web
、spring-boot-starter-data-jpa
等。这些依赖项已经集成了常用的库和默认配置,开发者只需引入对应的 Starter 即可直接使用相关功能,而无需手动配置复杂的依赖关系。内嵌服务器(Embedded Server)
Spring Boot 默认支持内嵌的 Tomcat、Jetty 或 Undertow 服务器,这意味着开发者可以将应用程序打包为一个独立的 JAR 文件,并直接运行,而无需额外部署到外部应用服务器。这大大简化了部署流程,提高了开发效率。自动装配(Auto-Configuration)
Spring Boot 最具代表性的特性之一就是自动装配。它能够根据类路径上的依赖项自动推断所需的配置,并注册相应的 Bean。例如,如果项目中包含 H2 数据库驱动,Spring Boot 会自动配置内存数据库连接池和相关的 DAO 组件,而无需开发者手动编写DataSource
配置。零配置(Zero Configuration)
Spring Boot 结合自动装配和默认约定,使得开发者几乎不需要编写任何 XML 或 Java 配置文件即可启动应用。这种零配置的理念极大降低了学习成本,提高了开发效率。生产就绪(Production Ready)
Spring Boot 提供了许多生产级别的功能,如健康检查(Health Check)、指标监控(Metrics)、日志管理(Log Management)等。这些功能可以帮助开发者更好地监控和维护应用程序。强大的 CLI 工具(Command Line Interface)
Spring Boot 提供了一个命令行工具(CLI),允许开发者通过简单的命令快速创建原型应用,甚至可以直接运行 Groovy 脚本而无需编译。丰富的文档和社区支持
Spring Boot 拥有庞大的社区和详尽的官方文档,开发者可以轻松找到解决方案或最佳实践。此外,Spring Boot 还与许多其他流行框架(如 Spring Cloud、Spring Security、Spring Data 等)无缝集成,形成了完整的生态系统。灵活的配置方式
Spring Boot 支持多种配置方式,包括application.properties
和application.yml
文件,同时支持外部配置(如环境变量、命令行参数等),便于在不同环境中进行动态调整。
综上所述,Spring Boot 凭借其简洁、高效、灵活的特点,成为现代 Java 开发的标准框架之一。接下来,我们将深入了解 Spring Boot 应用的基本结构,以便更清楚地理解它的启动机制。
Spring Boot 应用的基本结构
一个典型的 Spring Boot 项目通常遵循标准的 Maven 或 Gradle 项目结构,并结合 Spring Boot 的特定约定来组织代码。为了更好地理解 Spring Boot 的启动机制,我们需要先了解其基本的项目结构以及各个关键组成部分的作用。
1. 项目目录结构
Spring Boot 项目的标准目录结构如下所示:
my-springboot-project/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com.example.demo/
│ │ │ ├── DemoApplication.java
│ │ │ ├── controller/
│ │ │ ├── service/
│ │ │ └── repository/
│ │ └── resources/
│ │ ├── application.properties (或 application.yml)
│ │ └── static/
│ │ └── templates/
│ └── test/
│ └── java/
│ └── com.example.demo/
│ └── DemoApplicationTests.java
└── pom.xml (Maven) 或 build.gradle (Gradle)
(1)src/main/java
这是 Java 源代码存放的位置。通常,主类(包含 main
方法的类)位于包的根目录下,例如 com.example.demo.DemoApplication
。其余的业务逻辑代码,如 Controller、Service、Repository 等组件,则分别存放在不同的子包中。
(2)src/main/resources
该目录存放非 Java 文件资源,主要包括:
application.properties
或application.yml
:Spring Boot 的核心配置文件,用于设置端口号、数据源、日志级别等配置信息。static/
:静态资源目录,存放 HTML、CSS、JavaScript、图片等静态文件,这些文件会被直接映射到 Web 根路径下。templates/
:模板引擎资源目录,适用于 Thymeleaf、Freemarker 等模板引擎,用于渲染动态页面。
(3)src/test/java
这是单元测试代码存放的位置。Spring Boot 提供了对测试的良好支持,通常使用 JUnit 编写测试类,确保代码质量。
(4)pom.xml
(Maven)或 build.gradle
(Gradle)
这是项目的构建配置文件。Maven 使用 pom.xml
来声明依赖项和插件,Gradle 则使用 build.gradle
。Spring Boot 项目通常会引入 spring-boot-starter-*
相关的依赖,以启用对应的功能模块。
2. 主类(Main Class)
Spring Boot 应用的主类是带有 @SpringBootApplication
注解的类,并且包含 main
方法。例如:
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
(1)@SpringBootApplication
注解
@SpringBootApplication
是 Spring Boot 中最重要的注解之一,它是一个组合注解,包含了以下三个核心注解:
@SpringBootConfiguration
:表示该类是一个 Spring Boot 的配置类,本质上是@Configuration
的变体。@ComponentScan
:自动扫描并注册 Bean,通常扫描当前类所在包及其子包下的组件。@EnableAutoConfiguration
:启用 Spring Boot 的自动装配机制,这是 Spring Boot 实现零配置的核心功能之一。
(2)main
方法
main
方法是 Java 应用程序的入口点,Spring Boot 通过调用 SpringApplication.run()
方法来启动应用。该方法会触发一系列内部机制,包括上下文初始化、自动装配、Web 服务器启动等操作。
3. 控制器(Controller)
控制器负责处理 HTTP 请求,通常使用 @RestController
或 @Controller
注解。例如:
@RestController
@RequestMapping("/hello")
public class HelloController {
@GetMapping
public String sayHello() {
return "Hello, Spring Boot!";
}
}
4. 服务层(Service)
服务层负责处理业务逻辑,通常使用 @Service
注解标注:
@Service
public class HelloService {
public String getGreeting() {
return "Welcome to Spring Boot!";
}
}
5. 数据访问层(Repository)
数据访问层负责与数据库交互,通常使用 @Repository
注解标注:
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
}
6. 启动流程概览
当我们执行 DemoApplication.main()
方法时,Spring Boot 会经历以下几个主要阶段:
- 加载 Spring Boot 的自动装配机制:根据类路径上的依赖项,自动配置各类组件。
- 创建 Spring 应用上下文(ApplicationContext):初始化 IoC 容器,注册 Bean。
- 启动内嵌 Web 服务器(如 Tomcat):如果是 Web 应用,则启动 Web 服务器并监听指定端口。
- 执行 CommandLineRunner 或 ApplicationRunner:如果有自定义的启动任务,可以在应用启动完成后执行。
接下来,我们将深入探讨 Spring Boot 的 main
方法,分析其背后的执行流程,以及它是如何触发整个应用的启动过程的。
Spring Boot 的 main 方法分析
在 Spring Boot 应用程序中,main
方法是整个应用的入口点。虽然它的代码看起来非常简单,但背后隐藏着复杂的启动机制。Spring Boot 通过 SpringApplication.run()
方法实现了高度封装的启动流程,使开发者无需关心底层细节即可快速启动应用。
1. main
方法的典型写法
一个典型的 Spring Boot 应用程序的 main
方法如下所示:
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
这段代码看似简单,但它实际上触发了 Spring Boot 应用的完整生命周期。我们可以将其拆分为两个部分来理解:
SpringApplication
类的构造run()
方法的执行流程
2. SpringApplication
类的构造
SpringApplication
是 Spring Boot 提供的一个核心类,用于引导和配置 Spring 应用上下文。当调用 SpringApplication.run(DemoApplication.class, args)
时,首先会创建一个 SpringApplication
实例。
(1)构造函数源码分析
public SpringApplication(Class<?> primarySource) {
this(null, primarySource);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?> primarySource) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySource, "Primary source must not be null");
this.primarySources = new LinkedHashSet<>();
this.primarySources.add(primarySource);
this.webApplicationType = WebApplicationType.deduceFromClasspath();
setInitializers((List) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
setListeners((List) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
在这个构造过程中,SpringApplication
会完成以下几项关键操作:
a. 设置主类(Primary Source)
主类(Primary Source)指的是包含 main
方法的类,也就是我们传入的 DemoApplication.class
。Spring Boot 会利用这个类来确定组件扫描的起点。
b. 推断 Web 应用类型
webApplicationType = WebApplicationType.deduceFromClasspath();
这一行代码用于判断当前是否为 Web 应用。它会检查类路径中是否存在 javax.servlet.Servlet
或 org.springframework.web.reactive.DispatcherHandler
等类,从而决定是否需要启动 Web 容器(如 Tomcat)。
c. 加载 ApplicationContextInitializer
setInitializers((List) getSpringFactoriesInstances(ApplicationContextInitializer.class));
ApplicationContextInitializer
是 Spring 上下文刷新前的回调接口,用于在上下文创建之前执行一些初始化操作。Spring Boot 会从 META-INF/spring.factories
文件中读取所有注册的 ApplicationContextInitializer
实现类,并将其加入初始化列表。
d. 加载 ApplicationListener
setListeners((List) getSpringFactoriesInstances(ApplicationListener.class));
ApplicationListener
是 Spring 事件监听机制的一部分,用于监听 Spring 应用生命周期中的各种事件。Spring Boot 会在这里加载所有的 ApplicationListener
实现类,以便后续在启动过程中触发相应的事件通知。
e. 推断主类
this.mainApplicationClass = deduceMainApplicationClass();
此步骤用于确定主类(即包含 main
方法的类),以便在后续的日志输出和异常报告中使用。
3. run()
方法的执行流程
SpringApplication.run()
是 Spring Boot 启动的核心方法,它负责创建并启动 Spring 应用上下文。其大致执行流程如下:
(1)记录启动时间并触发启动事件
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
StopWatch
用于记录启动时间,方便性能分析。getRunListeners()
会从META-INF/spring.factories
中加载所有SpringApplicationRunListener
实现类,用于监听启动过程中的各个阶段。listeners.starting()
触发starting
事件,通知所有监听者应用开始启动。
(2)准备环境(Environment)
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
prepareEnvironment()
方法用于创建并配置Environment
对象,其中包括系统属性、JVM 参数、application.properties
配置等。configureIgnoreBeanInfo()
用于优化 Java Beans Introspection 性能。
(3)打印 Banner
printBanner(environment);
- 如果用户没有禁用 Banner,Spring Boot 会在控制台打印出经典的 ASCII 字符图案,并显示版本信息。
(4)创建 Spring 应用上下文
context = createApplicationContext();
analyzers = new FailureAnalyzers(context);
createApplicationContext()
会根据webApplicationType
的值选择合适的ApplicationContext
实现类,例如:AnnotationConfigServletWebServerApplicationContext
(Web 应用)AnnotationConfigApplicationContext
(普通 Java 应用)
FailureAnalyzers
用于捕获启动失败时的异常,并提供详细的错误分析信息。
(5)准备上下文
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
- 此步骤会将
Environment
、ApplicationArguments
、Banner
等对象注入到上下文中。 - 同时,还会执行所有注册的
ApplicationContextInitializer
,并对上下文进行进一步的定制。
(6)刷新上下文
refreshContext(context);
refreshContext()
会调用 Spring 的AbstractApplicationContext.refresh()
方法,触发 Spring 容器的初始化流程。- 在这个过程中,Spring 会加载所有的 Bean 定义,并完成自动装配、依赖注入等核心操作。
(7)结束启动流程
afterRefresh(context, applicationArguments);
stopWatch.stop();
listeners.finished(context, null);
afterRefresh()
会执行所有CommandLineRunner
和ApplicationRunner
接口的实现类,用于在应用启动后执行自定义逻辑。stopWatch.stop()
记录启动耗时。listeners.finished()
通知所有监听者应用启动完成。
4. 小结
Spring Boot 的 main
方法虽然只有一行代码,但它背后隐藏着一套高度封装的启动流程。通过 SpringApplication
类的构造和 run()
方法的执行,Spring Boot 会依次完成以下操作:
- 初始化 Spring Boot 的核心组件(如
ApplicationContextInitializer
、ApplicationListener
等)。 - 创建并配置
Environment
,加载外部配置。 - 打印 Banner 并创建 Spring 应用上下文。
- 准备上下文并执行自定义初始化逻辑。
- 刷新上下文,完成自动装配和依赖注入。
- 启动完成后执行
CommandLineRunner
或ApplicationRunner
。
理解 main
方法的执行流程有助于我们深入掌握 Spring Boot 的启动机制,为后续的调试和优化打下基础。接下来,我们将进一步探讨 Spring Boot 启动流程的整体架构,帮助你建立完整的认知体系。
Spring Boot 启动流程概述
Spring Boot 的启动流程是一套高度封装且高度可扩展的机制,涵盖了从应用启动到上下文初始化、自动装配、Web 服务器启动等多个阶段。理解整个启动流程不仅有助于排查启动问题,还能帮助开发者更好地利用 Spring Boot 的扩展机制进行自定义开发。
1. Spring Boot 启动流程的整体架构
Spring Boot 的启动流程可以划分为以下几个主要阶段:
阶段 | 描述 |
---|---|
1. 创建 SpringApplication 实例 |
初始化 Spring Boot 的核心组件,包括 ApplicationContextInitializer 、ApplicationListener 、推断 Web 应用类型等 |
2. 触发 starting 事件 |
通知所有监听者应用即将启动 |
3. 准备 Environment |
加载系统环境变量、JVM 参数、application.properties 等配置 |
4. 打印 Banner | 显示 Spring Boot 的欢迎信息 |
5. 创建 ApplicationContext |
根据应用类型(Web 或非 Web)创建合适的上下文实例 |
6. 准备上下文 | 注入 Environment 、ApplicationArguments 、Banner 等,并执行 ApplicationContextInitializer |
7. 刷新上下文 | 触发 Spring 容器的初始化流程,包括自动装配、依赖注入等 |
8. 启动完成后执行 CommandLineRunner 或 ApplicationRunner |
在应用启动完成后执行自定义逻辑 |
9. 触发 finished 事件 |
通知所有监听者应用启动完成 |
2. Spring Boot 启动流程的关键组件
在整个启动流程中,涉及多个核心组件,它们协同工作,共同完成 Spring Boot 的启动过程。
(1)SpringApplication
类
SpringApplication
是 Spring Boot 启动流程的核心类,负责引导整个应用的启动。它提供了 run()
方法作为启动入口,并在内部封装了上下文创建、环境配置、事件监听等关键操作。
(2)SpringApplicationRunListener
SpringApplicationRunListener
是 Spring Boot 启动过程中的事件监听器接口,用于监听启动的不同阶段(如 starting
、environmentPrepared
、contextPrepared
、contextLoaded
、finished
等)。Spring Boot 默认提供了 EventPublishingRunListener
,它会将这些事件广播给所有注册的 ApplicationListener
。
(3)ApplicationContextInitializer
ApplicationContextInitializer
是 Spring 上下文初始化的回调接口,用于在上下文创建之后、刷新之前执行一些自定义的初始化逻辑。开发者可以通过实现该接口来自定义上下文的行为。
(4)ApplicationListener
ApplicationListener
是 Spring 事件监听机制的一部分,用于监听 Spring 应用生命周期中的各种事件。Spring Boot 在启动过程中会发布多个事件,如 ApplicationStartingEvent
、ApplicationReadyEvent
、ApplicationFailedEvent
等,开发者可以通过监听这些事件来执行自定义逻辑。
(5)Environment
Environment
是 Spring 应用的环境抽象,它包含了系统环境变量、JVM 参数、application.properties
配置等信息。Spring Boot 会在启动过程中加载这些配置,并将其注入到上下文中。
(6)Banner
Banner
是 Spring Boot 启动时打印的欢迎信息,默认情况下会在控制台显示 Spring Boot 的 ASCII 图案。开发者可以通过自定义 banner.txt
文件或实现 Banner
接口来修改或禁用 Banner。
(7)CommandLineRunner
和 ApplicationRunner
这两个接口用于在应用启动完成后执行自定义逻辑。CommandLineRunner
接收原始的 String[] args
参数,而 ApplicationRunner
接收封装后的 ApplicationArguments
对象。
(8)FailureAnalyzers
FailureAnalyzers
是 Spring Boot 提供的一种启动失败分析机制,它会在应用启动失败时收集异常信息,并提供详细的错误分析报告,帮助开发者快速定位问题。
3. Spring Boot 启动流程的执行顺序
Spring Boot 的启动流程可以细分为以下几个步骤:
(1)初始化阶段
- 创建
SpringApplication
实例 - 推断 Web 应用类型
- 加载
ApplicationContextInitializer
- 加载
ApplicationListener
- 推断主类(
mainApplicationClass
)
(2)启动阶段
- 触发
starting
事件 - 准备
Environment
- 配置
ignoreBeanInfo
- 打印 Banner
(3)上下文创建阶段
- 创建
ApplicationContext
- 创建
FailureAnalyzers
- 准备上下文(注入
Environment
、ApplicationArguments
、Banner
) - 执行
ApplicationContextInitializer
- 发布
contextPrepared
事件 - 加载应用的主类(
primarySource
)
(4)上下文刷新阶段
- 刷新上下文(
refreshContext()
) - 触发 Spring 容器的初始化流程(如自动装配、依赖注入)
- 发布
applicationReadyEvent
事件
(5)启动完成阶段
- 执行
CommandLineRunner
和ApplicationRunner
- 停止
StopWatch
,记录启动耗时 - 触发
finished
事件
4. Spring Boot 启动流程的扩展点
Spring Boot 提供了多个扩展点,允许开发者在启动过程中插入自定义逻辑。这些扩展点包括:
SpringApplicationRunListener
:监听启动的不同阶段ApplicationContextInitializer
:在上下文创建后执行自定义初始化逻辑ApplicationListener
:监听 Spring 应用生命周期中的各种事件CommandLineRunner
/ApplicationRunner
:在应用启动完成后执行自定义逻辑
通过合理利用这些扩展点,开发者可以在不修改 Spring Boot 内部逻辑的情况下,实现高度定制化的启动行为。
5. 小结
Spring Boot 的启动流程是一个高度封装且高度可扩展的机制,涵盖了从应用启动到上下文初始化、自动装配、Web 服务器启动等多个阶段。通过理解整个启动流程,开发者可以更好地掌握 Spring Boot 的运行机制,并利用其提供的扩展点进行自定义开发。接下来,我们将深入探讨 SpringApplication
类的具体作用及其在启动过程中的初始化流程。
SpringApplication 类的作用与初始化过程
SpringApplication
是 Spring Boot 启动流程的核心类,它负责引导整个应用的启动过程。虽然开发者通常只需要调用 SpringApplication.run()
方法即可启动应用,但其内部封装了大量逻辑,包括上下文初始化、环境配置、事件监听等关键操作。理解 SpringApplication
的作用及其初始化过程,有助于我们更深入地掌握 Spring Boot 的启动机制。
1. SpringApplication
的作用
SpringApplication
类的主要职责包括:
- 推断应用类型:判断当前应用是 Web 应用还是普通 Java 应用。
- 加载
ApplicationContextInitializer
:用于在上下文创建之前执行自定义初始化逻辑。 - 加载
ApplicationListener
:监听 Spring Boot 启动过程中的各种事件。 - 推断主类(Main Application Class):确定包含
main
方法的类,以便在后续操作中使用。 - 创建
ApplicationContext
:根据应用类型(Web 或非 Web)创建合适的上下文实例。 - 触发启动事件:通知所有监听者应用启动的不同阶段(如
starting
、environmentPrepared
、contextPrepared
、finished
等)。
2. SpringApplication
的构造过程
SpringApplication
的构造函数是启动流程的第一步,它会初始化核心组件,并推断应用的基本信息。其构造函数的源码如下:
public SpringApplication(ResourceLoader resourceLoader, Class<?> primarySource) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySource, "Primary source must not be null");
this.primarySources = new LinkedHashSet<>();
this.primarySources.add(primarySource);
this.webApplicationType = WebApplicationType.deduceFromClasspath();
setInitializers((List) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((List) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
让我们逐一分析每个关键步骤。
(1)设置主类(Primary Source)
this.primarySources.add(primarySource);
主类(Primary Source)指的是包含 main
方法的类,即我们在调用 SpringApplication.run()
时传入的类(如 DemoApplication.class
)。Spring Boot 会利用这个类来确定组件扫描的起点。
(2)推断 Web 应用类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
这一行代码用于判断当前是否为 Web 应用。它会检查类路径中是否存在 javax.servlet.Servlet
或 org.springframework.web.reactive.DispatcherHandler
等类,从而决定是否需要启动 Web 容器(如 Tomcat)。
(3)加载 ApplicationContextInitializer
setInitializers((List) getSpringFactoriesInstances(ApplicationContextInitializer.class));
ApplicationContextInitializer
是 Spring 上下文初始化的回调接口,用于在上下文创建之后、刷新之前执行一些自定义的初始化逻辑。Spring Boot 会从 META-INF/spring.factories
文件中读取所有注册的 ApplicationContextInitializer
实现类,并将其加入初始化列表。
例如,在 spring-boot-autoconfigure
模块的 META-INF/spring.factories
文件中,可能会包含如下配置:
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
(4)加载 ApplicationListener
setListeners((List) getSpringFactoriesInstances(ApplicationListener.class));
ApplicationListener
是 Spring 事件监听机制的一部分,用于监听 Spring 应用生命周期中的各种事件。Spring Boot 会在这里加载所有的 ApplicationListener
实现类,以便后续在启动过程中触发相应的事件通知。
例如,在 spring-boot-autoconfigure
模块的 META-INF/spring.factories
文件中,可能会包含如下配置:
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
(5)推断主类
this.mainApplicationClass = deduceMainApplicationClass();
此步骤用于确定主类(即包含 main
方法的类),以便在后续的日志输出和异常报告中使用。
3. SpringApplication
的 run()
方法
在 SpringApplication
实例创建完成后,下一步是调用 run()
方法,启动整个应用。其核心代码如下:
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
printBanner(environment);
context = createApplicationContext();
analyzers = new FailureAnalyzers(context);
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
listeners.finished(context, null);
stopWatch.stop();
return context;
} catch (Throwable ex) {
handleRunFailure(context, ex, analyzers, listeners);
throw new IllegalStateException(ex);
}
}
让我们逐个分析 run()
方法中的关键步骤。
(1)记录启动时间并触发 starting
事件
StopWatch stopWatch = new StopWatch();
stopWatch.start();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
StopWatch
用于记录启动时间,方便性能分析。getRunListeners()
会从META-INF/spring.factories
中加载所有SpringApplicationRunListener
实现类,用于监听启动过程中的各个阶段。listeners.starting()
触发starting
事件,通知所有监听者应用开始启动。
(2)准备 Environment
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
prepareEnvironment()
方法用于创建并配置Environment
对象,其中包括系统属性、JVM 参数、application.properties
配置等。configureIgnoreBeanInfo()
用于优化 Java Beans Introspection 性能。
(3)打印 Banner
printBanner(environment);
- 如果用户没有禁用 Banner,Spring Boot 会在控制台打印出经典的 ASCII 字符图案,并显示版本信息。
(4)创建 ApplicationContext
context = createApplicationContext();
analyzers = new FailureAnalyzers(context);
createApplicationContext()
会根据webApplicationType
的值选择合适的ApplicationContext
实现类,例如:AnnotationConfigServletWebServerApplicationContext
(Web 应用)AnnotationConfigApplicationContext
(普通 Java 应用)
FailureAnalyzers
用于捕获启动失败时的异常,并提供详细的错误分析信息。
(5)准备上下文
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
- 此步骤会将
Environment
、ApplicationArguments
、Banner
等对象注入到上下文中。 - 同时,还会执行所有注册的
ApplicationContextInitializer
,并对上下文进行进一步的定制。
(6)刷新上下文
refreshContext(context);
refreshContext()
会调用 Spring 的AbstractApplicationContext.refresh()
方法,触发 Spring 容器的初始化流程。- 在这个过程中,Spring 会加载所有的 Bean 定义,并完成自动装配、依赖注入等核心操作。
(7)启动完成后执行 CommandLineRunner
或 ApplicationRunner
afterRefresh(context, applicationArguments);
afterRefresh()
会执行所有CommandLineRunner
和ApplicationRunner
接口的实现类,用于在应用启动后执行自定义逻辑。
(8)结束启动流程
listeners.finished(context, null);
stopWatch.stop();
listeners.finished()
通知所有监听者应用启动完成。stopWatch.stop()
记录启动耗时。
4. 小结
SpringApplication
是 Spring Boot 启动流程的核心类,它负责引导整个应用的启动过程。其构造函数会初始化核心组件(如 ApplicationContextInitializer
、ApplicationListener
、推断 Web 应用类型等),并在 run()
方法中依次完成以下操作:
- 初始化 Spring Boot 的核心组件
- 创建并配置
Environment
- 打印 Banner 并创建 Spring 应用上下文
- 准备上下文并执行自定义初始化逻辑
- 刷新上下文,完成自动装配和依赖注入
- 启动完成后执行
CommandLineRunner
或ApplicationRunner