Spring Boot 启动原理揭秘:从 main 方法到自动装配
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 {@GetMappingpublic 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