@Conditional

满足指定条件的时候才将某个 bean 加载到应用上下文中.

比如 FreemarkerAutoConfiguration 这个自动化配置类的定义如下:

1
2
3
4
5
6
7
@ConditionalOnClass(ThreadPoolTaskScheduler.class)
@Configuration
@EnableConfigurationProperties(TaskSchedulingProperties.class)
@AutoConfigureAfter(TaskExecutionAutoConfiguration.class)
public class TaskSchedulingAutoConfiguration {
// ...
}

这个自动化配置类被 @ConditionalOnClass 条件注解修饰,

这个条件注解存在的意义在于判断类加载器中是否存在 ThreadPoolTaskScheduler 这个类,

如果存在的话会在 Spring 容器中加载这个 TaskSchedulingAutoConfiguration 配置类, 否则不会加载.

@ConditionalOnXxxx

Spring Boot 在 @Conditional 注解的基础上进行了细化,无需自己实现 Condition 接口,只需要使用预定义好的 @ConditionalOnXxxx 类,如果验证通过,就会注册对应的 bean.

这些注解都定义在 org.springframework.boot.autoconfigure.condition 包下.

1
2
3
4
5
6
7
8
9
10
11
12
13
ConditionalOnBean
ConditionalOnClass
ConditionalOnCloudPlatform
ConditionalOnExpression
ConditionalOnJava
ConditionalOnJndi
ConditionalOnMissingBean
ConditionalOnMissingClass
ConditionalOnNotWebApplication
ConditionalOnProperty
ConditionalOnResource
ConditionalOnSingleCandidate
ConditionalOnWebApplication
1
2
3
4
5
6
7
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnBeanCondition.class)
public @interface ConditionalOnBean {
// ...
}

spring-boot Condition 注解对应处理的 Conditional 类

1
2
3
4
5
6
7
Condition (org.springframework.context.annotation)
ConfigurationCondition (org.springframework.context.annotation)
AbstractNestedCondition (org.springframework.boot.autoconfigure.condition)
AllNestedConditions (org.springframework.boot.autoconfigure.condition)
NoneNestedConditions (org.springframework.boot.autoconfigure.condition)
AnyNestedCondition (org.springframework.boot.autoconfigure.condition)
OnBeanCondition (org.springframework.boot.autoconfigure.condition)

Condition 接口

ConfigurationCondition 接口

多了一个 getConfigurationPhase() 方法, 也就是 条件注解的生效阶段. 只有在 ConfigurationPhase 中定义的两种阶段下才会生效.

SpringBootCondition

SpringBoot 中所有条件注解对应的条件类都继承这个抽象类.

OnClassCondition

@ConditionalOnClass 或者 @ConditionalOnMissingClass 注解对应的条件类是 OnClassCondition

入口

1.ComponentScan 扫描 basePackage 下的 components 的时候, 会调用 isConditionMatch(metadataReader) 判断该 component 是否条件匹配. 通过调用 conditionEvaluator.shouldSkip(metadataReader.getAnnotationMetadata()) 判断是否要 skip 该 component.
2.// TODO

调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
conditionEvaluator.shouldSkip()
1. 判断必须有 @Conditional 注解
2. 判断是否有 phase
3. 通过 metadata 获取该类上的所有 Conditional 注解
4. 遍历通过 conditionClassName 获取对应的 Condition 类
5. 对 conditions 排序
6. 遍历 conditions, 若是 ConfigurationCondition 类型, 调用 getConfigurationPhase() 方法, 调用 condition.matches() 方法
SpringBootCondition.matches()
1.getClassOrMethodName() 从 metadata 中获取类名或者方法名 (条件注解可以作用的类或者方法上)
2.getMatchOutcome(): 抽象方法, 具体子类实现. ConditionOutcome 记录了匹配结果 和 log 信息
由具体的 Condition 接口的实现类实现该逻辑, 如 OnClassCondition, OnExpressionCondition 等
// OnClassCondition.getMatchOutcome() 参见 下面逻辑
3.logOutcome(): 打印 匹配信息 日志
4.recordEvaluation(): 记录 匹配信息 结果
5.outcome.isMatch(): 返回是否匹配结果

OnClassCondition.getMatchOutcome()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
ClassLoader classLoader = context.getClassLoader();
ConditionMessage matchMessage = ConditionMessage.empty();
List<String> onClasses = getCandidates(metadata, ConditionalOnClass.class); // 1.ConditionalOnClass 注解处理. 获取类上 @ConditionalOnClass 注解里的条件值. 如从 @ConditionalOnClass({ Servlet.class, ServerContainer.class }) 中获取 Servlet.class, ServerContainer.class
if (onClasses != null) {
List<String> missing = filter(onClasses, ClassNameFilter.MISSING,
classLoader); // 判断条件类是否存在, 不存在的添加到 missing 里
if (!missing.isEmpty()) { // 有不存在的条件
return ConditionOutcome
.noMatch(ConditionMessage.forCondition(ConditionalOnClass.class)
.didNotFind("required class", "required classes")
.items(Style.QUOTE, missing)); // 不匹配 condition
}
matchMessage = matchMessage.andCondition(ConditionalOnClass.class)
.found("required class", "required classes").items(Style.QUOTE,
filter(onClasses, ClassNameFilter.PRESENT, classLoader)); // 匹配 condition
}
List<String> onMissingClasses = getCandidates(metadata,
ConditionalOnMissingClass.class); // 2. ConditionalOnMissingClass 注解处理
if (onMissingClasses != null) {
List<String> present = filter(onMissingClasses, ClassNameFilter.PRESENT,
classLoader);
if (!present.isEmpty()) {
return ConditionOutcome.noMatch(
ConditionMessage.forCondition(ConditionalOnMissingClass.class)
.found("unwanted class", "unwanted classes")
.items(Style.QUOTE, present));
}
matchMessage = matchMessage.andCondition(ConditionalOnMissingClass.class)
.didNotFind("unwanted class", "unwanted classes")
.items(Style.QUOTE, filter(onMissingClasses, ClassNameFilter.MISSING,
classLoader));
}
return ConditionOutcome.match(matchMessage);
}
1
2
3
4
5
6
7
对 ConditionalOnClass 和 ConditionalOnMissingClass 注解的处理.
1. 对 ConditionalOnClass 注解的处理
1. 获取类上 @ConditionalOnClass 注解里的条件值. 如从 @ConditionalOnClass({Servlet.class, ServerContainer.class}) 中获取 Servlet.class, ServerContainer.class
2.filter(onClasses, ClassNameFilter.MISSING, classLoader) 通过 ter.MISSING 这个 filter 过滤出不存在的类 (即通过 classLoader 加载不到)
3. 返回是否匹配结果
2. 对 ConditionalOnMissingClass 注解的处理
逻辑同上