跳到主要内容

Spring Boot 条件装配与自动配置深度内核

Spring Boot “约定优于配置”的工程实现 = 候选自动配置类列表 + @Conditional 过滤 + @Configuration 注册 Bean。本篇把条件体系、导入选择器、Starter 结构与排障一次讲清。

相关:Boot 启动扩展机制SPI 扩展点


一、@Conditional 体系

1. 常用注解

注解生效条件
@ConditionalOnClass类路径存在指定类
@ConditionalOnMissingClass类路径不存在
@ConditionalOnBean容器中已有指定 Bean
@ConditionalOnMissingBean容器中尚无指定 Bean
@ConditionalOnProperty配置属性存在/等于某值
@ConditionalOnWebApplicationServlet/Reactive Web 环境
@ConditionalOnExpressionSpEL 为 true
@ConditionalOnResource类路径资源存在
@ConditionalOnSingleCandidate指定类型仅有一个候选 Bean

2. 判定流程

实现接口:

public interface Condition {
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}

ConditionOutcome 带 match 原因字符串,--debugConditionEvaluationReport 可打印为何某自动配置没生效——排障神器。

3. 阶段差异:ConfigurationPhase

  • PARSE_CONFIGURATION:解析配置类时就判断(如 @ConditionalOnClass,避免类不存在导致解析失败)。
  • REGISTER_BEAN:注册 Bean 时再判断(如 @ConditionalOnBean,此时别的 BD 已可见)。

@ConditionalOnBean 写在类上时要小心顺序;更稳妥常放在 @Bean 方法上。


二、自动配置入口

@SpringBootApplication
└─ @EnableAutoConfiguration
└─ @Import(AutoConfigurationImportSelector)

1. 收集候选类

Boot 版本清单位置
2.xMETA-INF/spring.factoriesEnableAutoConfiguration
3.xMETA-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

每行一个全限定类名;多个 Starter 的列表会合并去重。

2. 过滤

  1. 用户 exclude / excludeName
  2. Autoconfiguration.imports 中的过滤 SPI
  3. 各配置类上的 @Conditional*
  4. 最终剩余类当作配置类导入容器

3. 排序

@AutoConfigureBefore / @AutoConfigureAfter / @AutoConfigureOrder 保证例如:

DataSourceAutoConfiguration
→ before → Mybatis 自动配置
→ before → 业务 Repository

三、典型自动配置类解剖

@AutoConfiguration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@ConditionalOnProperty(name = "spring.data.redis.host")
public class RedisAutoConfiguration {

@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<?, ?> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<?, ?> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
return template;
}
}

设计套路:

  1. 类路径门闩 @ConditionalOnClass:没引依赖就不加载。
  2. 属性绑定 @EnableConfigurationProperties
  3. 可覆盖 @ConditionalOnMissingBean:用户自定义同类型 Bean 则跳过默认。
  4. 开关 @ConditionalOnProperty

四、自定义 Starter 标准结构

my-spring-boot-starter // 空壳,只管依赖聚合
└─ pom 依赖 my-autoconfigure + 第三方库

my-spring-boot-autoconfigure
├─ com.example.MyAutoConfiguration
├─ com.example.MyProperties
└─ META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

AutoConfiguration.imports

com.example.MyAutoConfiguration

MyProperties

@ConfigurationProperties(prefix = "my.feature")
public class MyProperties {
private boolean enabled = true;
private int timeoutMs = 3000;
// getters/setters
}

用户侧:

my:
feature:
enabled: true
timeout-ms: 5000
<dependency>
<groupId>com.example</groupId>
<artifactId>my-spring-boot-starter</artifactId>
</dependency>

五、用户覆盖自动配置的正确姿势

目标做法
换实现自己 @Bean + 类型与默认相同,靠 OnMissingBean
关掉某自动配置@SpringBootApplication(exclude=...)spring.autoconfigure.exclude
改属性application.yml 绑定 *Properties
全部自己管exclude 后手写 @Configuration

避免:复制粘贴一整份官方 AutoConfiguration 再改三行——升级 Boot 即崩。


六、排障清单

  1. Bean 没有?

    • debug=trueNegative matches
    • 是否缺依赖导致 OnClass 失败。
    • 是否被 OnProperty 关掉。
    • 是否已有同名 Bean 导致 OnMissingBean 失败。
  2. Bean 重复?

    • 自己 @Bean 与自动配置都生效 → 给自动配置加 OnMissingBean 或 exclude。
  3. 顺序错误?

    • 使用 @AutoConfigureAfter;或 @DependsOn;或 @Order 于 BPP。
  4. Boot3 清单写错位置?

    • 仍写 spring.factoriesEnableAutoConfiguration 可能不加载。
  5. 多模块组件扫描不到?

    • 自动配置类通常不靠组件扫描,靠 imports 列表;业务 @Component 才靠 @SpringBootApplication 扫描包。

七、与 Spring 原生 SPI 的关系

机制用途
SpringFactoriesLoader / imports 文件发现自动配置、监听器、失败分析器
ApplicationContextInitializer上下文刷新前改造
EnvironmentPostProcessor最早改 Environment
BeanFactoryPostProcessor改 BeanDefinition

Starter 作者常组合:EnvironmentPostProcessor 加默认配置 + AutoConfiguration 建 Bean。


八、总结

  • 条件装配决定“何时创建”;自动配置决定“默认创建什么”。
  • 心智模型:清单加载 → 条件过滤 → 有序注册 → 用户可覆盖
  • 会写 Starter = 会写带 @Conditional@AutoConfiguration + 属性类 + imports 登记。

更完整的 Boot 扩展与 FatJar 见 扩展机制FatJar