天天看點

(二)AutoConfigurationImportFilter過濾自動配置元件AutoConfigurationImportFilter的使用AutoConfigurationImportFilter過濾基本流程參考

目錄

  • AutoConfigurationImportFilter的使用
  • AutoConfigurationImportFilter過濾基本流程
    • 1.擷取META-INF/spring.factories中配置的所有過濾器
    • 2.周遊所有過濾器,并執行其match方法
    • 3.拼接自動配置類名和注解名為特定字元
    • 4.根據字元串查詢中繼資料中配置的類
    • 5.嘗試用類加載器加載該類
    • 6.加載成功則比對成功,抛出異常則比對失敗
  • 參考

AutoConfigurationImportFilter的使用

在AutoConfigurationImportSelector實作的selectImports方法中會通過AutoConfigurationImportFilter的相關接口實作來進行自動配置類的過濾

private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) {
    long startTime = System.nanoTime();
    String[] candidates = StringUtils.toStringArray(configurations);
    boolean[] skip = new boolean[candidates.length];
    boolean skipped = false;
    //擷取META-INF/spring.factories中配置的所有過濾器
    Iterator var8 = this.getAutoConfigurationImportFilters().iterator();

    //周遊所有過濾器,并執行其match方法
    while(var8.hasNext()) {
    
        AutoConfigurationImportFilter filter = (AutoConfigurationImportFilter)var8.next();
        this.invokeAwareMethods(filter);
        boolean[] match = filter.match(candidates, autoConfigurationMetadata);

        for(int i = 0; i < match.length; ++i) {
            if (!match[i]) {
                skip[i] = true;
                candidates[i] = null;
                skipped = true;
            }
        }
    }

    if (!skipped) {
        return configurations;
    } else {
        List<String> result = new ArrayList(candidates.length);

        int numberFiltered;
        for(numberFiltered = 0; numberFiltered < candidates.length; ++numberFiltered) {
            if (!skip[numberFiltered]) {
                result.add(candidates[numberFiltered]);
            }
        }

        if (logger.isTraceEnabled()) {
            numberFiltered = configurations.size() - result.size();
            logger.trace("Filtered " + numberFiltered + " auto configuration class in " + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms");
        }

        return new ArrayList(result);
    }
}

protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
    return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);
}


           

AutoConfigurationImportFilter過濾基本流程

  1. 擷取META-INF/spring.factories中配置的所有過濾器
  2. 周遊所有過濾器,并執行其match方法
  3. 拼接自動配置類名和注解名為特定字元
  4. 根據字元串查詢中繼資料中配置的類
  5. 嘗試用類加載器加載該類
  6. 加載成功則比對成功,抛出異常則比對失敗

1.擷取META-INF/spring.factories中配置的所有過濾器

protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
    return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);
}

           

通過loadFactories方法在META-INF/spring.factories中擷取所有的篩選條件,包括:

  • OnBeanCondition
  • OnClassCondition
  • OnWebApplicationCondition

2.周遊所有過濾器,并執行其match方法

周遊到的過濾器都實作了FilteringSpringBootCondition類,而FilteringSpringBootCondition則實作了AutoConfigurationImportFilter類,其中的match方法用來判斷條件是否符合,match方法的實作方法如下:

public boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) {
    ConditionEvaluationReport report = ConditionEvaluationReport.find(this.beanFactory);
    ConditionOutcome[] outcomes = this.getOutcomes(autoConfigurationClasses, autoConfigurationMetadata);
    boolean[] match = new boolean[outcomes.length];

    for(int i = 0; i < outcomes.length; ++i) {
        match[i] = outcomes[i] == null || outcomes[i].isMatch();
        if (!match[i] && outcomes[i] != null) {
            this.logOutcome(autoConfigurationClasses[i], outcomes[i]);
            if (report != null) {
                report.recordConditionEvaluation(autoConfigurationClasses[i], this, outcomes[i]);
            }
        }
    }

    return match;
}

           

其中主要的判斷邏輯,是在getOutcomes方法中,各過濾器實作了相應的getOutcomes方法用來進行條件判斷。

3.拼接自動配置類名和注解名為特定字元

在各過濾器實作的getOutcomes方法中,對需要過濾的自動配置類進行分半處理,并且單獨提取每一個自動配置類的條件進行判斷,最後進行判斷的步驟為:

private ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses, int start, int end, AutoConfigurationMetadata autoConfigurationMetadata) {
    ConditionOutcome[] outcomes = new ConditionOutcome[end - start];

    for(int i = start; i < end; ++i) {
        String autoConfigurationClass = autoConfigurationClasses[i];
        if (autoConfigurationClass != null) {
            String candidates = autoConfigurationMetadata.get(autoConfigurationClass, "ConditionalOnClass");
            if (candidates != null) {
                outcomes[i - start] = this.getOutcome(candidates);
            }
        }
    }

    return outcomes;
}

private ConditionOutcome getOutcome(String candidates) {
    try {
        if (!candidates.contains(",")) {
            return this.getOutcome(candidates, this.beanClassLoader);
        }

        String[] var2 = StringUtils.commaDelimitedListToStringArray(candidates);
        int var3 = var2.length;

        for(int var4 = 0; var4 < var3; ++var4) {
            String candidate = var2[var4];
            ConditionOutcome outcome = this.getOutcome(candidate, this.beanClassLoader);
            if (outcome != null) {
                return outcome;
            }
        }
    } catch (Exception var7) {
    }

    return null;
}

private ConditionOutcome getOutcome(String className, ClassLoader classLoader) {
    return ClassNameFilter.MISSING.matches(className, classLoader) ? ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnClass.class, new Object[0]).didNotFind("required class").items(Style.QUOTE, new Object[]{className})) : null;
}


           

其中,通過PropertiesAutoConfigurationMetadata實作了AutoConfigurationMetadata的get方法,以拼接後的字元串作為key,從META-INF/spring-autoconfigure-metadata.properties檔案中得到該條件對應的類:

public String get(String className, String key, String defaultValue) {
   
    //拼接自動配置類名和注解名為特定字元
    //根據字元串查詢中繼資料中配置的類
    String value = this.properties.getProperty(className + "." + key);
    return value != null ? value : defaultValue;
}

           

4.根據字元串查詢中繼資料中配置的類

5.嘗試用類加載器加載該類

獲得類名稱之後,則采用ClassNameFilter.MISSING的matches方法進行判斷:

protected static enum ClassNameFilter {
    PRESENT {
        public boolean matches(String className, ClassLoader classLoader) {
            return isPresent(className, classLoader);
        }
    },
    MISSING {
        public boolean matches(String className, ClassLoader classLoader) {
            return !isPresent(className, classLoader);
        }
    };

    private ClassNameFilter() {
    }

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

    public static boolean isPresent(String className, ClassLoader classLoader) {
        if (classLoader == null) {
            classLoader = ClassUtils.getDefaultClassLoader();
        }
	
	//加載成功則比對成功,抛出異常則比對失敗
        try {
            forName(className, classLoader);
            return true;
        } catch (Throwable var3) {
            return false;
        }
    }

    //嘗試用類加載器加載該類
    private static Class<?> forName(String className, ClassLoader classLoader) throws ClassNotFoundException {
        return classLoader != null ? classLoader.loadClass(className) : Class.forName(className);
    }
}

           

6.加載成功則比對成功,抛出異常則比對失敗

參考

(《Spring Boot 技術内幕》)