SpringBoot启动过程与自动配置过程解读

 更新时间:2025年08月25日 14:28:24   作者:没事学AI  
本文详解SpringBoot启动流程及自动配置机制,涵盖main方法初始化、环境配置、容器启动等关键步骤,解析条件注解与spring.factories的智能配置逻辑,并提供自定义配置方法,强调其"约定优于配置"的核心理念与工程化封装特性

一、Spring Boot 启动流程

Spring Boot 应用的入口是 main 方法中的 SpringApplication.run(),但这行代码背后藏着一整套“初始化-配置-启动”的流水线。

我们可以将其分为构造阶段运行阶段两大步,每一步都有明确的职责边界。

1.1 构造阶段:初始化核心组件

SpringApplication 实例的创建是启动的第一步,这个阶段的核心是“搭骨架”——确定应用类型、加载初始化器和监听器,为后续运行铺路。

  • 验证主类合法性:通过 Assert.notNull 确保传入的主配置类(标注 @SpringBootApplication 的类)非空,这是整个应用的“根”。
  • 推断应用类型:通过 WebApplicationType.deduceFromClasspath() 检查类路径中是否存在 DispatcherServlet(MVC)或 DispatcherHandler(WebFlux),自动判断是 SERVLETREACTIVE 还是非 Web 应用(NONE)。
  • 加载初始化器与监听器:从 META-INF/spring.factories 中读取 ApplicationContextInitializerApplicationListener 的实现类。这些组件是 Spring Boot 的“扩展点”,例如初始化器可用于预先配置上下文,监听器可监听启动各阶段事件。
  • 定位主程序类:通过栈追踪找到调用 main 方法的类,作为应用的入口标记。

关键源码片段

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 = WebApplicationType.deduceFromClasspath();
    this.setInitializers(getSpringFactoriesInstances(ApplicationContextInitializer.class));
    this.setListeners(getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = deduceMainApplicationClass();
}

1.2 运行阶段:从环境准备到容器启动

SpringApplication.run() 方法是真正的“执行引擎”,可细分为 10 个关键步骤,环环相扣:

  1. 启动监控与监听器:通过 StopWatch 记录启动时间,同时触发 SpringApplicationRunListeners.starting(),通知所有监听器“应用开始启动”。
  2. 环境配置:创建 ConfigurableEnvironment(根据应用类型选择 StandardServletEnvironmentStandardReactiveWebEnvironment),并加载系统变量、配置文件(如 application.yml)等。完成后调用 environmentPrepared 事件,允许监听器对环境进行二次调整。
  3. 打印 Banner:默认打印 Spring Boot 版本信息,可通过 resources/banner.txt 自定义(支持 ASCII 艺术或变量替换)。
  4. 创建应用上下文:根据应用类型生成对应的 IOC 容器,例如 Servlet 应用使用 AnnotationConfigServletWebServerApplicationContext
  5. 上下文准备:将环境、命令行参数等注入上下文,执行初始化器的 initialize 方法,并触发 contextPrepared 事件。
  6. 上下文刷新:调用 refreshContext,触发 Spring 容器的核心流程(Bean 定义加载、实例化、依赖注入等),同时启动嵌入式服务器(如 Tomcat)。
  7. 收尾工作:执行 afterRefresh(预留扩展点),停止计时器,并触发 started 事件。
  8. 回调自定义逻辑:从容器中获取 ApplicationRunnerCommandLineRunner 的实现类,按顺序执行其 run 方法(适合启动后初始化数据等操作)。
  9. 启动完成:触发 running 事件,应用正式就绪。
  10. 异常处理:若启动失败,调用 failed 事件,打印异常并关闭上下文。

实战技巧:若需在启动后执行初始化逻辑(如加载缓存),优先使用 ApplicationRunner(支持 ApplicationArguments 解析)而非 CommandLineRunner(直接接收字符串数组)。

二、自动配置:Spring Boot 的“智能配置”核心

Spring Boot 最令人称道的“自动配置”,本质是“基于条件的约定配置”。

它通过一套规则判断环境中存在的依赖,自动装配对应的组件,避免手动编写 XML 或 @Bean 配置。

2.1 自动配置的底层原理

自动配置的触发点是 @EnableAutoConfiguration 注解(被 @SpringBootApplication 间接包含),其核心逻辑在 AutoConfigurationImportSelector 中:

  1. 加载候选配置类:通过 SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class, classLoader),扫描所有 Jar 包的 META-INF/spring.factories,获取 EnableAutoConfiguration 对应的配置类列表(如 HttpEncodingAutoConfigurationDataSourceAutoConfiguration 等)。
  2. 过滤与去重:移除重复或被排除的配置类(通过 @SpringBootApplication(exclude = ...) 指定),再通过条件注解(如 @ConditionalOnClass)判断配置类是否生效。
  3. 注入容器:将生效的配置类加载到容器中,其内部的 @Bean 方法会自动生成组件。

关键源码片段

// AutoConfigurationImportSelector 中加载候选配置类的逻辑
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
        getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
    Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories");
    return configurations;
}

2.2 条件注解:自动配置的“开关”

自动配置的“智能”依赖于 @Conditional 系列注解,它们决定了一个配置类或 @Bean 方法是否生效。

常用条件注解如下:

注解作用示例场景
@ConditionalOnClass类路径存在指定类时生效检测到 DataSource.class 时配置数据源
@ConditionalOnMissingBean容器中不存在指定 Bean 时生效若用户未定义 RestTemplate,则自动配置默认实例
@ConditionalOnProperty配置项满足条件时生效通过 spring.datasource.enabled=true 控制数据源是否启用
@ConditionalOnWebApplication仅 Web 应用生效配置 DispatcherServlet

实战案例:

HttpEncodingAutoConfiguration(HTTP 编码自动配置)的条件判断:

@Configuration
@EnableConfigurationProperties(HttpProperties.class) // 绑定配置文件属性
@ConditionalOnWebApplication(type = Type.SERVLET) // 仅 Servlet 应用生效
@ConditionalOnClass(CharacterEncodingFilter.class) // 存在字符编码过滤器类时生效
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {
    @Bean
    @ConditionalOnMissingBean // 若用户未定义,则自动配置
    public CharacterEncodingFilter characterEncodingFilter() {
        // 从 HttpProperties 中读取配置(如 spring.http.encoding.charset=utf-8)
        CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
        filter.setEncoding(properties.getCharset().name());
        return filter;
    }
}

技巧

若需禁用某个自动配置(如默认的数据源配置),可通过

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class) 

或配置

spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration

2.3 自定义自动配置:扩展 Spring Boot

若需为自己的组件实现自动配置(如公司内部 SDK),需遵循以下步骤:

编写配置类:使用 @Configuration 和条件注解定义组件装配逻辑,通过 @EnableConfigurationProperties 绑定配置属性。

注册配置类:在 Jar 包的 META-INF/spring.factories 中添加:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.sdk.MyComponentAutoConfiguration

添加配置元数据:在 META-INF/spring-configuration-metadata.json 中定义配置项的描述(IDE 会自动提示),例如:

{
  "properties": [
    {
      "name": "example.sdk.enabled",
      "type": "java.lang.Boolean",
      "defaultValue": true,
      "description": "是否启用 SDK 组件"
    }
  ]
}

验证方法:启动应用时添加 --debug 参数,控制台会打印自动配置报告,显示哪些配置类生效(Positive matches)或不生效(Negative matches)。

三、总结:Spring Boot 便捷性的本质

Spring Boot 的启动流程和自动配置,本质是“约定优于配置”的极致体现:

  • 启动流程通过标准化的步骤,将 Spring 容器的初始化与嵌入式服务器的启动无缝衔接,减少手动干预。
  • 自动配置基于条件注解和 spring.factories 机制,实现了“依赖驱动配置”,让开发者专注于业务逻辑而非框架整合。

理解这些原理后,不仅能更高效地排查问题(如启动慢、自动配置失效),还能灵活扩展 Spring Boot(如自定义 starters 或启动流程)。

记住:Spring Boot 不是“魔法”,而是对 Spring 框架的“工程化封装”——看透其源码,就能真正掌控它。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • java实现给出分数数组得到对应名次数组的方法

    java实现给出分数数组得到对应名次数组的方法

    这篇文章主要介绍了java实现给出分数数组得到对应名次数组的方法,涉及java针对数组的遍历、排序及运算的相关技巧,需要的朋友可以参考下
    2015-07-07
  • java实现文件加密解密多种方法

    java实现文件加密解密多种方法

    这篇文章主要给大家介绍了关于java实现文件加密解密多种方法的相关资料, Java支持多种加密解密算法,常用的有DES、AES、RSA等,需要的朋友可以参考下
    2023-07-07
  • eclipse实现ElGamal数字签名

    eclipse实现ElGamal数字签名

    这篇文章主要为大家详细介绍了eclipse实现ElGamal数字签名,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-06-06
  • Java中的Stream流与IO流完整实战指南(从零掌握)

    Java中的Stream流与IO流完整实战指南(从零掌握)

    本文详细介绍了Java中的Stream流和IO流的使用方法,包括它们的基本概念、核心API、适用场景以及如何正确关闭资源,通过多个实战案例,帮助读者掌握这些核心技能,提高Java开发效率,感兴趣的朋友跟随小编一起看看吧
    2025-11-11
  • 浅析Spring的JdbcTemplate方法

    浅析Spring的JdbcTemplate方法

    本篇浅析Spring的JdbcTemplate方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-01-01
  • 关于java关键字this和super的区别和理解

    关于java关键字this和super的区别和理解

    这篇文章主要给大家介绍了关于java关键字this和super的区别和理解的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-01-01
  • Java设计模式之抽象工厂模式

    Java设计模式之抽象工厂模式

    这篇文章主要为大家详细介绍了Java设计模式之抽象工厂模式的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-03-03
  • logback EvaluatorFilter日志过滤器源码解读

    logback EvaluatorFilter日志过滤器源码解读

    这篇文章主要为大家介绍了logback EvaluatorFilter日志过滤器源码解读,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-11-11
  • 修改Zookeeper的客户端连接端口方式(默认2181端口)

    修改Zookeeper的客户端连接端口方式(默认2181端口)

    本文介绍了Zookeeper配置文件zoo.cfg的基本配置和修改客户端连接端口的方法,特别强调了server.x配置项中三个端口的含义,并给出修改后的zoo.cfg文件作为参考
    2026-04-04
  • Jdk中没有jre文件夹怎么办?如何解决?

    Jdk中没有jre文件夹怎么办?如何解决?

    这篇文章主要介绍了Jdk中没有jre文件夹怎么办?如何解决的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-02-02

最新评论