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

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

    这是该注解的源码,可以通过value和name来指定要判断的类,而真正执行判断的逻辑在OnClassCondition类中。

    • OnClassCondition类继承了FilteringSpringBootCondition类
    • FilteringSpringBootCondition类又继承了SpringBootCondition类
    • SpringBootCondition类实现了Condition接口

    image.png

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

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

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

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

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

    1. List<String> onClasses = getCandidates(metadata, ConditionalOnClass.class);
    1. private List<String> getCandidates(AnnotatedTypeMetadata metadata,
    2. Class<?> annotationType) {
    3. MultiValueMap<String, Object> attributes =
    4. metadata.getAllAnnotationAttributes(annotationType.getName(), true);
    5. if (attributes == null) {
    6. return null;
    7. }
    8. List<String> candidates = new ArrayList<>();
    9. addAll(candidates, attributes.get("value"));
    10. addAll(candidates, attributes.get("name"));
    11. return candidates;
    12. }

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

    1. List<String> missing = filter(onClasses, ClassNameFilter.MISSING, classLoader);
    1. protected final List<String> filter(Collection<String> classNames,
    2. ClassNameFilter classNameFilter,
    3. ClassLoader classLoader) {
    4. if (CollectionUtils.isEmpty(classNames)) {
    5. return Collections.emptyList();
    6. }
    7. List<String> matches = new ArrayList<>(classNames.size());
    8. for (String candidate : classNames) {
    9. if (classNameFilter.matches(candidate, classLoader)) {
    10. matches.add(candidate);
    11. }
    12. }
    13. return matches;
    14. }

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

    1. protected enum ClassNameFilter {
    2. // ...
    3. MISSING {
    4. @Override
    5. public boolean matches(String className, ClassLoader classLoader) {
    6. return !isPresent(className, classLoader);
    7. }
    8. };
    9. static boolean isPresent(String className, ClassLoader classLoader) {
    10. if (classLoader == null) {
    11. classLoader = ClassUtils.getDefaultClassLoader();
    12. }
    13. try {
    14. resolve(className, classLoader);
    15. return true;
    16. }
    17. catch (Throwable ex) {
    18. return false;
    19. }
    20. }
    21. }
    1. protected static Class<?> resolve(String className, ClassLoader classLoader)
    2. throws ClassNotFoundException {
    3. if (classLoader != null) {
    4. return Class.forName(className, false, classLoader);
    5. }
    6. return Class.forName(className);
    7. }

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

    这就是@ConditionalOnClass注解的核心源码流程,期待你的点赞哦。