SpringBoot中ConditionalOnClass注解的原理


SpringBoot中的自動配置類有很多ConditionalOnClass注解,@ConditionalOnClass 在注解值中所有的類都存在時(通過嘗試使用類加載器加載指定的類的方式判斷)才會匹配,

那這些ConditionalOnClass注解的原理是什么呢,了解ConditionalOnClass注解的原理前要先了解Conditional注解的原理,因為Conditional注解是最基礎的

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
    Class<? extends Condition>[] value();
}

Contional注解里可以標注的屬性是一個Class數組

而處理這個注解的是一個Condition接口,SpringBootCondition就實現了這個接口來對Contional注解進行處理

@FunctionalInterface
public interface Condition {
    boolean matches(ConditionContext var1, AnnotatedTypeMetadata var2);
}
public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String classOrMethodName = getClassOrMethodName(metadata);

try {
//獲取ContionOutCome對象,這個對象的屬性有是否匹配和匹配信息
ConditionOutcome outcome = this.getMatchOutcome(context, metadata);//getMatchOutcome方法在本類是一個抽象方法,具體實現是在子類實現的,OnClassCondition就實現了這個方法
this.logOutcome(classOrMethodName, outcome);
this.recordEvaluation(context, classOrMethodName, outcome);
return outcome.isMatch();//返回匹配結果 true或者是False
} 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);
}
}

 

了解完Conditional注解的原理之后就可以來了解ConditionalOnClass注解的原理了,可以先看下ConditionalOnClass注解的定義

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({OnClassCondition.class})
public @interface ConditionalOnClass {
    Class<?>[] value() default {};

    String[] name() default {};
}

可以看到ConditionalOnClass注解其實是通過Conditional注解起作用的,Conditional注解標注的屬性是OnClassCondition.class,接着來看OnClassCondition.class的源碼,從源碼可以看到OnClassCondition.class是通過getMatchOutCome來獲取匹配結果的,

而匹配結果是在SpringBootCondition里被使用的

public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
          //獲取容器的類加載器
        ClassLoader classLoader = context.getClassLoader();
        ConditionMessage matchMessage = ConditionMessage.empty();
    // 獲取@ConditionalOnClass注解 value以及name屬性聲明的所有類
        List<String> onClasses = this.getCandidates(metadata, ConditionalOnClass.class);
        List onMissingClasses;
        if (onClasses != null) {
            //加載ConditionalOnClass注解指定的類
            onMissingClasses = this.filter(onClasses, ClassNameFilter.MISSING, classLoader);
            // 如果加載成功即類路徑上有ConditionalOnClasses注解指定的類,也就是說onMissingClasses為空,加載失敗即onMissingClasses不為空,返回一個匹配失敗的結果
// 這個if執行的話表明加載失敗 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) { //加載ConditionalOnMissingClass注解指定的類 List<String> present = this.filter(onMissingClasses, ClassNameFilter.PRESENT, classLoader); // 如果加載失敗即類路徑上沒有ConditionalOnMissingClasses注解指定的類,也就是說present為空,加載成功即present不為空,返回一個匹配失敗的結果
// 這個if執行的話表明加載失敗 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);//返回匹配的結果 }

public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String classOrMethodName = getClassOrMethodName(metadata);

try {
ConditionOutcome outcome = this.getMatchOutcome(context, metadata);//上面方法返回的匹配結果是在這里使用的
this.logOutcome(classOrMethodName, outcome);
this.recordEvaluation(context, classOrMethodName, outcome);
return outcome.isMatch();//匹配成功則返回true,把ConditionalOnClass標記的類加入容器中,匹配失敗則跳過標注的類
} 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);
}
}
 

可以看出getMatchOutCome方法里是通過filter方法來判斷類路徑上是否有ConditionalOnClass注解里標注的類

protected final List<String> filter(Collection<String> classNames, FilteringSpringBootCondition.ClassNameFilter classNameFilter, ClassLoader classLoader) {
        if (CollectionUtils.isEmpty(classNames)) {
            return Collections.emptyList();
        } else {
            List<String> matches = new ArrayList(classNames.size());
            Iterator var5 = classNames.iterator();

            while(var5.hasNext()) {
                String candidate = (String)var5.next();
                //classNameFilter為MISSING時,如果加載到了 
                //ConditionalOnClass指定的類,則matches的返回值為flase,則不會把ConditionalOnClass注解指定的類加入到matches,即matches為空
                if (classNameFilter.matches(candidate, classLoader)) {
                    matches.add(candidate);
                }
            }

            return matches;
        }
    }
filter方法是通過ClassNameFilter這個枚舉類里的matches方法來判斷類路徑上是否有ConditionalOnClass注解里標注的類
protected static enum ClassNameFilter {
    //判斷類路徑中是否存在該類,該類能被加載則返回True,否則返回False
        PRESENT {
            public boolean matches(String className, ClassLoader classLoader) {
                return isPresent(className, classLoader);
            }
        },
    //判斷類路徑中是否不存在該類,該類能被加載則返回False,否則返回True
        MISSING {
            public boolean matches(String className, ClassLoader classLoader) {
                return !isPresent(className, classLoader);
            }
        };

        private ClassNameFilter() {
        }

        abstract boolean matches(String var1, ClassLoader var2);

        static boolean isPresent(String className, ClassLoader classLoader) {
            if (classLoader == null) {
                classLoader = ClassUtils.getDefaultClassLoader();
            }

            try {
                //利用loadClass以及forName方法,判斷類路徑下有沒有這個指定的類
                //有則返回true
                FilteringSpringBootCondition.resolve(className, classLoader);
                return true;
            } catch (Throwable var3) {
                //沒有則加載失敗返回false
                return false;
            }
        }
    }
}

protected static Class<?> resolve(String className, ClassLoader classLoader) throws ClassNotFoundException {
return classLoader != null ? classLoader.loadClass(className) : Class.forName(className);//有類加載器就通過類加載器加載,沒有則通過forName方法加載
}

看到這里我們就可以知道ConditionalOnClass注解里的類屬性是通過類加載器和forName的方法判斷類路徑上是否有該類,如果類路徑上有該類則加載成功,也就是能夠成功匹配,成功匹配后SpringBoot就會把ConditionalOnClass注解標記的類加入到容器中



 
       


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM