注解@ConditionalOnClass的源码实现

在Springboot中,支持了很多条件注解,@ConditionalOnClass注解就是其中之一。它主要是用来判断该注解所指定的某个类或者某些类是否在ClassPath中存在,如果存在则符合条件,如果不存在则不符合

注解源码:

1
2
3
4
5
6
7
8
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnClassCondition.class)
public @interface ConditionalOnClass {
Class<?>[] value() default {};
String[] name() default {};
}

通过valuename来指定要判断的类,而真正执行判断的逻辑在OnClassCondition类中。

  1. ConditionalOnClass注解逻辑在OnClassCondition类中
  2. OnClassCondition继承了SpringBootCondition
  3. SpringBootCondition类实现了Condition接口

所以Spring在解析条件注解时,就会调用Condition接口的matches()方法,SpringBootCondition类中实现了matches()方法,所以会被先调用

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
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);
}
}

//....................省略..............
}

在matches方法中,会调用getMatchOutcome()方法,并得到ConditionOutcome对象,它表示的就是条件判断的结果。

1
2
3
4
5
6
7
8
public class ConditionOutcome {
//表示条件是否匹配
private final boolean match;

private final ConditionMessage message;

//....................省略..............
}

getMatchOutcome()方法在SpringBootCondition类中是一个抽象方法,在子类OnClassCondition类中才真正实现了getMatchOutcome方法
进行条件判断.

所以核心就是这个getMatchOutcome()方法,在这个方法中会先获取@ConditionalOnClass注解的value和name属性的值,这些值就是待判断的类名集合。

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
//OnClassCondition 类
@Order(Ordered.HIGHEST_PRECEDENCE)
class OnClassCondition extends SpringBootCondition
implements AutoConfigurationImportFilter, BeanFactoryAware, BeanClassLoaderAware {

//..................省略................

@Override
public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
ClassLoader classLoader = context.getClassLoader();
ConditionMessage matchMessage = ConditionMessage.empty();
//重点:找到所有包含ConditionalOnClass 注解的类
List<String> onClasses = getCandidates(metadata, ConditionalOnClass.class);
if (onClasses != null) {
//然后根据classLoader去加载,判断类是否存在
List<String> missing = getMatches(onClasses, MatchType.MISSING, classLoader);
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,
getMatches(onClasses, MatchType.PRESENT, classLoader));
}
List<String> onMissingClasses = getCandidates(metadata,
ConditionalOnMissingClass.class);
if (onMissingClasses != null) {
List<String> present = getMatches(onMissingClasses, MatchType.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,
getMatches(onMissingClasses, MatchType.MISSING, classLoader));
}
//如果都存在则返回匹配的ConditionOutcome对象
return ConditionOutcome.match(matchMessage);
}


private List<String> getCandidates(AnnotatedTypeMetadata metadata,
Class<?> annotationType) {
MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(annotationType.getName(), true);
if (attributes == null) {
return Collections.emptyList();
}
List<String> candidates = new ArrayList<>();
addAll(candidates, attributes.get("value"));
addAll(candidates, attributes.get("name"));
return candidates;
}
}

接下来就会逐个判断类名集合中的每个类名,判断逻辑为:利用MatchType.MISSING来判断某个类是否存在

1
List<String> missing = getMatches(onClasses, MatchType.MISSING, classLoader);
1
2
3
4
5
6
7
8
9
10
private List<String> getMatches(Collection<String> candidates, MatchType matchType,
ClassLoader classLoader) {
List<String> matches = new ArrayList<>(candidates.size());
for (String candidate : candidates) {
if (matchType.matches(candidate, classLoader)) {
matches.add(candidate);
}
}
return matches;
}

MatchType.MISSING就是利用ClassLoader来加载类,如果加载到了表示类存在,没加载到表示类不存在

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
37
38
private enum MatchType {
PRESENT {
@Override
public boolean matches(String className, ClassLoader classLoader) {
return isPresent(className, classLoader);
}
},

MISSING {
@Override
public boolean matches(String className, ClassLoader classLoader) {
return !isPresent(className, classLoader);
}
};

private 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);
}

public abstract boolean matches(String className, ClassLoader classLoader);
}

判断完之后,只要missing集合不为空,那就表示待判断的类中有不存在的,就返回条件不匹配的ConditionOutCome对象,否则就返回条件匹配的ConditionOutCome对象。