SpringBoot 源碼分析 - 自動配置深度分析三
- refresh和自動配置大致流程
- AutoConfigurationImportSelector的getAutoConfigurationEntry獲取自動配置實體(重點)
- AutoConfigurationImportSelector的getCandidateConfigurations獲取EnableAutoConfiguration類型的名字集合
- AutoConfigurationImportSelector的removeDuplicates去重
- AutoConfigurationImportSelector的getExclusions獲取要排除的
- AutoConfigurationImportSelector的checkExcludedClasses檢查要排除的
- AutoConfigurationImportSelector的filter過濾
refresh和自動配置大致流程
AutoConfigurationImportSelector的getAutoConfigurationEntry獲取自動配置實體(重點)
前面講了那么多,都是為了這里啊,不然直接上來都不知道是怎么來的,我們來看看這個干了什么,別看就那么點,其實里面很復雜,簡單的說就是從我們初始化加載的所有的jar
包下的META-INF/spring.factories
屬性中找到org.springframework.boot.autoconfigure.EnableAutoConfiguration
屬性,其實這個時候以及有緩存啦,前面初始化的時候已經全加載過了。然后進行去重,再獲取要排除的名字,檢查排除的類的合理性,然后排除,再進行條件類過濾,因為可能有些配置類缺少某些類就不能用了,觸發自動裝配導入事件,最后封裝成AutoConfigurationEntry
返回。
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,AnnotationMetadata annotationMetadata) {if (!isEnabled(annotationMetadata)) {return EMPTY_ENTRY;}AnnotationAttributes attributes = getAttributes(annotationMetadata);List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);//獲取自動裝配類configurations = removeDuplicates(configurations);//去重Set<String> exclusions = getExclusions(annotationMetadata, attributes);//獲取要排除的checkExcludedClasses(configurations, exclusions);//檢查要排除的configurations.removeAll(exclusions);//排除configurations = filter(configurations, autoConfigurationMetadata);//過濾fireAutoConfigurationImportEvents(configurations, exclusions);//觸發導入事件return new AutoConfigurationEntry(configurations, exclusions);}
主要自動配置類都在這里,當然我們可以自定義,后面我們會自定義一個:
AutoConfigurationImportSelector的getCandidateConfigurations獲取EnableAutoConfiguration類型的名字集合
這個代碼我們前面很熟悉啊,就不多說了,從緩存中加載對應EnableAutoConfiguration
類型的類名字,最終加載了124
個,好多啊,沒關系,后面過濾就沒那么多了。
AutoConfigurationImportSelector的removeDuplicates去重
這個很巧妙,放進Set
里又拿出來放進List
里:
protected final <T> List<T> removeDuplicates(List<T> list) {return new ArrayList<>(new LinkedHashSet<>(list));}
AutoConfigurationImportSelector的getExclusions獲取要排除的
其實就是從注解屬性的exclude
和excludeName
獲取,當然還有個環境配置屬性spring.autoconfigure.exclude
也可以,就是說可以在yml
或者propertise
里配啦,其他就不多說了。
AutoConfigurationImportSelector的checkExcludedClasses檢查要排除的
其實就是看下要排除的在不在自動配置集合里,有不在的就報異常,可能要排除的并不是自動配置的類,表示無效排除:
private void checkExcludedClasses(List<String> configurations, Set<String> exclusions) {List<String> invalidExcludes = new ArrayList<>(exclusions.size());for (String exclusion : exclusions) {if (ClassUtils.isPresent(exclusion, getClass().getClassLoader()) && !configurations.contains(exclusion)) {invalidExcludes.add(exclusion);//不在自動配置類里,還要排除的,屬于無效排除,要拋異常}}if (!invalidExcludes.isEmpty()) {handleInvalidExcludes(invalidExcludes);}}protected void handleInvalidExcludes(List<String> invalidExcludes) {StringBuilder message = new StringBuilder();for (String exclude : invalidExcludes) {message.append("\t- ").append(exclude).append(String.format("%n"));}throw new IllegalStateException(String.format("The following classes could not be excluded because they are not auto-configuration classes:%n%s",message));}
AutoConfigurationImportSelector的filter過濾
這里會獲取過濾器,其實就是OnClassCondition
,OnWebApplicationCondition
,OnBeanCondition
這幾個條件,他會去配置類的注解上查找相應的條件類是否存在,不存在就會被過濾掉,過濾的時候可能會開啟線程,幫助一起處理,因為配置類數量多。如果多核的話,會用啟動一個線程去分擔一半數量的檢查,會判斷條件類是否能加載到,不能就被過濾掉了,如果用多個線程可能效果不太好,spring
團隊應該做過實驗,2
個最好。
private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) {long startTime = System.nanoTime();// 轉換自動配置類的List集合為String數組String[] candidates = StringUtils.toStringArray(configurations);// 存儲最終的匹配結果,和candidates數組索引位置一一對應,true代表最終需要// 自動引入,false代表不需要自動引入boolean[] skip = new boolean[candidates.length];boolean skipped = false;// <AutoConfigurationImportSelector#filter_1>見詳細講解for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {// <AutoConfigurationImportSelector#filter_2>見詳細講解invokeAwareMethods(filter);// <AutoConfigurationImportSelector#filter_3>見詳細講解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直接返回,否則執行后續邏輯,通過布爾數組進行過濾if (!skipped) {return configurations;}// 存儲過濾后需要自動配置的類List<String> result = new ArrayList<>(candidates.length);for (int i = 0; i < candidates.length; i++) {// 如果當前位置為fasle則說明不需要跳過,則添加到最終結果中if (!skip[i]) {result.add(candidates[i]);}}// <AutoConfigurationImportSelector#filter_4>見詳細講解if (logger.isTraceEnabled()) {int numberFiltered = configurations.size() - result.size();logger.trace("Filtered " + numberFiltered + " auto configuration class in "+ TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms");}// 返回結果,不知道為什么要再重新new一個return new ArrayList<>(result);
}