Spring Boot 自动配置原理

候选人小许在面试阿里 P6 时,面试官问道:

"Spring Boot 的自动配置是怎么实现的?"

小许说:"通过 @EnableAutoConfiguration..."

面试官追问:"具体是怎么找到并加载配置的?"

小许说:"好像是通过 spring.factories..."

面试官继续追问:"spring.factories 里面写了什么?为什么需要它?"

小许答不上来了。

【面试官心理】 这道题我用来测试候选人对 Spring Boot 核心原理的理解。自动配置是 Spring Boot 最核心的特性,也是区分"Spring Boot 使用者"和"Spring Boot 理解者"的关键。能说清楚 spring.factories 和 AutoConfigurationImportSelector 的,基本都研究过 Spring Boot 源码。


一、核心问题 🔴

1.1 问题拆解

第一层:概念

  • "Spring Boot 自动配置是什么?"
  • "自动配置和手动配置有什么区别?"

第二层:原理

  • "@EnableAutoConfiguration 是怎么工作的?"
  • "spring.factories 文件里写了什么?"
  • "AutoConfigurationImportSelector 做了什么?"

第三层:过程

  • "自动配置的加载顺序是什么?"
  • "@Conditional 是怎么控制配置生效的?"

第四层:定制

  • "怎么排除某些自动配置?"
  • "怎么自定义自动配置?"

1.2 ❌ 错误示范

候选人原话 A:"自动配置就是 Spring Boot 默认配置了很多 Bean,不用我们自己配了。"

问题诊断

  • 知道大概效果,但不理解原理
  • 说不清 spring.factories 的作用

候选人原话 B:"spring.factories 里面是配置类的路径。"

问题诊断

  • 知道一部分,但说错了
  • 实际上里面是 AutoConfiguration 类的全限定名

候选人原话 C:"自动配置不需要任何条件判断。"

问题诊断

  • 完全错误
  • @Conditional 系列注解就是用来控制条件的

1.3 标准回答

P5 回答:核心流程

Spring Boot 自动配置的核心流程:

自动配置流程:

1. @SpringBootApplication
   └─ @EnableAutoConfiguration
       └─ @Import(AutoConfigurationImportSelector.class)
           └─ selectImports()
               └─ SpringFactoriesLoader.loadFactoryNames()
                   └─ 扫描所有 spring.factories
                       └─ 加载自动配置类
                           └─ @Conditional 判断是否生效
                               └─ 注册 Bean

1.4 追问升级

追问 1:spring.factories 详解

# spring-boot-autoconfigure/META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration

spring.factories 的格式:

  • Key:org.springframework.boot.autoconfigure.EnableAutoConfiguration
  • Value:逗号分隔的 AutoConfiguration 类的全限定名

Spring Boot 2.7+ 的变化:

# Spring Boot 2.7+ 推荐使用 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
# spring.factories 仍然支持,但会显示弃用警告

# META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
com.example.autoconfigure.MyAutoConfiguration
com.example.autoconfigure.AnotherAutoConfiguration

追问 2:AutoConfigurationImportSelector

这是自动配置的核心类:

// AutoConfigurationImportSelector 实现了 DeferredImportSelector
public class AutoConfigurationImportSelector
        implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware {

    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        // 1. 获取所有自动配置类的名称
        List<String> configurations = getCandidateConfigurations(
            annotationMetadata, getAttributes(annotationMetadata));

        // 2. 去重
        configurations = removeDuplicates(configurations);

        // 3. 获取需要排除的配置
        Set<String> exclusions = getExclusions(annotationMetadata);
        configurations.removeAll(exclusions);

        // 4. 根据 @Conditional 判断是否生效
        configurations = filter(configurations, autoConfigurationMetadata);

        return configurations.toArray(new String[0]);
    }

    // 加载 spring.factories 中的配置
    protected List<String> getCandidateConfigurations(
            AnnotationMetadata metadata, AnnotationAttributes attributes) {
        // SpringFactoriesLoader 是关键
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
            getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
        return configurations;
    }
}

// SpringFactoriesLoader
public final class SpringFactoriesLoader {
    public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
        // 1. 扫描 classpath 下所有 spring.factories
        // 2. 根据 factoryType 读取对应的值
        // 3. 返回配置类名列表
    }
}

追问 3:@Conditional 控制生效条件

AutoConfiguration 类使用 @Conditional 系列注解控制是否生效:

// DataSourceAutoConfiguration 示例
@Configuration
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@ConditionalOnMissingBean(DataSource.class)
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ DataSourcePoolMetadataProvidersConfiguration.class })
public class DataSourceAutoConfiguration {

    @ConditionalOnClass(EnableTransactionManager.class)
    @ConditionalOnMissingBean(PlatformTransactionManager.class)
    public DataSourceTransactionManager transactionManager(
            DataSourceProperties properties) {
        return new DataSourceTransactionManager(dataSource);
    }
}
注解作用
@ConditionalOnClassclasspath 中存在指定类时才生效
@ConditionalOnMissingBean容器中不存在指定 Bean 时才生效
@ConditionalOnProperty配置属性满足条件时才生效
@ConditionalOnBean容器中存在指定 Bean 时才生效
@ConditionalOnMissingClassclasspath 中不存在指定类时才生效
@ConditionalOnExpressionSpEL 表达式为 true 时才生效
// @ConditionalOnProperty 示例
@Configuration
@ConditionalOnProperty(prefix = "spring.cache", name = "type", havingValue = "redis")
public class RedisCacheAutoConfiguration {
    // 只有配置了 spring.cache.type=redis 才生效
}

// @ConditionalOnMissingBean 示例
@Configuration
@ConditionalOnMissingBean(UserService.class)
public class DefaultUserServiceAutoConfiguration {
    // 只有容器中没有 UserService Bean 时才生效
    // 这就是为什么用户自定义 Bean 可以覆盖自动配置
}

追问 4:配置顺序和覆盖规则

自动配置类的加载顺序由 @AutoConfigureBefore@AutoConfigureAfter 控制:

@Configuration
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
@ConditionalOnClass({ EnableWebMvc.class })
public class WebMvcAutoConfiguration {
    // 在 DataSourceAutoConfiguration 之后加载
}

@Configuration
@AutoConfigureBefore(WebMvcAutoConfiguration.class)
public class WebMvcConfig {
    // 在 WebMvcAutoConfiguration 之前加载
}

覆盖规则(优先级从高到低):

  1. 用户定义的 @Bean(@Configuration 类中的方法)
  2. 用户定义的 @Component(@Service、@Controller 等)
  3. 自动配置类中的 @Bean
  4. 自动配置类的默认行为

二、延伸问题 🟡

2.1 Spring Boot 2.7 的变化

Spring Boot 2.7 引入了新的自动配置文件位置:

Spring Boot 2.7 之前:
META-INF/spring.factories
  └─ org.springframework.boot.autoconfigure.EnableAutoConfiguration=...

Spring Boot 2.7+:
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
  └─ 每行一个配置类路径

好处:

  • 更简单的格式
  • 支持按需加载(不需要一次性加载所有)
  • IDE 提示更友好

2.2 排除自动配置

// 方式1:@SpringBootApplication 排除
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })

// 方式2:配置文件排除
spring:
  autoconfigure:
    exclude:
      - org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration

// 方式3:@EnableAutoConfiguration 排除
@EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class })

2.3 自定义自动配置

// 1. 创建自动配置类
@Configuration
@ConditionalOnClass(MyService.class)
@EnableConfigurationProperties(MyServiceProperties.class)
@AutoConfigureAfter(HttpEncodingAutoConfiguration.class)
public class MyServiceAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public MyService myService(MyServiceProperties properties) {
        return new MyService(properties);
    }
}

// 2. 定义配置属性
@ConfigurationProperties(prefix = "my.service")
public class MyServiceProperties {
    private String url = "http://default";
    public String getUrl() { return url; }
    public void setUrl(String url) { this.url = url; }
}

// 3. 注册(spring.factories 或 .imports)
# META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
com.example.autoconfigure.MyServiceAutoConfiguration

三、生产避坑

3.1 自动配置冲突

// 常见冲突:两个自动配置都往容器注册了同类型的 Bean
@Configuration
public class ConfigA {
    @Bean
    public ObjectMapper objectMapper() {
        return new ObjectMapper();
    }
}

@Configuration
public class ConfigB {
    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        return mapper;
    }
}

// 解决:@ConditionalOnMissingBean
@Configuration
public class ConfigA {
    @Bean
    @ConditionalOnMissingBean(ObjectMapper.class)
    public ObjectMapper objectMapper() {
        return new ObjectMapper(); // 只有不存在时才注册
    }
}

3.2 条件判断不当导致的问题

// ❌ 错误:条件太严格
@ConditionalOnClass(JedisClient.class) // 依赖 JedisClient,可能不存在
public class RedisAutoConfiguration {
    // 如果 JedisClient 不在 classpath,整个配置不生效
}

// ✅ 正确:使用可选依赖
@ConditionalOnClass(name = "redis.clients.jedis.JedisClient")
public class RedisAutoConfiguration {
    // 类存在时才生效,不存在时静默跳过
}

四、工程选型

4.1 自动配置 vs 手动配置

对比自动配置手动配置
工作量
可控性
适合场景通用组件、框架集成业务定制、特定需求
调试难度较难较易

4.2 何时需要自定义自动配置

  1. 封装内部框架:将内部框架封装为 Starter
  2. 团队共享配置:统一团队的默认配置
  3. 第三方库集成:封装第三方库的配置

五、面试总结

Spring Boot 自动配置是 Spring Boot 最核心的特性。

P5 候选人能说出"通过 @EnableAutoConfiguration 实现"。 P6 候选人能说清 spring.factories 的作用、@Conditional 的使用。 P7 候选人能说出 AutoConfigurationImportSelector 的完整流程、Spring Boot 2.7 的变化。

记住,自动配置的核心是"约定大于配置"——Spring Boot 帮我们做了很多默认配置,但允许我们覆盖。