最近使用了ConditionalOnClass注解,但是不知道底层是怎么实现的,趁周末看一下,顺便进行记录。
先来看一下结构:核心是OnClassCondition这个类,最上层实现了Condition接口。最后判断是否存在这个类,是使用的ClassLoader.loadClass()方法。
然后看@ConditionalOnClass注解源码
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnClassCondition.class)
public @interface ConditionalOnClass {
Class>[] value() default {}; // 需要匹配的类
String[] name() default {}; // 需要匹配的类名
}
它有2个属性,分别是类数组和字符串数组(作用一样,类型不一样),而且被@Conditional注解所修饰,这个@Conditional注解有个名为values的Class< ? extends Condition >[]
类型的属性。 这个Condition是个接口,用于匹配组件是否有资格被容器注册,定义如下:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
/**
* All {@link Condition Conditions} that must {@linkplain Condition#matches match}
* in order for the component to be registered.
*/
Class extends Condition>[] value();
}
@FunctionalInterface
public interface Condition {
/**
* Determine if the condition matches.
* @param context the condition context
* @param metadata metadata of the {@link org.springframework.core.type.AnnotationMetadata class}
* or {@link org.springframework.core.type.MethodMetadata method} being checked
* @return {@code true} if the condition matches and the component can be registered,
* or {@code false} to veto the annotated component's registration
*/
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
也就是说@Conditional注解属性中可以持有多个Condition接口的实现类,所有的Condition接口需要全部匹配成功后这个@Conditional修饰的组件才有资格被注册。
Condition接口有个实现抽象类SpringBootCondition,SpringBoot中所有条件注解对应的条件类都继承这个抽象类。它实现了matches方法:
public abstract class SpringBootCondition implements Condition {
private final Log logger = LogFactory.getLog(getClass());
@Override
public final boolean matches(ConditionContext context,
AnnotatedTypeMetadata metadata) {
String classOrMethodName = getClassOrMethodName(metadata);
try {
//确定匹配的结果以及合适的日志输出
ConditionOutcome outcome = getMatchOutcome(context, metadata);
logOutcome(classOrMethodName, outcome);
recordEvaluation(context, classOrMethodName, outcome);
return outcome.isMatch();
}
catch (NoClassDefFoundError ex) {
throw new IllegalStateException(
"Could not evaluate condition on " + classOrMethodName + " due to "
+ ex.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)",
ex);
}
catch (RuntimeException ex) {
throw new IllegalStateException(
"Error processing condition on " + getName(metadata), ex);
}
}
}
然后提供了一个抽象方法getMatchOutcome等待子类去实现。
SpringBoot提供了两个基于Class的条件注解:@ConditionalOnClass(类加载器中存在指明的类)或者@ConditionalOnMissingClass(类加载器中不存在指明的类)。
@ConditionalOnClass或者@ConditionalOnMissingClass注解对应的条件类是OnClassCondition,定义如下:
先看实现的getMatchOutcome方法
@Order(Ordered.HIGHEST_PRECEDENCE)
class OnClassCondition extends FilteringSpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
//获取上下文中的classLoader
ClassLoader classLoader = context.getClassLoader();
ConditionMessage matchMessage = ConditionMessage.empty();
//获取加ConditionalOnClass注解的所有类名
List onClasses = getCandidates(metadata, ConditionalOnClass.class);
if (onClasses != null) {
//进行过滤,找到上面onClasses这个list中使用当前的classLoader获取不到的类
List missing = filter(onClasses, ClassNameFilter.MISSING,
classLoader);
//如果不为空,返回一个匹配失败的ConditionOutcome结果,
if (!missing.isEmpty()) {
return ConditionOutcome
.noMatch(ConditionMessage.forCondition(ConditionalOnClass.class)
.didNotFind("required class", "required classes")
.items(Style.QUOTE, missing));
}
matchMessage = matchMessage.andCondition(ConditionalOnClass.class)
.found("required class", "required classes").items(Style.QUOTE,
filter(onClasses, ClassNameFilter.PRESENT, classLoader));
}
//下面同理,找ConditionalOnMissingClass这个注解的匹配结果
List onMissingClasses = getCandidates(metadata,
ConditionalOnMissingClass.class);
if (onMissingClasses != null) {
List 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);
}
}
最后看一下是如何匹配的,在List
方法
protected List filter(Collection classNames,
ClassNameFilter classNameFilter, ClassLoader classLoader) {
if (CollectionUtils.isEmpty(classNames)) {
return Collections.emptyList();
}
List matches = new ArrayList<>(classNames.size());
for (String candidate : classNames) {
//ClassNameFilter枚举中的matches方法
if (classNameFilter.matches(candidate, classLoader)) {
matches.add(candidate);
}
}
return matches;
}
protected enum ClassNameFilter {
PRESENT {
@Override
public boolean matches(String className, ClassLoader classLoader) {
return isPresent(className, classLoader);
}
},
MISSING {
@Override
public boolean matches(String className, ClassLoader classLoader) {
//走的是isPresent
return !isPresent(className, classLoader);
}
};
public abstract boolean matches(String className, ClassLoader classLoader);
//最底层的方法是下面的forName,调用的ClassLoader.loadClass
public static boolean isPresent(String className, ClassLoader classLoader) {
if (classLoader == null) {
classLoader = ClassUtils.getDefaultClassLoader();
}
try {
forName(className, classLoader);
return true;
}
catch (Throwable ex) {
return false;
}
}
private static Class> forName(String className, ClassLoader classLoader)
throws ClassNotFoundException {
if (classLoader != null) {
return classLoader.loadClass(className);
}
return Class.forName(className);
}
}