主题
ConditionalOn
@ConditionalOn注解
Spring Boot 默认提供了很多个 AutoConfiguration 类,显然不可能会全部引入。所以在自动装配时,系统会去类路径下寻找是否有对应的配置类。如果有对应的配置类,则按条件进行判断,决定是否需要装配。
Spring Boot 中提供了一系列的条件注解,常见的包括:
注解 | 描述 |
---|---|
@ConditionalOnProperty | 只有当所提供的属性为 true 时才会实例化 Bean |
@ConditionalOnBean | 只有在当前上下文中存在某个对象时才会实例化 Bean |
@ConditionalOnClass | 只有当某个 Class 位于类路径上时才会实例化 Bean |
@ConditionalOnExpression | 只有当表达式求值为 true 的时候才会实例化 Bean |
@ConditionalOnMissingBean | 只有在当前上下文中不存在某个对象时才会实例化 Bean |
@ConditionalOnMissingClass | 只有当某个 Class 在类路径上不存在时才会实例化 Bean |
@ConditionalOnNotWebApplication | 只有当应用不是 Web 应用时才会实例化 Bean |
这些注解都定义在 org.springframework.boot.autoconfigure.condition 包中。
@ConditionalOnClass原理
java
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({OnClassCondition.class})
public @interface ConditionalOnClass {
Class<?>[] value() default {};
String[] name() default {};
}
该注解本身带有两个属性,一个 Class 类型的 value,一个 String 类型的 name,可以采用这两种方式中的任意一种来使用该注解。
同时 ConditionalOnClass 注解本身还带了一个 @Conditional(OnClassCondition.class) 注解。
所以, ConditionalOnClass 注解的判断条件其实就包含在 OnClassCondition 这个类中。
OnClassCondition 是 SpringBootCondition 的子类,而 SpringBootCondition 又实现了Condition 接口。Condition 接口只有一个 matches 方法,
java
@FunctionalInterface
public interface Condition {
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
SpringBootCondition中的matches方法实现
java
public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String classOrMethodName = getClassOrMethodName(metadata); // 1
try {
ConditionOutcome outcome = this.getMatchOutcome(context, metadata); // 2
this.logOutcome(classOrMethodName, outcome);
this.recordEvaluation(context, classOrMethodName, outcome);
return outcome.isMatch();
} catch (NoClassDefFoundError var5) {
throw new IllegalStateException("Could not evaluate condition on " + classOrMethodName + " due to " + var5.getMessage() + " not found. Make sure your own configuration does not rely on that class. This can also happen if you are @ComponentScanning a springframework package (e.g. if you put a @ComponentScan in the default package by mistake)", var5);
} catch (RuntimeException var6) {
throw new IllegalStateException("Error processing condition on " + this.getName(metadata), var6);
}
}
java
private static String getClassOrMethodName(AnnotatedTypeMetadata metadata) {
if (metadata instanceof ClassMetadata) {
ClassMetadata classMetadata = (ClassMetadata)metadata;
return classMetadata.getClassName();
} else {
MethodMetadata methodMetadata = (MethodMetadata)metadata;
return methodMetadata.getDeclaringClassName() + "#" + methodMetadata.getMethodName();
}
}
这里的 getClassOrMethodName 方法获取被添加了@ConditionalOnClass 注解的类或者方法的名称,而 getMatchOutcome 方法用于获取匹配的输出。
getMatchOutcome 方法实际上是一个抽象方法,需要交由 SpringBootCondition 的各个子类完成实现,这里的子类就是 OnClassCondition 类。
在 Spring Boot 中,@ConditionalOnClass 或者 @ConditionalOnMissingClass 注解对应的条件类都是 OnClassCondition,所以在 OnClassCondition 的 getMatchOutcome 中会同时处理两种情况。
java
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
ClassLoader classLoader = context.getClassLoader();
ConditionMessage matchMessage = ConditionMessage.empty();
List<String> onClasses = this.getCandidates(metadata, ConditionalOnClass.class);
List onMissingClasses;
if (onClasses != null) {
onMissingClasses = this.filter(onClasses, ClassNameFilter.MISSING, classLoader);
if (!onMissingClasses.isEmpty()) {
return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnClass.class, new Object[0]).didNotFind("required class", "required classes").items(Style.QUOTE, onMissingClasses));
}
matchMessage = matchMessage.andCondition(ConditionalOnClass.class, new Object[0]).found("required class", "required classes").items(Style.QUOTE, this.filter(onClasses, ClassNameFilter.PRESENT, classLoader));
}
onMissingClasses = this.getCandidates(metadata, ConditionalOnMissingClass.class);
if (onMissingClasses != null) {
List<String> present = this.filter(onMissingClasses, ClassNameFilter.PRESENT, classLoader);
if (!present.isEmpty()) {
return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnMissingClass.class, new Object[0]).found("unwanted class", "unwanted classes").items(Style.QUOTE, present));
}
matchMessage = matchMessage.andCondition(ConditionalOnMissingClass.class, new Object[0]).didNotFind("unwanted class", "unwanted classes").items(Style.QUOTE, this.filter(onMissingClasses, ClassNameFilter.MISSING, classLoader));
}
return ConditionOutcome.match(matchMessage);
}
首先通过 getCandidates 方法获取了 ConditionalOnClass 的 name 属性和 value 属性。
然后通过 getMatches 方法将这些属性值进行比对,得到这些属性所指定的但在类加载器中不存在的类。如果发现类加载器中应该存在但事实上又不存在的类,则返回一个匹配失败的 Condition;
反之,如果类加载器中存在对应类的话,则把匹配信息进行记录并返回一个 ConditionOutcome。