目录

一、传统 Spring 的配置之 “痛”

1.1 XML 配置的 “臃肿”

1.2 注解配置的 “零散”

1.3 依赖适配的 “繁琐”

二、SpringBoot 自动装配的 “魔力”

2.1 一个极简的 SpringBoot 2.x 示例

三、SpringBoot 2.x 自动装配的实现原理

3.1 核心入口:@SpringBootApplication 注解

3.2 核心执行类:AutoConfigurationImportSelector 源码解析

第一步:核心入口方法 selectImports

第二步:核心逻辑方法 getAutoConfigurationEntry

第三步:加载候选配置类 getCandidateConfigurations

3.3 配置加载:SpringFactoriesLoader 与 spring.factories

3.4 高频疑问:spring.factories 文件是每个 jar 包都有吗?

3.5 条件筛选:@Conditional 系列注解的执行

3.6 最终执行:Bean 注册到 Spring 容器

四、SpringBoot 2.x 自动装配的灵活性:自定义配置优先

五、关键变化:SpringBoot 3.x 中 spring.factories 的替代方案

5.1 配置文件的替换

5.2 核心逻辑的兼容

六、总结

核心要点


在 Java 后端开发的历程中,Spring 框架的出现让面向对象编程与依赖注入思想深入人心,但早期的 Spring 配置方式却让开发者陷入了 “配置地狱”;而 SpringBoot 的横空出世,以 “约定大于配置” 的理念彻底改变了这一现状,其中自动装配更是核心中的核心。本文将从传统 Spring 的配置痛点出发,聚焦 SpringBoot 2.x 拆解自动装配的实现原理,补充 SpringBoot 3.x 的核心变化,带你完整理解这一 “黑科技” 背后的逻辑。

一、传统 Spring 的配置之 “痛”

在 SpringBoot 诞生之前,我们使用纯 Spring 框架开发项目时,想要让一个功能跑起来,需要编写大量的配置代码 / 文件,核心痛点集中在以下几个方面:

1.1 XML 配置的 “臃肿”

早期 Spring 以 XML 配置为主,哪怕是简单的数据源、事务管理器、Bean 注册,都需要在applicationContext.xml中逐一声明:

<!-- 配置数据源 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/test"/>
    <property name="username" value="root"/>
    <property name="password" value="123456"/>
</bean>

<!-- 配置JdbcTemplate -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="dataSource"/>
</bean>

<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

一个简单的数据库操作,就需要十几行 XML 配置;如果是复杂项目,XML 文件动辄数百行,维护成本极高。

1.2 注解配置的 “零散”

后来 Spring 引入了注解配置(如@Configuration@Bean),虽然摆脱了 XML,但仍需要开发者手动编写大量配置类:

@Configuration
public class DataSourceConfig {
    // 手动配置数据源
    @Bean
    public DruidDataSource dataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/test");
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        return dataSource;
    }

    // 手动配置JdbcTemplate
    @Bean
    public JdbcTemplate jdbcTemplate(DruidDataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }
}

每个功能模块都需要对应一个配置类,开发者需要关注 “如何配置”,而非 “如何实现业务”,违背了 “关注点分离” 的原则。

1.3 依赖适配的 “繁琐”

不同框架(如 MyBatis、Redis、MQ)集成到 Spring 时,需要手动适配:比如集成 MyBatis,要手动配置SqlSessionFactoryMapperScannerConfigurer;集成 Redis,要手动配置RedisTemplateJedisConnectionFactory。一旦依赖版本变化,配置代码还需要同步修改,极易出错

二、SpringBoot 自动装配的 “魔力”

SpringBoot 的核心目标就是 “简化配置”,而自动装配(AutoConfiguration)正是实现这一目标的关键。使用 SpringBoot 后,上述所有配置都可以 “消失”—— 只需引入对应的 starter 依赖,SpringBoot 就能自动完成所有配置,开发者只需专注业务代码。

2.1 一个极简的 SpringBoot 2.x 示例

数据库操作为例,SpringBoot 2.x 项目中只需两步:

1.引入 starter 依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
</dependency>

application.yml中配置基础属性:

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/test
    username: root
    password: 123456

无需编写任何配置类,DataSourceJdbcTemplate等 Bean 会自动注册到 Spring 容器中,直接注入即可使用:

@Service
public class UserService {
    @Autowired
    private JdbcTemplate jdbcTemplate; // 直接使用,无需手动配置
    
    // 业务代码...
}

这就是自动装配的魔力 ——SpringBoot 根据 “约定”(依赖、配置)自动完成 Bean 的注册和配置,让开发者从繁琐的配置中解放出来。

三、SpringBoot 2.x 自动装配的实现原理

自动装配并非 “玄学”,而是 SpringBoot 2.x 基于 Spring 原有机制的扩展,核心逻辑可拆解为 “触发入口→加载配置→条件筛选→Bean 注册” 四步。

3.1 核心入口:@SpringBootApplication 注解

@SpringBootApplication是自动装配的 “总开关”,它是一个组合注解,核心子注解是@EnableAutoConfiguration

@EnableAutoConfiguration的底层通过@Import(AutoConfigurationImportSelector.class)导入核心类 —— 这是自动装配的 “执行入口”,所有自动配置的逻辑都从这个类开始。

3.2 核心执行类:AutoConfigurationImportSelector 源码解析

AutoConfigurationImportSelector实现了DeferredImportSelector接口(保证自动配置类加载优先级低于用户自定义配置),其核心方法是selectImports,我们逐行拆解你提供的这段核心代码:

第一步:核心入口方法 selectImports
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
    // 1. 检查自动装配是否开启(可通过spring.boot.autoconfigure.enable=false关闭)
    if (!isEnabled(annotationMetadata)) {
        return NO_IMPORTS; // 返回空数组,不加载任何自动配置类
    }
    // 2. 核心逻辑:获取自动配置入口(加载、筛选配置类)
    AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
    // 3. 将筛选后的配置类列表转为字符串数组返回
    return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}

这段代码是自动装配的 “总调度”,真正的核心逻辑在getAutoConfigurationEntry方法中。

第二步:核心逻辑方法 getAutoConfigurationEntry
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    // 前置校验:确认自动装配开启且当前环境合法
    if (!isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    }
    // 1. 获取@EnableAutoConfiguration注解的属性(如exclude、excludeName)
    AnnotationAttributes attributes = getAttributes(annotationMetadata);
    // 2. 加载所有候选的自动配置类(从spring.factories中读取)
    List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    // 3. 去重:避免不同jar包的spring.factories中重复声明同一配置类
    configurations = removeDuplicates(configurations);
    // 4. 排除:移除用户通过exclude属性指定的配置类(自定义排除)
    Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    checkExcludedClasses(configurations, exclusions);
    configurations.removeAll(exclusions);
    // 5. 过滤:通过SpringFactoriesLoader加载的过滤器(如OnClassCondition)筛选配置类
    configurations = getConfigurationClassFilter().filter(configurations);
    // 6. 触发自动配置导入事件(供开发者监听)
    fireAutoConfigurationImportEvents(configurations, exclusions);
    // 7. 返回最终筛选后的配置类入口
    return new AutoConfigurationEntry(configurations, exclusions);
}
第三步:加载候选配置类 getCandidateConfigurations

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    // 核心:通过SpringFactoriesLoader加载META-INF/spring.factories中的配置类
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
        getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
    Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
                    + "are using a custom packaging, make sure that file is correct.");
    return configurations;
}

// 指定要加载的工厂类类型:EnableAutoConfiguration
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
    return EnableAutoConfiguration.class;
}

这一步是连接 AutoConfigurationImportSelector 和 spring.factories 的关键SpringFactoriesLoader.loadFactoryNames会扫描类路径下所有META-INF/spring.factories文件,读取org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的配置类列表。

示例一个,如下:

3.3 配置加载:SpringFactoriesLoader 与 spring.factories

SpringFactoriesLoader是 Spring 的工厂加载机制,核心逻辑是:

  1. 扫描类路径下所有META-INF/spring.factories文件(仅存在于需要自动装配的 jar 包中);
  2. 根据EnableAutoConfiguration.class作为 key,读取对应的配置类全限定名列表;
  3. 将这些配置类返回给AutoConfigurationImportSelector作为候选配置类

以 SpringBoot 2.x 的spring-boot-autoconfigure包中的spring.factories为例,核心内容如下(简化版):

# 自动配置类列表
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration

3.4 高频疑问:spring.factories 文件是每个 jar 包都有吗?

很多开发者会误以为spring.factories是所有 jar 包的 “标配”,我刚开始也以为每个jar包都有,还去这些依赖jar包的目录里翻,结果很多都没有

1.仅需自动装配的 jar 包才包含:该文件是 SpringBoot 自动装配的 “约定文件”,只有需要被 SpringBoot 自动识别并完成配置的 jar 包才会包含,主要分为两类:

  • SpringBoot 官方 starter 包:如spring-boot-starter-webspring-boot-starter-jdbcspring-boot-autoconfigure等;
  • 第三方适配 SpringBoot 的 starter 包:如mybatis-spring-boot-starterdruid-spring-boot-starter等。

比如mp的starter包:

2.普通 jar 包无需包含:纯工具类 jar(如commons-lang3fastjson)、未适配 SpringBoot 的原生框架 jar(如原生mybatisjedis),因为不需要 SpringBoot 自动装配,所以不会包含该文件。

比如mybatis的jar包依赖:

简单来说:spring.factories是 “自动装配的标识文件”,只有需要 SpringBoot 主动识别并配置的 jar 包,才会在META-INF/目录下添加这个文件。

3.5 条件筛选:@Conditional 系列注解的执行

getConfigurationClassFilter().filter(configurations)这一步会触发条件注解的校验,SpringBoot 2.x 通过OnClassConditionOnBeanCondition等实现类,校验每个候选配置类上的@Conditional系列注解:

条件注解 作用
@ConditionalOnClass 类路径下存在指定类时,才加载该配置类(如引入了 JDBC 依赖才加载数据源配置)
@ConditionalOnMissingBean 容器中不存在指定 Bean 时,才创建该 Bean(用户自定义 Bean 优先级更高)
@ConditionalOnProperty 根据配置文件中的属性值决定是否加载(如配置了 spring.datasource 才生效)
@ConditionalOnWebApplication 当前项目是 Web 项目时才加载(如 DispatcherServlet 的自动配置)

以 SpringBoot 2.x 的DataSourceAutoConfiguration为例,其核心逻辑如下:

代码注释:

@Configuration
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class }) // 存在DataSource类才加载
@ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory") // 不存在R2DBC连接工厂时生效
@EnableConfigurationProperties(DataSourceProperties.class) // 绑定application.yml中的数据源配置
public class DataSourceAutoConfiguration {
    // 自动配置数据源Bean的逻辑...
}

3.6 最终执行:Bean 注册到 Spring 容器

经过AutoConfigurationImportSelector筛选后的配置类,会被 Spring 的ConfigurationClassPostProcessor解析:

  1. 解析配置类上的@Configuration注解;
  2. 执行配置类中的@Bean方法;
  3. 将生成的 Bean(如DataSourceJdbcTemplate)注册到 Spring 容器中;
  4. 最终实现 “自动装配”。

这一步其实就是平时我们项目开发中定义配置类的那一步

四、SpringBoot 2.x 自动装配的灵活性:自定义配置优先

SpringBoot 2.x 的自动装配并非 “一刀切”,而是保留了极高的灵活性 ——用户自定义配置永远优先于自动配置

  1. 自定义配置类:如果手动编写了@Configuration配置类并注册了DataSource Bean,SpringBoot 会通过@ConditionalOnMissingBean跳过自动配置的数据源;
  2. 配置属性覆盖:通过application.yml/application.properties配置的属性(如spring.datasource.url)会覆盖自动配置的默认值;
  3. 排除自动配置:可通过@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)排除指定的自动配置类;
  4. 关闭自动装配:可通过配置spring.boot.autoconfigure.enable=false全局关闭自动装配。

五、关键变化:SpringBoot 3.x 中 spring.factories 的替代方案

SpringBoot 3.x 基于 Spring Framework 6.x 构建,在自动装配的核心实现上做了重要调整 ——废弃了 spring.factories 文件,改用 SPI 机制,但AutoConfigurationImportSelector的核心逻辑框架未变:

5.1 配置文件的替换

在 SpringBoot 3.x 中,不再读取META-INF/spring.factories文件,而是将自动配置类的全限定名直接写入META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件中,格式为纯文本(每行一个配置类):

org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration

这种方式更符合 Java 的 SPI 规范,也简化了配置文件的解析逻辑。

5.2 核心逻辑的兼容

虽然配置文件形式变了,但AutoConfigurationImportSelector的核心流程(校验→加载→去重→排除→过滤)并未改变:

  • 只是getCandidateConfigurations方法中,读取配置的逻辑从SpringFactoriesLoader改为直接读取AutoConfiguration.imports文件;
  • 条件注解(@ConditionalOnClass@ConditionalOnMissingBean等)的使用方式和优先级完全不变;
  • 用户自定义配置的优先级依然高于自动配置,保证了升级的兼容性。

六、总结

核心要点

  1. SpringBoot 2.x 自动装配完整流程@EnableAutoConfiguration导入AutoConfigurationImportSelectorselectImports触发核心逻辑 → getAutoConfigurationEntry加载spring.factories中的候选配置类 → 去重 / 排除 / 条件筛选 → 注册 Bean 到容器;
  2. spring.factories 的存在条件:仅存在于需要自动装配的 starter 包中,普通 jar 包无需包含;
  3. SpringBoot 3.x 关键变化:废弃spring.factories,改用AutoConfiguration.imports文件,AutoConfigurationImportSelector核心逻辑框架不变;
  4. 核心设计思想:约定大于配置,用户自定义配置优先级高于自动配置,兼顾便捷性和灵活性。

感兴趣的宝子可以关注一波,后续会更新更多有用的知识!!!

Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐