一文彻底弄懂Spring Boot的启动过程

一,Spring Boot启动过程

1. 启动入口

Spring Boot 应用的启动入口通常是一个包含 @SpringBootApplication 注解的主类,并调用 SpringApplication.run() 方法。@SpringBootApplication 是一个复合注解,包含了 @Configuration@EnableAutoConfiguration@ComponentScan,从而开启了自动配置和组件扫描。

源码路径在 SpringApplication 类的 run() 方法:

java 复制代码
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
    return new SpringApplication(primarySource).run(args);
}

这个入口主要做了以下几件事情:

  1. 创建 SpringApplication 实例:初始化 Spring Boot 环境。
  2. 初始化环境和监听器 :设置启动的 Environment,并且添加 ApplicationListener 监听器。
  3. 准备和刷新 Spring 上下文 :通过 prepareContextrefreshContext 方法进行上下文环境的准备和刷新。

2. 创建 SpringApplication 实例

SpringApplication 的构造方法中,Spring Boot 解析应用的启动模式(例如是 Web 应用、Servlet 应用或是普通应用),并初始化应用的上下文类型。Spring Boot 的不同上下文类型包括 AnnotationConfigApplicationContext(非 Web 应用)和 AnnotationConfigServletWebServerApplicationContext(Web 应用)。

3. 初始化 Environment 和监听器

接下来,Spring Boot 会初始化 ConfigurableEnvironment,这个环境中包含了系统的属性、环境变量、配置文件等数据,作为后续加载 Bean 定义和初始化的基础。

同时,Spring Boot 也会初始化一系列的 ApplicationListener,用于监听和处理应用启动过程中的事件,比如 ApplicationEnvironmentPreparedEventApplicationPreparedEvent 等。

4. 加载配置类并触发自动配置

Spring Boot 使用 @EnableAutoConfiguration 注解触发自动配置,核心实现是在 AutoConfigurationImportSelector 中加载 META-INF/spring.factories 配置文件,文件中列出了许多自动配置类(如 DataSourceAutoConfigurationJpaRepositoriesAutoConfiguration 等),根据条件(例如某些 Bean 是否存在、某些属性是否被配置等)加载相应的自动配置。

5. 加载并注册 Bean

refreshContext() 方法中,Spring Boot 调用 refresh() 方法,这一步骤中完成了 BeanFactory 的初始化和 BeanPostProcessor 的注册,并解析 @Component@Service@Repository 等注解标注的 Bean 定义,将它们注册到 BeanFactory 中。

在源码层面,refresh() 方法中,invokeBeanFactoryPostProcessorsregisterBeanPostProcessors 这两个方法是关键,分别用于执行所有 BeanFactoryPostProcessorBeanPostProcessor,确保 Bean 的生命周期正确管理。

6. Web 环境中的嵌入式容器启动

在 Web 应用中,Spring Boot 会启动嵌入式 Web 容器(如 Tomcat 或 Jetty)。Spring Boot 默认通过 ServletWebServerApplicationContext 启动内嵌的 Web 服务器。在 refresh() 的最后,会启动嵌入式容器,将应用作为 Web 应用发布。

7. 执行 ApplicationRunner 和 CommandLineRunner

Spring Boot 启动完成后,会扫描并执行所有实现了 ApplicationRunnerCommandLineRunner 接口的 Bean。它们可以用于在启动后执行自定义逻辑。

8. 发布应用启动完成事件

最后,Spring Boot 发布 ApplicationReadyEvent 事件,通知所有监听器应用已启动完成。至此,Spring Boot 应用正式启动完成,可以接收 HTTP 请求或执行其他任务。

二、Spring Boot 启动过程的架构设计

在 Spring Boot 应用启动的过程中,SpringApplication.run() 是最常用的启动方式。通过这个方法,Spring Boot 为开发者屏蔽了大量复杂的初始化细节,我们只需提供主启动类的入口和简单的配置信息即可启动整个应用。

下面我们从源码入手,分步骤分析 SpringApplication.run 进行的操作。

1,SpringApplication.run() 的详细流程

SpringApplication.run 主要完成以下几大步骤:

  1. 初始化 SpringApplication 实例

    该实例负责整个 Spring Boot 应用的启动过程,通过判断应用类型和设置环境变量为后续配置加载和应用上下文创建提供基础。核心方法为 SpringApplication#prepareEnvironmentSpringApplication#createApplicationContext

  2. 创建应用上下文并刷新上下文

    SpringApplication 将根据应用类型来创建不同的 ApplicationContext(如 AnnotationConfigApplicationContextServletWebServerApplicationContext),并将所有 Bean 装载到上下文中。

  3. 加载环境配置

    Spring Boot 会基于开发环境或生产环境加载不同的配置文件。核心是 ConfigFileApplicationListener 监听配置事件,解析应用配置文件(application.propertiesapplication.yml)并装配到应用上下文的 Environment 对象中。

  4. 启动嵌入式容器

    如果是 Web 应用,Spring Boot 会启动内嵌的服务器(如 Tomcat、Jetty 或 Undertow),并将 DispatcherServlet 注册到服务器中。

2,SpringApplication 核心设计类

在源码层面,SpringApplication 是启动过程中的关键类。它通过构造函数和 run() 方法完成了以下工作:

java 复制代码
public class SpringApplication {

    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        this.webApplicationType = deduceWebApplicationType();
        this.setInitializers((Collection) getSpringFactoriesInstances(
            ApplicationContextInitializer.class));
        this.setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        this.mainApplicationClass = deduceMainApplicationClass();
    }

    public ConfigurableApplicationContext run(String... args) {
        // 初始化阶段
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        configureHeadlessProperty();
        SpringApplicationRunListeners listeners = getRunListeners(args);
        listeners.starting();
        
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
            context = createApplicationContext();
            prepareContext(context, environment, listeners, applicationArguments);
            refreshContext(context);
            afterRefresh(context, applicationArguments);
            listeners.started(context);
            callRunners(context, applicationArguments);
        } catch (Exception ex) {
            handleRunFailure(context, listeners, ex);
            throw new IllegalStateException(ex);
        } finally {
            stopWatch.stop();
        }
        return context;
    }
}

源码揭示了 SpringApplication 的核心功能在于配置监听器、加载环境、创建上下文等几个方面。通过 deduceWebApplicationType() 方法判断 Web 应用类型,为接下来的 ApplicationContext 选择提供了依据。这种设计实现了启动流程的高度定制和灵活适应性。


三、启动过程中的关键组件及设计模式

Spring Boot 的启动流程涉及到多个设计模式,这些模式不仅提升了代码的可读性和灵活性,也确保了在不同的业务场景下能够快速地调整启动行为。

1,ApplicationContext 和 ConfigurableEnvironment

ApplicationContext 是 Spring 框架的核心概念之一,提供了 IoC 容器的实现和应用上下文的管理。在 Spring Boot 中,ApplicationContext 包含了所有的 Bean,并提供了应用与外部环境的接口,如 ConfigurableEnvironment,用于管理应用的环境配置和属性。

ApplicationContext 的核心设计

ApplicationContext 是一个高度抽象的接口,它有多个具体实现,如 AnnotationConfigApplicationContextServletWebServerApplicationContext。Spring Boot 的 SpringApplication 会根据应用的类型选择合适的 ApplicationContext 实现,并对其进行配置和初始化:

  • 非 Web 应用:使用 AnnotationConfigApplicationContext 加载配置类和 Bean。
  • Web 应用:使用 ServletWebServerApplicationContext,并加载嵌入式 Web 容器配置。

这种设计模式提升了 ApplicationContext 的适配性,使其可以灵活适应各种不同类型的应用。

ConfigurableEnvironment 环境配置的加载

ConfigurableEnvironment 提供了应用的环境信息,包括系统属性、环境变量以及外部化的配置文件内容。在 Spring Boot 启动过程中,ConfigurableEnvironment 会在 prepareEnvironment() 方法中被初始化。

ConfigurableEnvironment 中,属性源的优先级管理极大地提升了配置的灵活性。Spring Boot 根据不同的 PropertySource(如 application.properties、环境变量、命令行参数等),提供优先级管理,这样使得在复杂环境下也能灵活覆盖配置。

2,ApplicationListener 事件驱动模型

ApplicationListener 是 Spring 框架中事件驱动机制的实现,Spring Boot 使用该机制管理启动过程中各类重要事件。以下是常见的启动事件:

  • ApplicationStartingEvent:在 SpringApplication.run() 开始时触发。
  • ApplicationEnvironmentPreparedEvent:在 Environment 准备好之后触发。
  • ApplicationPreparedEvent:在上下文加载完成之前触发。
  • ApplicationStartedEvent:在上下文刷新完成之后触发。
  • ApplicationReadyEvent:应用启动完成时触发。

事件驱动模型允许开发者在应用启动的各个阶段进行自定义操作,极大增强了扩展性和灵活性。

3,依赖注入和条件配置加载机制

Spring Boot 中自动配置依赖于条件加载机制,核心在于 @Conditional 系列注解,比如 @ConditionalOnClass@ConditionalOnMissingBean 等。这些注解允许 Spring Boot 根据实际情况有选择性地加载配置:

java 复制代码
@Configuration
@ConditionalOnClass(DataSource.class)
public class DataSourceAutoConfiguration {
    // 数据源自动配置逻辑
}

这种条件注入模式通过按需加载 Bean,有效减少了资源的浪费。此设计模式也让 Spring Boot 能够快速适应不同的环境和依赖场景,避免不必要的 Bean 被加载。


四、自动配置的实现机制

Spring Boot 通过 @EnableAutoConfiguration 启动自动配置功能,它的底层实现机制是 AutoConfigurationImportSelectorSpringFactoriesLoader,其中 SpringFactoriesLoader 会扫描 META-INF/spring.factories 文件加载自动配置类。

1,SpringFactoriesLoader 详解

Spring Boot 自动配置的一个核心机制是 SpringFactoriesLoader,它会从 META-INF/spring.factories 文件中加载所有需要自动配置的类。这种文件配置的方式让 Spring Boot 能够轻松拓展新的自动配置功能。

@EnableAutoConfiguration 通过 AutoConfigurationImportSelector 来加载自动配置类,自动配置的实现类和逻辑则由 @Conditional 注解管理,这样使得自动配置具有按需加载的特性。

2,条件注解与场景应用

自动配置机制在实际业务中应用非常广泛。举例来说,Spring Boot 中数据源的配置是通过 DataSourceAutoConfiguration 类实现的。在 DataSourceAutoConfiguration 中,使用 @ConditionalOnClass 注解来判断类路径中是否存在 DataSource 类,若存在,则注入数据源配置。

这类场景非常适合用在多数据源配置上,开发者可以利用条件注解在生产环境下配置多个数据库源,而在测试环境中只加载测试数据源配置。


五、嵌入式容器的启动流程

Spring Boot 在 Web 应用中默认使用嵌入式容器,这样可以使应用独立于外部服务器而运行,提升了应用的独立性和便捷性。

1,嵌入式容器的启动设计

在 Spring Boot 启动 Web 应用时,会根据 ServletWebServerApplicationContext 加载嵌入式容器。以下是启动嵌入式容器的关键流程:

  1. 创建 ServletWebServerApplicationContext :Spring Boot 会选择 Web 应用的上下文类 ServletWebServerApplicationContext,并加载 WebServerFactory 工厂类。
  2. 创建 Web 服务器:Spring Boot 使用工厂类创建内嵌的 Web 服务器,如 Tomcat、Jetty 或 Undertow。
  3. 注册 DispatcherServlet :Spring Boot 将 DispatcherServlet 注册到内嵌服务器中,并通过 ServletRegistrationBean 对其进行初始化和配置。

这种设计模式使得 Spring Boot 应用无缝支持不同类型的 Web 容器,并根据环境灵活选择合适的容器。

2,嵌入式容器在多环境中的应用

嵌入式容器尤其适合微服务架构,它能在容器化场景中快速适应 Docker 或 Kubernetes 等部署平台。此外,Spring Boot 允许开发者通过简单配置切换不同类型的 Web 服务器(如 Tomcat 到 Jetty),这样的设计为企业级应用提供了高灵活性。