Spring Boot 启动原理揭秘:从 main 方法到自动装配

Spring Boot 启动原理揭秘:从 main 方法到自动装配

引言

Spring Boot 作为 Java 领域最流行的开发框架之一,凭借其“开箱即用”的特性极大地简化了 Spring 应用的搭建和部署。然而,尽管开发者在日常工作中频繁使用 Spring Boot 的启动类(main 方法)和自动装配机制,真正理解其底层原理的人却并不多。本文将深入剖析 Spring Boot 的启动流程,从 main 方法入手,逐步揭示 Spring Boot 是如何通过自动装配实现零配置的奇迹的。

我们将按照以下结构展开:

  1. Spring Boot 简介与核心优势
  2. Spring Boot 应用的基本结构
  3. Spring Boot 的 main 方法分析
  4. Spring Boot 启动流程概述
  5. SpringApplication 类的作用与初始化过程
  6. Spring Boot 的上下文环境(ApplicationContext)创建过程
  7. Spring Boot 的自动装配机制详解
  8. Spring Boot 的条件化装配(Conditional On)机制
  9. Spring Boot 启动过程中的事件监听机制
  10. Spring Boot 启动过程中常见的扩展点与自定义配置
  11. 总结与最佳实践

接下来,我们将逐一解析这些内容,帮助你全面掌握 Spring Boot 的启动原理。

Spring Boot 简介与核心优势

什么是 Spring Boot?

Spring Boot 是由 Pivotal 团队推出的一个开源框架,旨在简化 Spring 应用的初始搭建和开发。它基于 Spring Framework 构建,提供了一种快速、便捷的方式来构建独立运行的、生产级的应用程序。Spring Boot 的核心理念是“约定优于配置”,这意味着开发者无需手动编写大量 XML 或 Java 配置代码即可快速启动项目。

Spring Boot 的核心优势

  1. 开箱即用(Opinionated Starter Dependencies)
    Spring Boot 提供了一系列预配置的依赖项(称为 “Starter”),例如 spring-boot-starter-webspring-boot-starter-data-jpa 等。这些依赖项已经集成了常用的库和默认配置,开发者只需引入对应的 Starter 即可直接使用相关功能,而无需手动配置复杂的依赖关系。

  2. 内嵌服务器(Embedded Server)
    Spring Boot 默认支持内嵌的 Tomcat、Jetty 或 Undertow 服务器,这意味着开发者可以将应用程序打包为一个独立的 JAR 文件,并直接运行,而无需额外部署到外部应用服务器。这大大简化了部署流程,提高了开发效率。

  3. 自动装配(Auto-Configuration)
    Spring Boot 最具代表性的特性之一就是自动装配。它能够根据类路径上的依赖项自动推断所需的配置,并注册相应的 Bean。例如,如果项目中包含 H2 数据库驱动,Spring Boot 会自动配置内存数据库连接池和相关的 DAO 组件,而无需开发者手动编写 DataSource 配置。

  4. 零配置(Zero Configuration)
    Spring Boot 结合自动装配和默认约定,使得开发者几乎不需要编写任何 XML 或 Java 配置文件即可启动应用。这种零配置的理念极大降低了学习成本,提高了开发效率。

  5. 生产就绪(Production Ready)
    Spring Boot 提供了许多生产级别的功能,如健康检查(Health Check)、指标监控(Metrics)、日志管理(Log Management)等。这些功能可以帮助开发者更好地监控和维护应用程序。

  6. 强大的 CLI 工具(Command Line Interface)
    Spring Boot 提供了一个命令行工具(CLI),允许开发者通过简单的命令快速创建原型应用,甚至可以直接运行 Groovy 脚本而无需编译。

  7. 丰富的文档和社区支持
    Spring Boot 拥有庞大的社区和详尽的官方文档,开发者可以轻松找到解决方案或最佳实践。此外,Spring Boot 还与许多其他流行框架(如 Spring Cloud、Spring Security、Spring Data 等)无缝集成,形成了完整的生态系统。

  8. 灵活的配置方式
    Spring Boot 支持多种配置方式,包括 application.propertiesapplication.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.propertiesapplication.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 会经历以下几个主要阶段:

  1. 加载 Spring Boot 的自动装配机制:根据类路径上的依赖项,自动配置各类组件。
  2. 创建 Spring 应用上下文(ApplicationContext):初始化 IoC 容器,注册 Bean。
  3. 启动内嵌 Web 服务器(如 Tomcat):如果是 Web 应用,则启动 Web 服务器并监听指定端口。
  4. 执行 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.Servletorg.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);
  • 此步骤会将 EnvironmentApplicationArgumentsBanner 等对象注入到上下文中。
  • 同时,还会执行所有注册的 ApplicationContextInitializer,并对上下文进行进一步的定制。
(6)刷新上下文
refreshContext(context);
  • refreshContext() 会调用 Spring 的 AbstractApplicationContext.refresh() 方法,触发 Spring 容器的初始化流程。
  • 在这个过程中,Spring 会加载所有的 Bean 定义,并完成自动装配、依赖注入等核心操作。
(7)结束启动流程
afterRefresh(context, applicationArguments);
stopWatch.stop();
listeners.finished(context, null);
  • afterRefresh() 会执行所有 CommandLineRunnerApplicationRunner 接口的实现类,用于在应用启动后执行自定义逻辑。
  • stopWatch.stop() 记录启动耗时。
  • listeners.finished() 通知所有监听者应用启动完成。

4. 小结

Spring Boot 的 main 方法虽然只有一行代码,但它背后隐藏着一套高度封装的启动流程。通过 SpringApplication 类的构造和 run() 方法的执行,Spring Boot 会依次完成以下操作:

  1. 初始化 Spring Boot 的核心组件(如 ApplicationContextInitializerApplicationListener 等)。
  2. 创建并配置 Environment,加载外部配置。
  3. 打印 Banner 并创建 Spring 应用上下文。
  4. 准备上下文并执行自定义初始化逻辑。
  5. 刷新上下文,完成自动装配和依赖注入。
  6. 启动完成后执行 CommandLineRunnerApplicationRunner

理解 main 方法的执行流程有助于我们深入掌握 Spring Boot 的启动机制,为后续的调试和优化打下基础。接下来,我们将进一步探讨 Spring Boot 启动流程的整体架构,帮助你建立完整的认知体系。

Spring Boot 启动流程概述

Spring Boot 的启动流程是一套高度封装且高度可扩展的机制,涵盖了从应用启动到上下文初始化、自动装配、Web 服务器启动等多个阶段。理解整个启动流程不仅有助于排查启动问题,还能帮助开发者更好地利用 Spring Boot 的扩展机制进行自定义开发。

1. Spring Boot 启动流程的整体架构

Spring Boot 的启动流程可以划分为以下几个主要阶段:

阶段描述
1. 创建 SpringApplication 实例初始化 Spring Boot 的核心组件,包括 ApplicationContextInitializerApplicationListener、推断 Web 应用类型等
2. 触发 starting 事件通知所有监听者应用即将启动
3. 准备 Environment加载系统环境变量、JVM 参数、application.properties 等配置
4. 打印 Banner显示 Spring Boot 的欢迎信息
5. 创建 ApplicationContext根据应用类型(Web 或非 Web)创建合适的上下文实例
6. 准备上下文注入 EnvironmentApplicationArgumentsBanner 等,并执行 ApplicationContextInitializer
7. 刷新上下文触发 Spring 容器的初始化流程,包括自动装配、依赖注入等
8. 启动完成后执行 CommandLineRunnerApplicationRunner在应用启动完成后执行自定义逻辑
9. 触发 finished 事件通知所有监听者应用启动完成

2. Spring Boot 启动流程的关键组件

在整个启动流程中,涉及多个核心组件,它们协同工作,共同完成 Spring Boot 的启动过程。

(1)SpringApplication

SpringApplication 是 Spring Boot 启动流程的核心类,负责引导整个应用的启动。它提供了 run() 方法作为启动入口,并在内部封装了上下文创建、环境配置、事件监听等关键操作。

(2)SpringApplicationRunListener

SpringApplicationRunListener 是 Spring Boot 启动过程中的事件监听器接口,用于监听启动的不同阶段(如 startingenvironmentPreparedcontextPreparedcontextLoadedfinished 等)。Spring Boot 默认提供了 EventPublishingRunListener,它会将这些事件广播给所有注册的 ApplicationListener

(3)ApplicationContextInitializer

ApplicationContextInitializer 是 Spring 上下文初始化的回调接口,用于在上下文创建之后、刷新之前执行一些自定义的初始化逻辑。开发者可以通过实现该接口来自定义上下文的行为。

(4)ApplicationListener

ApplicationListener 是 Spring 事件监听机制的一部分,用于监听 Spring 应用生命周期中的各种事件。Spring Boot 在启动过程中会发布多个事件,如 ApplicationStartingEventApplicationReadyEventApplicationFailedEvent 等,开发者可以通过监听这些事件来执行自定义逻辑。

(5)Environment

Environment 是 Spring 应用的环境抽象,它包含了系统环境变量、JVM 参数、application.properties 配置等信息。Spring Boot 会在启动过程中加载这些配置,并将其注入到上下文中。

(6)Banner

Banner 是 Spring Boot 启动时打印的欢迎信息,默认情况下会在控制台显示 Spring Boot 的 ASCII 图案。开发者可以通过自定义 banner.txt 文件或实现 Banner 接口来修改或禁用 Banner。

(7)CommandLineRunnerApplicationRunner

这两个接口用于在应用启动完成后执行自定义逻辑。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
  • 准备上下文(注入 EnvironmentApplicationArgumentsBanner
  • 执行 ApplicationContextInitializer
  • 发布 contextPrepared 事件
  • 加载应用的主类(primarySource
(4)上下文刷新阶段
  • 刷新上下文(refreshContext()
  • 触发 Spring 容器的初始化流程(如自动装配、依赖注入)
  • 发布 applicationReadyEvent 事件
(5)启动完成阶段
  • 执行 CommandLineRunnerApplicationRunner
  • 停止 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 类的主要职责包括:

  1. 推断应用类型:判断当前应用是 Web 应用还是普通 Java 应用。
  2. 加载 ApplicationContextInitializer:用于在上下文创建之前执行自定义初始化逻辑。
  3. 加载 ApplicationListener:监听 Spring Boot 启动过程中的各种事件。
  4. 推断主类(Main Application Class):确定包含 main 方法的类,以便在后续操作中使用。
  5. 创建 ApplicationContext:根据应用类型(Web 或非 Web)创建合适的上下文实例。
  6. 触发启动事件:通知所有监听者应用启动的不同阶段(如 startingenvironmentPreparedcontextPreparedfinished 等)。

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.Servletorg.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. SpringApplicationrun() 方法

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);
  • 此步骤会将 EnvironmentApplicationArgumentsBanner 等对象注入到上下文中。
  • 同时,还会执行所有注册的 ApplicationContextInitializer,并对上下文进行进一步的定制。
(6)刷新上下文
refreshContext(context);
  • refreshContext() 会调用 Spring 的 AbstractApplicationContext.refresh() 方法,触发 Spring 容器的初始化流程。
  • 在这个过程中,Spring 会加载所有的 Bean 定义,并完成自动装配、依赖注入等核心操作。
(7)启动完成后执行 CommandLineRunnerApplicationRunner
afterRefresh(context, applicationArguments);
  • afterRefresh() 会执行所有 CommandLineRunnerApplicationRunner 接口的实现类,用于在应用启动后执行自定义逻辑。
(8)结束启动流程
listeners.finished(context, null);
stopWatch.stop();
  • listeners.finished() 通知所有监听者应用启动完成。
  • stopWatch.stop() 记录启动耗时。

4. 小结

SpringApplication 是 Spring Boot 启动流程的核心类,它负责引导整个应用的启动过程。其构造函数会初始化核心组件(如 ApplicationContextInitializerApplicationListener、推断 Web 应用类型等),并在 run() 方法中依次完成以下操作:

  1. 初始化 Spring Boot 的核心组件
  2. 创建并配置 Environment
  3. 打印 Banner 并创建 Spring 应用上下文
  4. 准备上下文并执行自定义初始化逻辑
  5. 刷新上下文,完成自动装配和依赖注入
  6. 启动完成后执行 CommandLineRunnerApplicationRunner

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/88994.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/88994.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OpenCV 与深度学习:从图像分类到目标检测技术

一、深度学习&#xff1a;从 “人工设计” 到 “自动学习”1.1 深度学习的定位&#xff1a;AI、机器学习与深度学习的关系人工智能&#xff08;AI&#xff09;&#xff1a;是一个宽泛的领域&#xff0c;目标是构建能模拟人类智能的系统&#xff0c;涵盖推理、感知、决策等能力。…

Docker 镜像推送至 Coding 制品仓库超时问题排查与解决

Docker 镜像推送至 Coding 制品仓库超时问题排查与解决 在将 Docker 镜像推送至 Coding 的制品仓库时&#xff0c;可能会遇到 docker push 命令超时失败的问题。但使用 curl -i http://xxx.coding.xxxx.xx 测试时&#xff0c;连接却能成功建立。以下是排查过程及解决方案。 问题…

https交互原理

Https 交互时序图&#xff1a;HTTPS 通信中结合 RSA 和 AES 加密的流程&#xff0c;本质是利用 RSA 的安全特性交换 AES 密钥&#xff0c;再用高效的 AES 加密实际数据传输。HTTPS 交互核心流程&#xff08;TLS/SSL 握手&#xff09; 1. 建立 TCP 连接 客户端通过 TCP 三次握手…

LSTM入门案例(时间序列预测)| pytorch实现

需求 假如我有一个时间序列&#xff0c;例如是前113天的价格数据&#xff08;训练集&#xff09;&#xff0c;然后我希望借此预测后30天的数据&#xff08;测试集&#xff09;&#xff0c;实际上这143天的价格数据都已经有了。这里为了简单&#xff0c;每一天的数据只有一个价…

WPS、Word加载项开发流程(免费最简版本)

文章目录1 加载项对比2 WPS 加载项2.1 本地开发2.1.1 准备开发环境2.1.2 新建 WPS 加载项项目2.1.3 运行项目2.2 在线部署2.2.1 编译项目2.2.2 部署项目2.2.3 生成分发文件2.2.4 部署分发文件2.3 安装加载项2.4 取消发布3 Word 加载项3.1 本地开发3.1.1 准备开发环境3.1.2 新建…

Flink SQL 性能优化实战

最近我们组在大规模上线Flink SQL作业。首先&#xff0c;在进行跑批量初始化完历史数据后&#xff0c;剩下的就是消费Kafka历史数据进行追数了。但是发现某些作业的追数过程十分缓慢&#xff0c;要运行一晚上甚至三四天才能追上最新数据。由于是实时数仓指标计算上线初期&#…

HTML 树结构(DOM)深入讲解教程

一、HTML 树结构的核心概念 1.1 DOM&#xff08;文档对象模型&#xff09;的定义 DOM&#xff08;Document Object Model&#xff09;是 W3C 制定的标准接口&#xff0c;允许程序或脚本&#xff08;如 JavaScript&#xff09;动态访问和更新 HTML/XML 文档的内容、结构和样式。…

用鼠标点击终端窗口的时候出现:0;61;50M0;61;50M0;62;50M0

在做aws webrtc viewer拉流压测的过程中&#xff0c;我本地打开了多个终端&#xff0c;用于连接EC2实例&#xff1a; 一个终端用于启动 ‘并发master脚本’、监控master端的cpu、mem&#xff1b;一个终端用于监控master端的带宽情况&#xff1b;一个终端用于监控viewer端的cpu、…

C++-linux 5.gdb调试工具

GDB调试工具 在C/C开发中&#xff0c;程序运行时的错误往往比编译错误更难定位。GDB&#xff08;GNU Debugger&#xff09;是Linux环境下最强大的程序调试工具&#xff0c;能够帮助开发者追踪程序执行流程、查看变量状态、定位内存错误等。本章将从基础到进阶&#xff0c;全面讲…

Update~Read PLC for Chart ~ Log By Shift To be... Alarm AI Machine Learning

上图~ 持续迭代 1、增加报警弹窗,具体到哪个值,双边规格具体是多少 2、实时显示当前值的统计特征,Max Min AVG ... import tkinter as tk from tkinter import simpledialog import time import threading import queue import logging from datetime import datet…

es的自定义词典和停用词

在 Elasticsearch 中&#xff0c;自定义词典是优化分词效果的核心手段&#xff0c;尤其适用于中文或专业领域的文本处理。以下是关于 ES 自定义词典的完整指南&#xff1a; 为什么需要自定义词典&#xff1f; 默认分词不足&#xff1a; ES 自带的分词器&#xff08;如 Standard…

微算法科技技术突破:用于前馈神经网络的量子算法技术助力神经网络变革

随着量子计算和机器学习的迅猛发展&#xff0c;企业界正逐步迈向融合这两大领域的新时代。在这一背景下&#xff0c;微算法科技&#xff08;NASDAQ:MLGO&#xff09;成功研发出一套用于前馈神经网络的量子算法&#xff0c;突破了传统神经网络在训练和评估中的性能瓶颈。这一创新…

一文读懂循环神经网络(RNN)—语言模型+读取长序列数据(2)

目录 读取长序列数据 为什么需要 “读取长序列数据”&#xff1f; 读取长序列数据的核心方法 1. 滑动窗口&#xff08;Sliding Window&#xff09; 2. 分段截取&#xff08;Segmentation&#xff09; 3. 滚动生成&#xff08;Rolling Generation&#xff09; 4. 关键信息…

Oracle Virtualbox 虚拟机配置静态IP

Oracle Virtualbox 虚拟机配置静态IP VirtualBox的网卡&#xff0c;默认都是第一个不能自定义&#xff0c;后续新建的可以自定义。 新建NAT网卡、host主机模式网卡 依次点击&#xff1a;管理->工具->网络管理器新建host主机模式网卡 这个网卡的网段自定义&#xff0c;创建…

Linux RAID1 创建与配置实战指南(mdadm)

Linux RAID1 创建与配置实战指南&#xff08;mdadm&#xff09;一、RAID1 核心价值与实战目标RAID1&#xff08;磁盘镜像&#xff09; 通过数据冗余提供高可靠性&#xff1a;当单块硬盘损坏时&#xff0c;数据不丢失支持快速阵列重建读写性能略低于单盘&#xff08;镜像写入开销…

MySQL数据库----函数

目录函数1&#xff0c;字符串函数2&#xff0c;数值函数3&#xff0c;日期函数4&#xff0c;流程函数函数 1&#xff0c;字符串函数 MySQL中内置了很多字符串函数 2&#xff0c;数值函数 3&#xff0c;日期函数 4&#xff0c;流程函数

1.2 vue2(组合式API)的语法结构以及外部暴露

vue2 vue3中可以写vue2的语法&#xff0c;vue2的结构像一个花盆里的根&#xff08;根组件App.vue&#xff09;&#xff0c;根上可以插上不同的枝杈和花朵&#xff08;组件&#xff09;。 组件的结构&#xff1a; // 这里写逻辑行为 <script lang"ts"> export d…

Swift 解 LeetCode 324:一步步实现摆动排序 II,掌握数组重排的节奏感

文章目录摘要描述题解答案题解代码&#xff08;Swift&#xff09;题解代码分析步骤一&#xff1a;排序数组步骤二&#xff1a;左右指针分段步骤三&#xff1a;按位置交错插入示例测试及结果示例 1示例 2示例 3&#xff08;边界情况&#xff09;时间复杂度分析空间复杂度分析总结…

使用SQLMAP的文章管理系统CMS的sql注入渗透测试

SQLMAP注入演示&#xff1a;抓包拿到Cookie:召唤sqlmap&#xff1a;sqlmap -u "http://192.168.1.99:8085/show.php?id34" --cookie "pma_langzh_CN; kbqug_admin_username2621-PL_LxhFjyVe43ZuQvht6MI5q0ZcpRVV5FI0pzQ6XR8; kbqug_siteid2621-PL_LxhFjyVe4yA5…

I3C通信协议核心详解

一、物理层与电气特性双线结构 SCL&#xff08;串行时钟线&#xff09;&#xff1a;主设备控制&#xff0c;支持 推挽&#xff08;Push-Pull&#xff09;输出&#xff08;高速模式&#xff09;和 开漏&#xff08;Open-Drain&#xff09;&#xff08;兼容I2C模式&#xff09;。…