Spring源码 第七篇:Spring Boot 自动配置原理深度拆解

👋 前言

我们已经完整打通了 Spring 核心底层:IOC → Bean 生命周期 → 循环依赖 → AOP → 事务 → Spring MVC。

而 Spring Boot 的出现,让我们告别了复杂的 XML 配置,一个 @SpringBootApplication 注解就能启动 Web 应用。这背后,就是 自动配置(Auto-Configuration) 的魔力。

本篇基于 Spring Boot 2.7.x 版本进行拆解:

  • @SpringBootApplication 三大注解到底做了什么?
  • @EnableAutoConfiguration 是如何实现自动装配的?
  • spring.factories 与 SPI 机制的底层逻辑?
  • @Conditional 条件注解家族如何生效?
  • 自定义 Starter 的完整实现与最佳实践。

一、核心入口:@SpringBootApplication 注解拆解

java 复制代码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { 
    @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
    @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) 
})
public @interface SpringBootApplication {
    // ...
}

它本质是一个组合注解,核心由三部分组成:

  1. @SpringBootConfiguration :继承自 @Configuration,标记当前类为配置类
  2. @EnableAutoConfiguration:自动配置的核心开关
  3. @ComponentScan:默认扫描当前包及其子包下的所有组件

注意@SpringBootApplication 底层还隐式包含 @AutoConfigurationPackage,作用是注册启动类所在包为自动配置包,这是 "同包 / 子包组件能被扫描" 的关键底层原因。


二、灵魂核心:@EnableAutoConfiguration 工作原理

java 复制代码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    // ...
}

真正的魔法,来自它导入的 AutoConfigurationImportSelector

1. AutoConfigurationImportSelector 核心作用

它实现了 DeferredImportSelector 接口,会在所有 @Configuration 配置类处理完之后,再执行导入逻辑,负责:

  • 加载所有自动配置类
  • 收集候选配置,交由 Spring 容器做条件判断
  • 返回要导入的类全限定名,不直接注册配置类到容器,由 Spring 统一注册

核心入口方法是 selectImports

java 复制代码
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
        return NO_IMPORTS;
    }
    AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
    return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}

selectImports() 方法正常情况下会返回一个包含所有候选自动配置类全限定名的 String[] 数组。只有在自动配置被全局禁用(即配置 spring.boot.enableautoconfiguration=false)时,才会返回 NO_IMPORTS 常量。

2. 自动配置类的加载过程

关键方法 getAutoConfigurationEntry

java 复制代码
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    // 1. 获取候选的自动配置类列表
    List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    
    // 2. 去重、排除、过滤
    configurations = removeDuplicates(configurations);
    Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    checkExcludedClasses(configurations, exclusions);
    configurations.removeAll(exclusions);
    configurations = getConfigurationClassFilter().filter(configurations);
    
    // 3. 触发自动配置导入事件
    fireAutoConfigurationImportEvents(configurations, exclusions);
    
    return new AutoConfigurationEntry(configurations, exclusions);
}

三、自动配置类从哪来?SPI 机制与 spring.factories

1. 传统 SPI 机制回顾

Java SPI 允许服务提供者通过 META-INF/services/接口名 文件,声明接口的实现类,由 ServiceLoader 加载。Spring Boot 正是利用了类似的思想。

2. Spring Boot 自动配置的 SPI 文件

Spring Boot 2.7.x 之前 ,自动配置类的定义在 META-INF/spring.factories 文件中,例如 spring-boot-autoconfigure 模块中:

properties 复制代码
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
...

getCandidateConfigurations 方法就是通过 SpringFactoriesLoader.loadFactoryNames 来加载这些类名的。

3. Spring Boot 2.7.x+ 的新方式

从 2.7.x 开始,改为使用 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件,格式更简洁,加载性能更高,由 ImportCandidates 加载,不再使用 SpringFactoriesLoader


四、条件装配:@Conditional 注解家族

加载到的自动配置类,并不是都会生效,Spring Boot 会通过 @Conditional 系列注解进行条件判断,条件判断在配置类解析阶段执行,不在 ImportSelector 中过滤,只有满足条件的配置类才会被实例化。

常用条件注解

注解 作用
@ConditionalOnClass 类路径下存在指定类时生效
@ConditionalOnMissingClass 类路径下不存在指定类时生效
@ConditionalOnBean 容器中存在指定 Bean 时生效
@ConditionalOnMissingBean 容器中不存在指定 Bean 时生效
@ConditionalOnProperty 配置文件中存在指定属性且值匹配时生效
@ConditionalOnWebApplication 当前是 Web 应用时生效
@ConditionalOnNotWebApplication 当前不是 Web 应用时生效

注意 :这些注解的底层,都是 @Conditional,并配合对应的 Condition 实现类,例如 OnClassConditionOnBeanCondition 等,会在配置类解析阶段进行判断,决定是否加载该配置类。


五、案例拆解:WebMvcAutoConfiguration 自动配置

以最常用的 Web MVC 自动配置为例,看它是如何生效的:

java 复制代码
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ 
    DispatcherServletAutoConfiguration.class, 
    TaskExecutionAutoConfiguration.class,
    ValidationAutoConfiguration.class 
})
public class WebMvcAutoConfiguration {

    // 配置默认的视图解析器、消息转换器等
    @Configuration(proxyBeanMethods = false)
    @Import(EnableWebMvcConfiguration.class)
    @EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
    @Order(0)
    public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
        // ...
    }
}

可以看到,它满足以下条件才会生效:

  1. 当前是 Servlet Web 应用
  2. 类路径下存在 ServletDispatcherServlet
  3. 容器中没有 WebMvcConfigurationSupport 类型的 Bean

六、自定义 Starter:从零实现一个

1. 核心步骤

  1. 创建一个 Maven 项目
  2. 编写自动配置类 MyServiceAutoConfiguration
  3. 在配置类中使用 @Bean 注册自定义的服务 Bean
  4. 添加条件注解,确保配置类在合适的条件下生效
  5. META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 中声明你的配置类
  6. 打包发布,其他项目引入依赖即可自动装配

2. 示例代码

自动配置类

java 复制代码
@Configuration
@ConditionalOnClass(MyService.class)
@EnableConfigurationProperties(MyServiceProperties.class)
public class MyServiceAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public MyService myService(MyServiceProperties properties) {
        MyService service = new MyService();
        service.setPrefix(properties.getPrefix());
        return service;
    }
}

配置属性类

java 复制代码
@ConfigurationProperties(prefix = "my.service")
public class MyServiceProperties {
    private String prefix = "Hello";
    // getter/setter
}

声明自动配置

src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件中:

复制代码
com.example.mystarter.MyServiceAutoConfiguration

---## 七、Spring Boot 自动配置精简流程

流程图:

复制代码
┌─────────────────────┐
│ @SpringBootApplication │
└──────────┬──────────┘
           │
           ▼
┌─────────────────────┐
│ @EnableAutoConfiguration │
└──────────┬──────────┘
           │
           ▼
┌─────────────────────────────┐
│ AutoConfigurationImportSelector │
└──────────┬──────────────────┘
           │
           ▼
┌─────────────────────────────┐
│   加载候选自动配置类列表     │
└──────────┬──────────────────┘
           │
           ▼
┌─────────────────────────────┐
│   过滤、去重、排除配置类     │
└──────────┬──────────────────┘
           │
           ▼
┌─────────────────────────────┐
│   @Conditional 条件筛选      │
└──────────┬──────────────────┘
           │
           ▼
┌─────────────────────────────┐
│   符合条件的配置类生效       │
└──────────┬──────────────────┘
           │
           ▼
┌─────────────────────────────┐
│   注册到 Spring 容器         │
└──────────┬──────────────────┘
           │
           ▼
┌─────────────────────────────┐
│       完成自动装配          │
└─────────────────────────────┘

详细步骤说明:

  1. 启动入口@SpringBootApplication 注解触发 @EnableAutoConfiguration
  2. 选择器加载AutoConfigurationImportSelector 负责加载所有候选自动配置类
  3. 候选配置 :从 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件(Spring Boot 2.7.x+)或 spring.factories(旧版本)加载配置类列表
  4. 初步过滤:对配置类进行去重、排除用户指定的配置类
  5. 条件筛选 :根据 @Conditional 系列注解(如 @ConditionalOnClass@ConditionalOnBean 等)进行条件判断
  6. 配置生效:只有满足所有条件的配置类才会被实例化并注册到 Spring 容器
  7. 完成装配:所有生效的配置类创建相应的 Bean,完成自动配置过程

核心要点:

  • 自动配置是"按需加载"的,只有满足条件的配置类才会生效
  • 条件判断基于类路径、Bean存在性、配置文件属性等多种因素
  • 整个过程在应用启动时自动完成,无需手动配置

八、高频面试题

1. Spring Boot 自动配置的原理是什么?

:基于 @EnableAutoConfiguration,通过 AutoConfigurationImportSelector 加载所有自动配置类,再通过 @Conditional 条件注解进行筛选,将符合条件的配置类注册到容器中。

2. spring.factories 是做什么的?

:它是 Spring Boot 早期实现 SPI 扩展的文件,用于声明所有自动配置类的全限定名,由 SpringFactoriesLoader 加载。在 2.7.x 版本及之前,它是 Spring Boot SPI 机制的核心,用于声明自动配置类。但在 3.0 版本之后,它已被 AutoConfiguration.imports 文件取代。

3. 如何自定义一个 Spring Boot Starter?

:编写自动配置类、配置属性类,声明在 AutoConfiguration.imports 文件中,打包后引入依赖即可自动装配。

4. @Conditional 系列注解的作用?

:条件装配,只有满足特定条件时,对应的配置类或 Bean 才会被创建,是自动配置实现按需加载的核心。


📚 总结

Spring Boot 自动配置通过 @EnableAutoConfigurationAutoConfigurationImportSelector 实现了"约定大于配置"的理念。它结合了 Java SPI 机制和条件注解,让开发者能够快速搭建项目,同时保留了足够的灵活性进行自定义扩展。

理解自动配置原理,不仅能帮助我们更好地使用 Spring Boot,还能让我们在遇到问题时快速定位,并能够根据业务需求定制自己的 Starter,提升开发效率。

相关推荐
日取其半万世不竭1 小时前
给 Docker 容器设置 CPU 和内存限制,避免单个服务拖垮整机
java·docker·容器
铁皮哥1 小时前
【agent 开发】Claude Code 的 Skill 是怎么被加载的?从 name/description 到 SKILL.md 再到资源文件
java·服务器·数据库·python·gitee·github·软件工程
白宇横流学长1 小时前
基于SpringBoot实现的校园失物招领平台设计与实现【源码+文档】
java·spring boot·后端
罗超驿1 小时前
6.Java多线程详解:Thread类、线程属性与start()方法深度解析
java·开发语言·面试·java-ee
苦逼的猿宝2 小时前
IT技术交流和分享平台的设计与实现(源码+论文)
java·毕业设计·springboot·计算机毕业设计
摇滚侠2 小时前
IDEA 需要修改的配置 开发工具
java·ide·intellij-idea
2601_957786772 小时前
企业矩阵运营的“三段论“:管号、产内容、获线索——全链路系统的价值拆解
java·前端·矩阵·多平台管理
Run_Teenage2 小时前
算法模板:输入输出,并查集
java·开发语言·算法
一 乐2 小时前
公交线路查询系统|基于Java+vue公交线路查询系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·论文·毕设·公交线路查询系统