条件评估源码分析
概述
Spring Boot 的条件评估(Condition Evaluation)机制是整个自动配置体系的决策核心。自动配置注册了约 100-200 个配置类,但只有满足特定条件的配置才会生效。这些条件通过 @Conditional 系列注解表达,由 ConditionEvaluator 在容器刷新期间统一评估。
本文从源码层面逐层剖析,涵盖评估入口、内置条件注解、结果产出与缓存优化。
一、评估入口:ConditionEvaluator.shouldSkip()
条件评估的入口位于 ConditionEvaluator.shouldSkip() 方法,它在 ConfigurationClassParser 解析 @Configuration 类时被调用。
1.1 调用链路
AbstractApplicationContext.refresh()
→ invokeBeanFactoryPostProcessors()
→ ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry()
→ ConfigurationClassParser.parse()
→ processConfigurationClass()
→ conditionEvaluator.shouldSkip() ← 条件评估入口1.2 shouldSkip() 逐行解析
// 类路径:org.springframework.context.annotation.ConditionEvaluator
public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {
// 1. 元数据上没有任何 @Conditional 注解 → 快速跳过,无需评估
if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {
return false;
}
// 2. 如果指定了 phase,检查 @Conditional 注解的生效阶段
if (phase == null) {
// 如果 @Conditional 同时标注了 PARSE_CONFIGURATION 阶段,且当前处于 REGISTER_BEAN 阶段
// 则放行到 REGISTER_BEAN 阶段再做评估(延迟评估)
if (metadata instanceof AnnotationMetadata &&
ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) {
return false;
}
phase = ConfigurationPhase.PARSE_CONFIGURATION;
}
// 3. 收集所有 @Conditional 中引用的 Condition 类
List<Condition> conditions = new ArrayList<>();
for (String[] conditionClasses : getConditionClasses(metadata)) {
for (String conditionClass : conditionClasses) {
if (Condition.class.isAssignableFrom(classes.resolveClassName(conditionClass))) {
conditions.add(classes.resolveClass(conditionClass).newInstance());
}
}
}
// 4. 排序(按 Ordered 或 @Order 注解)
AnnotationAwareOrderComparator.sort(conditions);
// 5. 逐个评估条件,任何一个返回 false → 跳过
for (Condition condition : conditions) {
ConfigurationPhase requiredPhase = null;
if (condition instanceof ConfigurationCondition) {
requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
}
// 匹配阶段才评估
if (requiredPhase == null || requiredPhase == phase) {
if (!condition.matches(this.context, metadata)) {
return true; // 条件不满足 → 跳过该配置类
}
}
}
return false; // 全部条件满足 → 不跳过
}关键设计点:
| 设计点 | 说明 |
|---|---|
| 两阶段评估 | PARSE_CONFIGURATION 和 REGISTER_BEAN,允许条件在解析配置和注册 Bean 时分别判断 |
| 快速失败短路 | 无 @Conditional 注解时直接返回 false,零开销 |
| 延迟评估 | 实现了 ConfigurationCondition 的注解可指定评估阶段 |
| Ordered 排序 | 条件之间可排序,保证评估顺序的确定性 |
1.3 两阶段评估策略
public enum ConfigurationPhase {
PARSE_CONFIGURATION, // 配置类解析阶段,此时 BeanDefinition 尚未注册
REGISTER_BEAN // Bean 注册阶段,此时可查询容器中已有的 Bean
}PARSE_CONFIGURATION:用于@ConditionalOnClass等无需依赖容器的条件REGISTER_BEAN:用于@ConditionalOnBean/@ConditionalOnMissingBean等需要查询 BeanFactory 的条件
二、FilteringSpringBootCondition 类族
Spring Boot 在 Spring Framework 的 Condition 接口之上,构建了一套专用于自动配置的条件体系。
2.1 类层次结构
Condition (Spring Framework)
└─ SpringBootCondition (Spring Boot)
├─ FilteringSpringBootCondition
│ ├─ OnClassCondition
│ ├─ OnBeanCondition
│ └─ OnWebApplicationCondition
└─ OnPropertyCondition
└─ OnResourceCondition
└─ OnExpressionCondition2.2 SpringBootCondition 模板方法
// 类路径:org.springframework.boot.autoconfigure.condition.SpringBootCondition
public abstract class SpringBootCondition implements Condition {
@Override
public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 1. 提取注解元数据中的条件类名
String classOrMethodName = getClassName(metadata);
// 2. 调用子类实现的 getMatchOutcome() 获取匹配结果
ConditionOutcome outcome = getMatchOutcome(context, metadata);
// 3. 记录日志
logOutcome(classOrMethodName, outcome);
// 4. 将评估结果注册到 ConditionEvaluationReport
recordEvaluation(context, classOrMethodName, outcome);
// 5. 返回是否匹配
return outcome.isMatch();
}
// 子类必须实现此方法,返回匹配结果
public abstract ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata);
}模板方法模式:matches() 定义了固定流程——获取结果、记录日志、注册报告,子类只需关注 getMatchOutcome() 的匹配逻辑。
2.3 FilteringSpringBootCondition
// 类路径:org.springframework.boot.autoconfigure.condition.FilteringSpringBootCondition
public abstract class FilteringSpringBootCondition extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 1. 收集所有待匹配的类名
List<String> candidates = getCandidates(context, metadata);
// 2. 如果有缓存命中,直接返回缓存结果
if (candidates.isEmpty()) {
return ConditionOutcome.match(ConditionMessage.empty());
}
// 3. 遍历候选类,逐个进行类加载检查
for (String candidate : candidates) {
if (!isPresent(candidate, context)) {
return ConditionOutcome.noMatch(ConditionMessage
.forCondition(metadata)
.didNotFind("class", candidate));
}
}
return ConditionOutcome.match(ConditionMessage
.forCondition(metadata)
.found("class", candidates).items(ConditionMessage.Style.QUOTE));
}
// 子类提取候选类名列表(从注解属性中读取)
protected abstract List<String> getCandidates(ConditionContext context,
AnnotatedTypeMetadata metadata);
}三、核心条件注解源码分析
3.1 OnClassCondition
// 类路径:org.springframework.boot.autoconfigure.condition.OnClassCondition
@Order(Ordered.HIGHEST_PRECEDENCE) // 最高优先级,最先评估
class OnClassCondition extends FilteringSpringBootCondition {
@Override
protected List<String> getCandidates(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 从 @ConditionalOnClass 注解中提取 value 和 name 属性
MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(
ConditionalOnClass.class.getName(), true);
if (attributes == null) return Collections.emptyList();
List<String> candidates = new ArrayList<>();
// value() — Class 数组(推荐方式,编译期检查)
addAll(candidates, attributes.get("value"));
// name() — String 数组(支持运行时动态类名)
addAll(candidates, attributes.get("name"));
return candidates;
}
@Override
public ConfigurationPhase getConfigurationPhase() {
return ConfigurationPhase.PARSE_CONFIGURATION; // 解析阶段即可评估
}
}匹配逻辑:
@ConditionalOnClass(name = "redis.clients.jedis.Jedis")
→ OnClassCondition.getCandidates() 提取 "redis.clients.jedis.Jedis"
→ FilteringSpringBootCondition.isPresent()
→ ClassUtils.isPresent()(使用线程上下文类加载器)
→ 类存在 → match
→ 类不存在 → noMatch3.2 OnBeanCondition
// 类路径:org.springframework.boot.autoconfigure.condition.OnBeanCondition
@Order(Ordered.LOWEST_PRECEDENCE) // 最低优先级,最后评估(需等容器准备就绪)
class OnBeanCondition extends FilteringSpringBootCondition {
@Override
public ConfigurationPhase getConfigurationPhase() {
return ConfigurationPhase.REGISTER_BEAN; // 必须 Bean 注册阶段
}
@Override
protected List<String> getCandidates(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 解析 @ConditionalOnBean / @ConditionalOnMissingBean / @ConditionalOnSingleCandidate
MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(
ConditionalOnBean.class.getName(), true);
// ... 提取 value(), name(), annotation() 等属性
}
}核心差异:
// @ConditionalOnBean → 要求容器中 **存在** 指定 Bean
// @ConditionalOnMissingBean → 要求容器中 **不存在** 指定 Bean
// @ConditionalOnSingleCandidate → 要求容器中指定类型有且仅有一个候选 Bean
// 匹配策略枚举
enum SearchStrategy {
CURRENT, // 仅在当前容器搜索
PARENTS, // 仅在父容器搜索
ALL // 在当前和父容器中搜索
}内部匹配实现(简化):
OnBeanCondition.getMatchOutcome()
→ 解析注解的 type(类)/ name(名称)/ annotation(注解)/ parameterizedContainer(泛型容器)
→ 根据 SearchStrategy 搜索 BeanFactory
→ BeanFactory.getBeanNamesForType()
→ BeanFactory.getBeanNamesForAnnotation()
→ @ConditionalOnBean: count > 0 → match
→ @ConditionalOnMissingBean: count == 0 → match
→ @ConditionalOnSingleCandidate: count == 1 → match3.3 OnPropertyCondition
// 类路径:org.springframework.boot.autoconfigure.condition.OnPropertyCondition
class OnPropertyCondition extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 1. 解析 @ConditionalOnProperty 的所有属性
ConditionalOnProperty annotation = (ConditionalOnProperty)
metadata.getAnnotationAttributes(ConditionalOnProperty.class.getName());
// 2. 构建完整的属性键
String prefix = annotation.prefix();
String havingValue = annotation.havingValue();
String[] names = annotation.name();
// 3. 遍历每个 name,检查属性是否存在且值匹配
for (String name : names) {
String key = prefix + "." + name;
String value = environment.getProperty(key);
if (value == null) {
// 属性不存在
if (matchIfMissing) return match;
return noMatch("property '" + key + "' is missing");
}
if (!havingValue.isEmpty() && !havingValue.equals(value)) {
return noMatch("property '" + key + "' was '" + value + "', expected '" + havingValue + "'");
}
}
return ConditionOutcome.match(...);
}
}3.4 条件注解与源码类汇总
| 注解 | 源码类 | 评估阶段 | 核心逻辑 |
|---|---|---|---|
@ConditionalOnClass | OnClassCondition | PARSE_CONFIGURATION | 类加载器检查指定类是否存在 |
@ConditionalOnMissingClass | OnClassCondition | PARSE_CONFIGURATION | 与上者反向逻辑,同一类处理 |
@ConditionalOnBean | OnBeanCondition | REGISTER_BEAN | 搜索 BeanFactory 中是否存在指定 Bean |
@ConditionalOnMissingBean | OnBeanCondition | REGISTER_BEAN | 搜索 BeanFactory 中是否不存在指定 Bean |
@ConditionalOnSingleCandidate | OnBeanCondition | REGISTER_BEAN | 指定类型有且仅有一个候选 Bean |
@ConditionalOnProperty | OnPropertyCondition | PARSE_CONFIGURATION | 从 Environment 读取配置属性 |
@ConditionalOnResource | OnResourceCondition | PARSE_CONFIGURATION | 检查 classpath 资源文件是否存在 |
@ConditionalOnWebApplication | OnWebApplicationCondition | PARSE_CONFIGURATION | 检测 Web 应用类型(SERVLET/REACTIVE) |
@ConditionalOnNotWebApplication | OnWebApplicationCondition | PARSE_CONFIGURATION | 检测非 Web 应用 |
@ConditionalOnExpression | OnExpressionCondition | PARSE_CONFIGURATION | 解析 SpEL 表达式 |
@ConditionalOnJava | OnJavaCondition | PARSE_CONFIGURATION | 检查 Java 版本范围 |
@ConditionalOnJndi | OnJndiCondition | PARSE_CONFIGURATION | 检查 JNDI 资源是否存在 |
@ConditionalOnCloudPlatform | OnCloudPlatformCondition | PARSE_CONFIGURATION | 检查云平台环境 |
四、ConditionOutcome 与 ConditionMessage
4.1 ConditionOutcome
ConditionOutcome 封装单个条件的评估结果,包含两个核心字段:
// 类路径:org.springframework.boot.autoconfigure.condition.ConditionOutcome
public class ConditionOutcome {
private final boolean match; // 是否匹配
private final ConditionMessage message; // 描述信息(用于日志和报告)
// 工厂方法
public static ConditionOutcome match() { ... }
public static ConditionOutcome match(ConditionMessage message) { ... }
public static ConditionOutcome noMatch(ConditionMessage message) { ... }
public boolean isMatch() { return this.match; }
public ConditionMessage getMessage() { return this.message; }
}4.2 ConditionMessage
ConditionMessage 提供链式 API 构建可读性强的描述文本:
// 类路径:org.springframework.boot.autoconfigure.condition.ConditionMessage
public class ConditionMessage {
private final String message;
// 链式工厂方法
public static ConditionMessage forCondition(String condition) { ... }
public static ConditionMessage forCondition(Class<?> condition) { ... }
public static ConditionMessage forCondition(AnnotatedTypeMetadata metadata) { ... }
// 构建匹配信息
public ConditionMessage found(String element, Object value) { ... }
public ConditionMessage didNotFind(String element, Object value) { ... }
// Items 格式化
public ConditionMessage items(Style style, Object... items) { ... }
// 最终输出
@Override
public String toString() {
return this.message;
}
}典型输出示例:
@ConditionalOnClass found required class 'org.springframework.data.redis.core.RedisOperations'
↑ 由 ConditionMessage.forCondition(metadata).found("class", "RedisOperations").items(QUOTE) 生成
@ConditionalOnProperty (spring.aop.auto=true) matched
↑ 由 ConditionMessage.forCondition("ConditionalOnProperty").found("spring.aop.auto", "true") 生成4.3 日志记录与报告注册
// SpringBootCondition.matches() 中的记录逻辑
private void recordEvaluation(ConditionContext context, String classOrMethodName,
ConditionOutcome outcome) {
// 获取 ConditionEvaluationReport(单例,绑定在 BeanFactory 上)
ConditionEvaluationReport report = ConditionEvaluationReport.get(
context.getBeanFactory());
// 登记评估结果
if (outcome.isMatch()) {
report.recordConditionEvaluated(classOrMethodName, outcome);
} else {
report.recordConditionEvaluated(classOrMethodName, outcome);
// 同时记录为 Negative match
report.recordNegativeEvaluation(classOrMethodName, outcome);
}
}五、自动配置报告生成原理
5.1 ConditionEvaluationReport
// 类路径:org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport
public class ConditionEvaluationReport {
// 每个配置类的评估结果
private final Map<String, ConditionAndOutcomes> outcomes;
// 排除的配置类
private final Set<String> exclusions;
// 父容器的报告
private final List<ConditionEvaluationReport> parentReports;
// 添加正匹配
public void recordConditionEvaluated(String name, ConditionOutcome outcome) { ... }
// 添加负匹配(内部调用 recordConditionEvaluated)
public void recordNegativeEvaluation(String name, ConditionOutcome outcome) { ... }
// 获取完整的评估数据
public Map<String, ConditionAndOutcomes> getConditionAndOutcomesBySource() {
return this.outcomes;
}
// 获取排除列表
public Set<String> getExclusions() {
return this.exclusions;
}
}5.2 报告输出
AutoConfigurationReportLoggingInitializer 在启动完成后打印报告:
// 类路径:org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
public class ConditionEvaluationReportLoggingListener
implements ApplicationListener<ApplicationReadyEvent> {
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
// 从 BeanFactory 获取报告
ConditionEvaluationReport report = ConditionEvaluationReport.get(
event.getApplicationContext().getBeanFactory());
// 输出报告
logReport(report);
}
private void logReport(ConditionEvaluationReport report) {
StringBuilder message = new StringBuilder();
message.append("%n%n============================%n");
message.append("AUTO-CONFIGURATION REPORT%n");
message.append("============================%n%n");
// 1. Positive matches(正匹配)
message.append("Positive matches:%n-----------------%n");
report.getConditionAndOutcomesBySource().entrySet().stream()
.filter(e -> e.getValue().isFullMatch())
.forEach(e -> formatOutcome(message, e));
// 2. Negative matches(负匹配)
message.append("%nNegative matches:%n-----------------%n");
report.getConditionAndOutcomesBySource().entrySet().stream()
.filter(e -> !e.getValue().isFullMatch())
.forEach(e -> formatOutcome(message, e));
// 3. Exclusions(排除项)
message.append("%nExclusions:%n-----------%n");
report.getExclusions().forEach(name -> message.append(name).append("%n"));
logger.info(message.toString());
}
}5.3 报告示例
启用 debug=true 后可在控制台看到完整报告:
============================
AUTO-CONFIGURATION REPORT
============================
Positive matches:
-----------------
DataSourceAutoConfiguration matched:
- @ConditionalOnClass found required class 'javax.sql.DataSource' (OnClassCondition)
RedisAutoConfiguration matched:
- @ConditionalOnClass found required class 'org.springframework.data.redis.core.RedisOperations' (OnClassCondition)
Negative matches:
-----------------
RabbitAutoConfiguration:
Did not match:
- @ConditionalOnClass did not find required class 'com.rabbitmq.client.ConnectionFactory' (OnClassCondition)
ActiveMQAutoConfiguration:
Did not match:
- @ConditionalOnClass did not find required class 'javax.jms.ConnectionFactory' (OnClassCondition)
Exclusions:
-----------
org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration5.4 条件评估信息输出格式
// 内部类,保存单个条件的所有评估结果
public static class ConditionAndOutcomes implements Iterable<ConditionAndOutcome> {
private final Set<ConditionAndOutcome> outcomes = new LinkedHashSet<>();
public boolean isFullMatch() {
// 所有条件都匹配时才视为 FullMatch
return outcomes.stream().allMatch(ConditionAndOutcome::isMatch);
}
}六、实战:三大自动配置的条件分析
6.1 RedisAutoConfiguration
// 类路径:org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration
@AutoConfiguration
@ConditionalOnClass(RedisOperations.class) // ① classpath 需有 Redis 客户端库
@EnableConfigurationProperties(RedisProperties.class)
@AutoConfigureAfter(RedisReactiveAutoConfiguration.class)
public class RedisAutoConfiguration {
// ——— 内嵌配置类:Lettuce 连接工厂 ———
@Configuration
@ConditionalOnClass(LettuceConnectionFactory.class) // ② 需引入 lettuce-core
@ConditionalOnMissingBean(RedisConnectionFactory.class) // ③ 用户未自定义连接工厂
protected static class LettuceConfiguration {
@Bean
public LettuceConnectionFactory redisConnectionFactory(
RedisProperties properties) {
return new LettuceConnectionFactory(properties.getHost(), properties.getPort());
}
}
// ——— 内嵌配置类:Jedis 连接工厂 ———
@Configuration
@ConditionalOnClass(JedisConnectionFactory.class) // ②' 需引入 jedis
@ConditionalOnMissingBean(RedisConnectionFactory.class) // ③ 用户未自定义连接工厂
protected static class JedisConfiguration {
@Bean
public JedisConnectionFactory redisConnectionFactory(
RedisProperties properties) {
return new JedisConnectionFactory();
}
}
// ——— RedisTemplate ———
@Bean
@ConditionalOnMissingBean(name = "redisTemplate") // ④ 用户未自定义 redisTemplate
@ConditionalOnSingleCandidate(RedisConnectionFactory.class) // ⑤ 连接工厂已就绪
public RedisTemplate<Object, Object> redisTemplate(
RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
// ——— StringRedisTemplate ———
@Bean
@ConditionalOnMissingBean
public StringRedisTemplate stringRedisTemplate(
RedisConnectionFactory redisConnectionFactory) {
return new StringRedisTemplate(redisConnectionFactory);
}
}条件决策树:
RedisAutoConfiguration
├─ @ConditionalOnClass(RedisOperations.class) → classpath 有 spring-data-redis?
│ ├─ YES → 进入配置
│ │ ├─ Lettuce 分支 → @ConditionalOnClass(LettuceConnectionFactory.class)?
│ │ │ ├─ YES → @ConditionalOnMissingBean(RedisConnectionFactory)?
│ │ │ │ ├─ YES → 创建 LettuceConnectionFactory
│ │ │ │ └─ NO → 跳过(使用用户自定义的)
│ │ │ └─ NO → Jedis 分支
│ │ └─ redisTemplate → @ConditionalOnMissingBean(name="redisTemplate")?
│ │ └─ YES → @ConditionalOnSingleCandidate(RedisConnectionFactory)?
│ │ └─ YES → 创建 RedisTemplate
│ └─ NO → 整个 RedisAutoConfiguration 不生效6.2 DataSourceAutoConfiguration
// 类路径:org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
@AutoConfiguration
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class }) // ①
@EnableConfigurationProperties(DataSourceProperties.class)
@AutoConfigureBefore({ JdbcTemplateAutoConfiguration.class,
HibernateJpaAutoConfiguration.class })
public class DataSourceAutoConfiguration {
// ——— 内嵌配置:内嵌数据源 ———
@Configuration
@Conditional(EmbeddedDatabaseCondition.class) // ② 自定义条件
@ConditionalOnMissingBean({ DataSource.class,
XADataSource.class }) // ③ 用户未自定义
protected static class EmbeddedDatabaseConfiguration {
@Bean
public DataSource dataSource(DataSourceProperties properties) {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.build();
}
}
// ——— 连接池配置 ———
@Configuration
@Conditional(PooledDataSourceCondition.class) // ②' 池化条件
@ConditionalOnMissingBean({ DataSource.class,
XADataSource.class }) // ③ 用户未自定义
@AutoConfigureBefore({ HikariBasedConfiguration.class,
TomcatBasedConfiguration.class })
protected static class PooledDataSourceConfiguration {
@Configuration
@ConditionalOnClass(HikariDataSource.class) // ④ HikariCP
protected static class HikariBasedConfiguration {
@Bean
public DataSource dataSource() {
return createDataSource(properties, HikariDataSource.class);
}
}
@Configuration
@ConditionalOnClass(TomcatDataSource.class) // ④' Tomcat CP
protected static class TomcatBasedConfiguration {
@Bean
public DataSource dataSource() {
return createDataSource(properties, TomcatDataSource.class);
}
}
@Configuration
@ConditionalOnClass(BasicDataSource.class) // ④'' DBCP2
protected static class Dbcp2BasedConfiguration {
@Bean
public DataSource dataSource() {
return createDataSource(properties, BasicDataSource.class);
}
}
}
}关键条件:
| 条件 | 说明 |
|---|---|
@ConditionalOnClass(DataSource.class) | classpath 需有 JDBC 驱动 |
EmbeddedDatabaseCondition | 自定义条件:检测 classpath 中的内嵌数据库(H2/HSQL/Derby) |
PooledDataSourceCondition | 自定义条件:检测 classpath 中的连接池 |
@ConditionalOnClass(HikariDataSource.class) | Spring Boot 2.x+ 默认优先 HikariCP |
6.3 RabbitAutoConfiguration
// 类路径:org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration
@AutoConfiguration
@ConditionalOnClass(RabbitTemplate.class) // ① 需引入 spring-rabbit
@EnableConfigurationProperties(RabbitProperties.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class RabbitAutoConfiguration {
// ——— ConnectionFactory ———
@Bean
@ConditionalOnMissingBean
public CachingConnectionFactory rabbitConnectionFactory(
RabbitProperties config) {
CachingConnectionFactory factory = new CachingConnectionFactory();
factory.setHost(config.getHost());
factory.setPort(config.getPort());
factory.setVirtualHost(config.getVirtualHost());
factory.setUsername(config.getUsername());
factory.setPassword(config.getPassword());
return factory;
}
// ——— RabbitTemplate ———
@Bean
@ConditionalOnSingleCandidate(ConnectionFactory.class) // ② 连接工厂已存在
@ConditionalOnMissingBean // ③ 用户未自定义
@ConditionalOnProperty(prefix = "spring.rabbitmq",
name = "dynamic", matchIfMissing = true) // ④ 配置检查
public RabbitTemplate rabbitTemplate(
ConnectionFactory connectionFactory) {
return new RabbitTemplate(connectionFactory);
}
// ——— RabbitAdmin ———
@Bean
@ConditionalOnSingleCandidate(ConnectionFactory.class)
@ConditionalOnProperty(prefix = "spring.rabbitmq",
name = "dynamic", matchIfMissing = true)
@ConditionalOnMissingBean
public AmqpAdmin amqpAdmin(ConnectionFactory connectionFactory) {
return new RabbitAdmin(connectionFactory);
}
// ——— 消息转换器 ———
@Configuration
@ConditionalOnClass(Jackson2JsonMessageConverter.class) // ⑤ Jackson
@ConditionalOnProperty(prefix = "spring.rabbitmq",
name = "message-converter",
havingValue = "json") // ⑥ 配置要求
protected static class MessageConverterConfiguration {
@Bean
@ConditionalOnMissingBean
public MessageConverter jacksonMessageConverter() {
return new Jackson2JsonMessageConverter();
}
}
}七、条件注解的缓存与性能优化
7.1 类加载缓存
FilteringSpringBootCondition 中的 isPresent() 方法对已检查的类名做缓存:
// 类路径:org.springframework.boot.autoconfigure.condition.FilteringSpringBootCondition
private static final Set<String> PRESENT_CLASSES = new ConcurrentHashSet<>();
private static final Set<String> MISSING_CLASSES = new ConcurrentHashSet<>();
protected final boolean isPresent(String className, ConditionContext context) {
// 1. 先查缓存
if (PRESENT_CLASSES.contains(className)) return true;
if (MISSING_CLASSES.contains(className)) return false;
// 2. 实际类加载
ClassLoader cl = context.getClassLoader();
try {
ClassUtils.forName(className, cl);
PRESENT_CLASSES.add(className); // 缓存命中 → 加入正缓存
return true;
} catch (ClassNotFoundException ex) {
MISSING_CLASSES.add(className); // 缓存未命中 → 加入负缓存
return false;
}
}缓存统计:在典型的 Spring Boot 应用中,约 80% 的条件检查类名已在缓存中,大幅降低了类加载开销。
7.2 属性值缓存
OnPropertyCondition 对 Environment.getProperty() 的结果进行缓存:
// OnPropertyCondition 内部使用 PropertyResolver 的缓存机制
// Spring Environment 实现(AbstractEnvironment)本身对 property 做了缓存
// 同一个 property 键在启动期间不会重复加载资源文件7.3 条件评估顺序优化
评估顺序 原因
────────────────────────────────────────────────────
1. OnClassCondition 只需类加载器,最快
2. OnPropertyCondition 只需读配置,较快
3. OnResourceCondition 只需检查资源
4. OnWebApplicationCondition 只需检查 Web 类型
5. OnBeanCondition 需扫描 BeanFactory,最慢通过 @Order 注解控制评估顺序,保证最快失败的条件先执行,尽早短路。
7.4 延迟匹配(Deferred Import)
// AutoConfigurationImportSelector 实现了 DeferredImportSelector
// 等到所有普通 @Configuration 解析完毕后再处理自动配置
// 好处:此时用户自定义的 @Configuration 已经注册为 BeanDefinition
// OnBeanCondition 可以准确判断 Bean 是否存在
// 具体延迟机制:
// ConfigurationClassParser 将 DeferredImportSelector 的导入推迟到最后处理
// 处理时调用 AutoConfigurationGroup.process() 进行批量过滤7.5 AutoConfigurationImportFilter
Spring Boot 对自动配置类的条件过滤做了批量优化:
// 类路径:org.springframework.boot.autoconfigure.AutoConfigurationImportFilter
public interface AutoConfigurationImportFilter {
// 批量匹配方法,一次性检查多个配置类
// 相比逐个检查,减少类加载器和元数据的重复创建
boolean[] match(String[] autoConfigurationClasses,
AutoConfigurationMetadata autoConfigurationMetadata);
}
// 实现类:OnClassCondition 同时实现了此接口
public class OnClassCondition extends FilteringSpringBootCondition
implements AutoConfigurationImportFilter {
@Override
public boolean[] match(String[] autoConfigurationClasses,
AutoConfigurationMetadata autoConfigurationMetadata) {
// 批量遍历所有候选配置类
// 一次解析所有 @ConditionalOnClass 注解,批量检查类存在性
// 避免重复创建 ClassLoader 和 MetadataReader
boolean[] result = new boolean[autoConfigurationClasses.length];
for (int i = 0; i < autoConfigurationClasses.length; i++) {
result[i] = matches(autoConfigurationClasses[i],
autoConfigurationMetadata);
}
return result;
}
}八、条件评估完整流程总结
8.1 整体流程图
spring.factories / AutoConfiguration.imports
│
├─ 读取约 142 个自动配置候选类
│
├─ AutoConfigurationImportSelector.filter()
│ │
│ ├─ AutoConfigurationImportFilter.match() ← 批量预过滤
│ │ └─ OnClassCondition.batchMatch()
│ │ └─ 类加载器检查(含缓存)
│ │
│ └─ 第一轮过滤:排除明显不满足条件的配置类
│
├─ ConfigurationClassParser.processConfigurationClass()
│ │
│ └─ ConditionEvaluator.shouldSkip() ← 逐类评估
│ │
│ ├─ 读取 @Conditional 注解列表
│ │
│ ├─ 阶段匹配(PARSE_CONFIGURATION / REGISTER_BEAN)
│ │
│ └─ SpringBootCondition.matches() ← 模板方法
│ │
│ ├─ getMatchOutcome() ← 子类实现
│ │ ├─ OnClassCondition: 类存在性
│ │ ├─ OnBeanCondition: 容器 Bean
│ │ ├─ OnPropertyCondition:配置属性
│ │ └─ ...
│ │
│ ├─ logOutcome() ← 日志记录
│ │
│ └─ recordEvaluation() ← 注册到报告
│ └─ ConditionEvaluationReport
│
└─ 启动完成后 → ApplicationReadyEvent
└─ ConditionEvaluationReportLoggingListener
└─ 输出 AUTO-CONFIGURATION REPORT
├─ Positive matches
├─ Negative matches
└─ Exclusions8.2 关键类一览
| 类名 | 职责 |
|---|---|
ConditionEvaluator | 条件评估入口,解析 @Conditional 注解列表 |
SpringBootCondition | 模板基类,封装日志和报告注册 |
FilteringSpringBootCondition | 类名匹配基类,包含类加载缓存 |
OnClassCondition | @ConditionalOnClass / @ConditionalOnMissingClass 实现 |
OnBeanCondition | @ConditionalOnBean / @ConditionalOnMissingBean / @ConditionalOnSingleCandidate 实现 |
OnPropertyCondition | @ConditionalOnProperty 实现 |
ConditionOutcome | 封装单次评估结果(match + message) |
ConditionMessage | 构建可读的条件评估描述文本 |
ConditionEvaluationReport | 聚合所有评估结果,供报告输出 |
AutoConfigurationImportFilter | 批量预过滤接口,减少评估次数 |
AutoConfigurationImportSelector | 自动配置导入选择器,驱动整个过滤流程 |
8.3 调试与诊断
# 1. 查看完整自动配置报告
debug=true
# 2. 查看条件评估日志
logging.level.org.springframework.boot.autoconfigure.condition=DEBUG
# 3. 查看配置类解析日志
logging.level.org.springframework.context.annotation.ConfigurationClassParser=TRACE
# 4. 编程方式获取报告
@Autowired
private ApplicationContext context;
ConditionEvaluationReport report = ConditionEvaluationReport.get(context);
report.getConditionAndOutcomesBySource().forEach((source, outcomes) -> {
System.out.println(source + " → " + (outcomes.isFullMatch() ? "MATCH" : "NO MATCH"));
outcomes.forEach(outcome ->
System.out.println(" " + outcome.getOutcome().getMessage()));
});# DEBUG 日志示例
2026-07-22 10:15:30.123 DEBUG 12345 --- [main] o.s.b.a.condition.OnClassCondition : ConditionEvaluationReportLoggingListener
@ConditionalOnClass classes found: [org.springframework.data.redis.core.RedisOperations] (OnClassCondition)
2026-07-22 10:15:30.456 DEBUG 12345 --- [main] o.s.b.a.condition.OnClassCondition :
@ConditionalOnClass classes did not find: com.rabbitmq.client.ConnectionFactory (OnClassCondition)通过条件评估报告,开发者可以精确诊断为什么某个自动配置没有生效,是哪个条件导致了不匹配,从而快速定位 classpath 依赖缺失或配置错误问题。