Skip to content

Spring Boot 自动配置原理

一、自动配置核心注解

@EnableAutoConfiguration

java
@AutoConfigurationPackage  // 自动配置包
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    Class<?>[] exclude() default {};
    String[] excludeName() default {};
}

@AutoConfiguration

java
// 标记这是一个自动配置类
@AutoConfiguration
// 条件:类路径有 DataSource
@ConditionalOnClass(DataSource.class)
// 条件:没有手动配置 DataSource
@ConditionalOnMissingBean(DataSource.class)
public class DataSourceAutoConfiguration {
    
    @Bean
    public DataSource dataSource() {
        // 自动配置数据源
        return new HikariDataSource();
    }
}

二、自动配置流程

┌─────────────────────────────────────────────────────────────────┐
│                    自动配置完整流程                                │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  SpringApplication.run()                                         │
│        │                                                         │
│        ▼                                                         │
│  prepareContext()                                                │
│        │                                                         │
│        ▼                                                         │
│  load邦元 (Bean Definitions)                                      │
│        │                                                         │
│        ├─ 主类 (@SpringBootApplication)                         │
│        └─ @Import(AutoConfigurationImportSelector)               │
│                                                                  │
│        ▼                                                         │
│  refreshContext() → invokeBeanFactoryProcessors()                │
│        │                                                         │
│        ▼                                                         │
│  AutoConfigurationImportSelector.selectImports()                 │
│        │                                                         │
│        ├─ 1. 获取 spring.factories 中的配置类                    │
│        │   META-INF/spring.factories                             │
│        │   EnableAutoConfiguration = xxxAutoConfiguration         │
│        │                                                         │
│        ├─ 2. 过滤排除                                            │
│        │   @ConditionalOnClass                                  │
│        │   @ConditionalOnMissingBean                            │
│        │   @ConditionalOnProperty                                │
│        │                                                         │
│        └─ 3. 返回需要导入的配置类列表                              │
│                                                                  │
│        ▼                                                         │
│  ConfigurationClassPostProcessor 处理配置类                       │
│        │                                                         │
│        ├─ @Bean 注册为 Bean                                      │
│        ├─ @Import 导入其他配置类                                  │
│        └─ @ComponentScan 扫描组件                                │
│                                                                  │
│        ▼                                                         │
│  配置类中的 @Bean 被注册                                          │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

三、@Conditional 条件装配

常用条件注解

注解条件
@ConditionalOnClass类路径存在某类时生效
@ConditionalOnMissingClass类路径不存在某类时生效
@ConditionalOnBean容器中存在某 Bean 时生效
@ConditionalOnMissingBean容器中不存在某 Bean 时生效
@ConditionalOnProperty配置属性满足条件时生效
@ConditionalOnResource存在某资源时生效
@ConditionalOnWebApplication是 Web 应用时生效
@ConditionalOnNotWebApplication非 Web 应用时生效

@ConditionalOnClass

java
@Configuration
@AutoConfiguration
// 只有类路径存在 RedisOperations 时才加载
@ConditionalOnClass(RedisOperations.class)
public class RedisAutoConfiguration {
    
    @Bean
    @ConditionalOnMissingBean
    public RedisTemplate<String, Object> redisTemplate() {
        return new RedisTemplate<>();
    }
}

@ConditionalOnMissingBean

java
@Configuration
@AutoConfiguration
public class DataSourceAutoConfiguration {
    
    @Bean
    // 如果用户没有手动定义 DataSource,才使用自动配置
    @ConditionalOnMissingBean(DataSource.class)
    public DataSource dataSource() {
        return new HikariDataSource();
    }
}

// 用户可以覆盖
@Configuration
public class MyConfig {
    @Bean
    public DataSource dataSource() {  // 优先级更高
        return new DruidDataSource();
    }
}

@ConditionalOnProperty

java
@Configuration
@AutoConfiguration
public class CacheAutoConfiguration {
    
    @Bean
    // 配置 spring.cache.type=redis 时生效
    @ConditionalOnProperty(
        name = "spring.cache.type",
        havingValue = "redis",
        matchIfMissing = false  // 默认为 false
    )
    public RedisCacheManager cacheManager() {
        return new RedisCacheManager();
    }
}

# application.yml
spring:
  cache:
    type: redis

条件组合

java
@Configuration
@AutoConfiguration
// 多个条件同时满足
@ConditionalOnClass({RedisOperations.class, CacheManager.class})
@ConditionalOnBean(CacheManager.class)
@ConditionalOnProperty(
    prefix = "spring.cache",
    name = "type",
    havingValue = "redis"
)
public class RedisCacheAutoConfiguration {
    // ...
}

自定义条件

java
// 1. 实现 Condition 接口
public class MyCondition implements Condition {
    
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // 自定义条件逻辑
        return context.getEnvironment().containsProperty("my.feature.enabled");
    }
}

// 2. 使用
@Bean
@Conditional(MyCondition.class)
public MyBean myBean() {
    return new MyBean();
}

// 3. 自定义条件注解
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Conditional(MyCondition.class)
public @interface ConditionalOnMyFeature { }

四、spring.factories 机制

文件位置与格式

META-INF/spring.factories
properties
# 格式:接口=实现类(多个用逗号分隔)

# 自动配置类
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.autoconfig.MyAutoConfiguration,\
com.example.autoconfig.OtherAutoConfiguration

# 失败分析器
org.springframework.boot.autoconfigure.FailureAnalyzer=\
com.example.analyzer.MyFailureAnalyzer

# 事件监听器
org.springframework.boot.SpringApplicationRunListener=\
com.example.listener.MyListener

Spring Boot 3.x 变化

Spring Boot 3.0+ 使用 SPI 机制替代 spring.factories

META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
# 每个配置类一行
com.example.autoconfig.MyAutoConfiguration
com.example.autoconfig.OtherAutoConfiguration
java
// 源码变化
// Spring Boot 2.7-
AutoConfigurationImportSelector.getAutoConfigurationEntry()

// Spring Boot 3.0+
AutoConfigurationImportSelector.getAutoConfigurationEntry() {
    // 使用 imports 文件替代 factories
}

五、自定义 Starter

项目结构

my-spring-boot-starter/
├── pom.xml
└── src/main/java/com/example/starter/
    ├── MyAutoConfiguration.java
    ├── MyProperties.java
    └── MyService.java

// 使用方项目
├── pom.xml
└── src/main/resources/
    └── META-INF/
        └── spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
        或 META-INF/spring.factories

1. 定义配置属性类

java
// MyProperties.java
@ConfigurationProperties(prefix = "my")
public class MyProperties {
    
    private boolean enabled = true;
    private String host = "localhost";
    private int port = 8080;
    private List<String> whitelist = new ArrayList<>();
    
    // getters/setters
}

2. 定义服务类

java
// MyService.java
public class MyService {
    
    private final MyProperties properties;
    
    public MyService(MyProperties properties) {
        this.properties = properties;
    }
    
    public void doSomething() {
        System.out.println("MyService: " + properties);
    }
}

3. 定义自动配置类

java
// MyAutoConfiguration.java
@AutoConfiguration
@EnableConfigurationProperties(MyProperties.class)
@ConditionalOnProperty(prefix = "my", name = "enabled", havingValue = "true", matchIfMissing = true)
public class MyAutoConfiguration {
    
    @Bean
    @ConditionalOnMissingBean(MyService.class)
    public MyService myService(MyProperties properties) {
        return new MyService(properties);
    }
}

4. 注册自动配置(Spring Boot 2.7-)

properties
# src/main/resources/META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.starter.MyAutoConfiguration

5. 注册自动配置(Spring Boot 3.0+)

properties
# src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
com.example.starter.MyAutoConfiguration

6. 使用方

xml
<dependency>
    <groupId>com.example</groupId>
    <artifactId>my-spring-boot-starter</artifactId>
    <version>1.0.0</version>
</dependency>
yaml
my:
  enabled: true
  host: localhost
  port: 9090
  whitelist:
    - item1
    - item2
java
@SpringBootApplication
public class App {
    public static void main(String[] args) {
        var ctx = SpringApplication.run(App.class, args);
        var service = ctx.getBean(MyService.class);
        service.doSomething();
    }
}

六、自动配置优先级

优先级从高到低

1. 命令行参数
   java -jar app.jar --spring.datasource.url=jdbc:mysql://...

2. ServletConfig / ServletContext 参数

3. JNDI

4. Java System properties
   System.setProperty("spring.datasource.url", "...")

5. OS 环境变量

6. 随机值覆盖属性(spring.ramdom.*)

7. JAR 包外配置文件
   ./config/application.yml

8. JAR 包内配置文件
   classpath:application.yml

9. @PropertySource 加载

10. 默认属性(spring-boot-defaults)

配置加载顺序

启动类加载 → application.yml → application-{profile}.yml → application.properties

              Spring Boot 默认值(最低优先级)

七、@EnableConfigurationProperties vs @ConfigurationPropertiesScan

java
// 方式1:@EnableConfigurationProperties(推荐在配置类上使用)
@Configuration
@EnableConfigurationProperties(MyProperties.class)
public class MyAutoConfiguration {
    // MyProperties 自动注册为 Bean
}

// 方式2:@ConfigurationPropertiesScan(Spring Boot 2.2+)
@SpringBootApplication
@ConfigurationPropertiesScan  // 扫描 @ConfigurationProperties
public class Application { }

// 方式3:@Component + @ConfigurationProperties(不推荐)
@Component
@ConfigurationProperties(prefix = "my")
public class MyProperties { }

八、AutoConfigurationImportFilter

过滤不需要的配置

java
// 实现 AutoConfigurationImportFilter
public class MyAutoConfigurationFilter implements AutoConfigurationImportFilter {
    
    @Override
    public boolean[] match(String[] autoConfigurationClasses, 
                           AutoConfigurationMetadata autoConfigurationMetadata) {
        boolean[] results = new boolean[autoConfigurationClasses.length];
        for (int i = 0; i < results.length; i++) {
            // 自定义过滤逻辑
            results[i] = !autoConfigurationClasses[i].contains("XXX");
        }
        return results;
    }
}

注册 Filter

properties
# META-INF/spring.factories
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
com.example.filter.MyAutoConfigurationFilter

九、失败分析器 FailureAnalyzer

自定义失败分析器

java
// 端口被占用时的默认提示不够详细
// 自定义分析器
@Component
public class MyBeanMissingFailureAnalyzer implements FailureAnalyzer {
    
    @Override
    public AnalysisResult analyze(Throwable failure) {
        if (failure instanceof BeanDefinitionStoreException) {
            String message = failure.getMessage();
            if (message.contains("MyBean")) {
                return FailureResult.withCause("MyBean 初始化失败,请检查配置")
                    .withSolution("确保添加了 @EnableMyFeature 注解");
            }
        }
        return null;  // 不处理,返回 null
    }
}
properties
# META-INF/spring.factories
org.springframework.boot.autoconfigure.FailureAnalyzer=\
com.example.analyzer.MyBeanMissingFailureAnalyzer

十、面试高频问题

Q1: 自动配置原理是什么?

  1. @EnableAutoConfiguration 启用自动配置
  2. AutoConfigurationImportSelector 读取 spring.factories
  3. 根据 @Conditional 条件筛选配置类
  4. 使用 @Bean 注册组件
  5. 用户配置优先于自动配置

Q2: 如何排除某个自动配置类?

java
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)

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

Q3: @ConditionalOnMissingBean 有什么用?

  • 防止重复注册 Bean
  • 允许用户自定义覆盖默认配置
  • 用户定义的 Bean 优先于自动配置

Q4: Spring Boot 3.x 自动配置有什么变化?

  • spring.factories 改为 META-INF/spring/*.imports
  • 每行一个完全限定名
  • 更好的 IDE 支持

Q5: 如何自定义 starter?

  1. 创建工程,引入 spring-boot-starter
  2. 定义配置属性类 @ConfigurationProperties
  3. 定义服务类
  4. 定义自动配置类 @AutoConfiguration
  5. 注册到 META-INF/spring/*.imports

Q6: 自动配置何时生效?

  • 应用启动时 refreshContext() 阶段
  • 按配置类中 @Conditional 条件判断
  • 配置类被解析后 @Bean 方法执行

十一、下一章预告

下一章我们将学习 Spring Boot starter 与起步依赖

  • starter 依赖管理机制
  • 常用 starter 详解
  • 如何排除/替换内置 starter

基于 MIT 许可发布