<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Spring Boot 中的 @EnableAutoConfiguration 是如何處理的?

          共 10824字,需瀏覽 22分鐘

           ·

          2020-07-31 17:18

          點(diǎn)擊上方藍(lán)色“程序猿DD”,選擇“設(shè)為星標(biāo)”

          回復(fù)“資源”獲取獨(dú)家整理的學(xué)習(xí)資料!

          作者 | 溫安適

          來源 |?https://my.oschina.net/floor/blog/4354771
          贈書:聊聊「分布式架構(gòu)」那些事兒

          引言

          工作中,我們直接或間接的,用到@EnableAutoConfiguration注解。

          今天,我們就聊聊@EnableAutoConfiguration的處理邏輯。

          找核心方法

          @Enable開頭的的注解,上一般都有@Import用于指定要注解的邏輯實(shí)現(xiàn)類。

          @EnableAutoConfiguration上的@Import,導(dǎo)入的是 AutoConfigurationImportSelector。

          AutoConfigurationImportSelector就是要找的入口類。 類關(guān)系如下:

          img

          Aware系列都是用于注入響應(yīng)的資源,Ordered用于排序。

          值得關(guān)注的是 DeferredImportSelector,查看其類注釋,簡要翻譯如下:

          importselector的變體,在所有@Configuration bean之后運(yùn)行,可以實(shí)現(xiàn)Ordered進(jìn)行排序。

          提供{getImportGroup(),它可以跨不同的選擇器提供額外的排序和過濾邏輯。

          DeferredImportSelector保證在所有@Configuration加載之后執(zhí)行,也就說,如果有相關(guān)配置類已加載,則可以跳過自動裝配類。

          DeferredImportSelector是如何保證在@Configuration bean加載之后執(zhí)行的呢???

          帶著這個疑問,我查看了ConfigurationClassPostProcessor#processConfigBeanDefinitions

          (至于為什么要查看這個方法,請看上一篇 @Enable驅(qū)動原理)

          瀏覽過程如下:

          img

          概要邏輯如下:

          \1. ImportSelector的解析在ConfigurationClassParser#processImports中處理

          在其中this.deferredImportSelectorHandler.handle(..)j將DeferredImportSelector放入隊列,延后處理。

          \2. DeferredImportSelector處理邏輯在

          ConfigurationClassParser#parse中的this.deferredImportSelectorHandler.process()中。

          瀏覽this.deferredImportSelectorHandler.process()代碼;

          img

          DeferredImportSelectorGrouping#getImports的代碼如下:

          private?static?class?DeferredImportSelectorGrouping?{
          ????private?final?DeferredImportSelector.Group?group;
          ???public?Iterable?getImports()?{
          ???for?(DeferredImportSelectorHolder?deferredImport?:?this.deferredImports)?{
          ??????this.group.process(deferredImport.getConfigurationClass().getMetadata(),
          ????????????deferredImport.getImportSelector());
          ???}
          ???return?this.group.selectImports();
          }?????
          }?
          }

          這里需要關(guān)注的是this.group.process,this.group.selectImports2個方法。

          也就是AutoConfigurationImportSelector.AutoConfigurationGroup的process,selectImports就是我們需要關(guān)注的核心方法。

          逐個分析

          process

          public?class?AutoConfigurationImportSelector?{
          private?static?class?AutoConfigurationGroup{
          ????//省略其他代碼
          ??private?final?Map?entries?=?new?LinkedHashMap<>();??
          @Override
          public?void?process(AnnotationMetadata?annotationMetadata,?DeferredImportSelector?deferredImportSelector)?{
          ???Assert.state(deferredImportSelector?instanceof?AutoConfigurationImportSelector,
          ?????????()?->?String.format("Only?%s?implementations?are?supported,?got?%s",
          ???????????????AutoConfigurationImportSelector.class.getSimpleName(),
          ???????????????deferredImportSelector.getClass().getName()));
          ???AutoConfigurationEntry?autoConfigurationEntry?=?
          ???((AutoConfigurationImportSelector)?deferredImportSelector)
          ?????????.getAutoConfigurationEntry(getAutoConfigurationMetadata(),
          ??????????????????????????annotationMetadata);
          ???this.autoConfigurationEntries.add(autoConfigurationEntry);
          ???for?(String?importClassName?:?autoConfigurationEntry.getConfigurations())?{
          ??????this.entries.putIfAbsent(importClassName,?annotationMetadata);
          ???}
          }
          }

          概要邏輯:

          1.getAutoConfigurationMetadata() 加載autoConfigurationMetadata,

          2.getAutoConfigurationEntry()根據(jù)autoConfigurationMetadata獲得AutoConfigurationEntry

          3.通過AutoConfigurationEntry ,獲得要導(dǎo)入的類的名稱,存入內(nèi)部的 autoConfigurationEntries中

          1. getAutoConfigurationMetadata() 加載autoConfigurationMetadata

          public?class?AutoConfigurationImportSelector?{
          private?static?class?AutoConfigurationGroup{
          private?AutoConfigurationMetadata?getAutoConfigurationMetadata()?{
          ???if?(this.autoConfigurationMetadata?==?null)?{
          ??????this.autoConfigurationMetadata?=?AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
          ???}
          ???return?this.autoConfigurationMetadata;
          }
          }
          }

          核心就是AutoConfigurationMetadataLoader.loadMetadata 查看其源碼如下:

          final?class?AutoConfigurationMetadataLoader?{
          ???protected?static?final?String?PATH
          ????????????=?"META-INF/"?+?"spring-autoconfigure-metadata.properties";
          ???private?AutoConfigurationMetadataLoader()?{
          ???}
          ???public?static?AutoConfigurationMetadata?loadMetadata
          ???????????????????????????????????(ClassLoader?classLoader)?{
          ??????return?loadMetadata(classLoader,?PATH);
          ???}
          ???static?AutoConfigurationMetadata?loadMetadata
          ???????????????????????(ClassLoader?classLoader,?String?path)?{
          ??????try?{
          ?????????Enumeration?urls?=?(classLoader?!=?null)?
          ???????????????????????????classLoader.getResources(path)
          ???????????????????????????:?ClassLoader.getSystemResources(path);
          ?????????Properties?properties?=?new?Properties();
          ?????????while?(urls.hasMoreElements())?{
          ????????????properties.putAll(
          ????????????????PropertiesLoaderUtils.loadProperties(
          ????????????????????????????????new?UrlResource(urls.nextElement())));
          ?????????}
          ?????????return?loadMetadata(properties);
          ??????}
          ??????catch?(IOException?ex)?{
          ?????????throw?new
          ??????????IllegalArgumentException(
          ?????????????"Unable?to?load?@ConditionalOnClass?location?["?+?path?+?"]",
          ??????????????????????????????????????????????????????????????????????ex);
          ??????}
          ???}
          ???//省略部分代碼
          ?}???

          加載autoConfigurationMetadata,就是讀取META-INF/spring-autoconfigure-metadata.properties的配置文件,轉(zhuǎn)換為autoConfigurationMetadata對象。

          2.根據(jù)autoConfigurationMetadata獲得AutoConfigurationEntry

          getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata)的實(shí)現(xiàn)如下

          public?class?AutoConfigurationImportSelector{
          ????//省略其他代碼
          protected?AutoConfigurationEntry?getAutoConfigurationEntry(AutoConfigurationMetadata?autoConfigurationMetadata,
          ??????AnnotationMetadata?annotationMetadata)?{
          ???if?(!isEnabled(annotationMetadata))?{
          ??????return?EMPTY_ENTRY;
          ???}
          ???AnnotationAttributes?attributes?=?getAttributes(annotationMetadata);
          ???List?configurations?=?getCandidateConfigurations(annotationMetadata,?attributes);
          ???configurations?=?removeDuplicates(configurations);
          ???Set?exclusions?=?getExclusions(annotationMetadata,?attributes);
          ???checkExcludedClasses(configurations,?exclusions);
          ???configurations.removeAll(exclusions);
          ???configurations?=?filter(configurations,?autoConfigurationMetadata);
          ???fireAutoConfigurationImportEvents(configurations,?exclusions);
          ???return?new?AutoConfigurationEntry(configurations,?exclusions);
          }
          }

          概要邏輯:

          • getAttributes獲取注解的屬性
          • getCandidateConfiguration獲取候選裝配組件
          • removeDuplicates刪除重復(fù)的配置項
          • getExclusions 獲取排除的組件
          • spring.autoconfigure.exclude,exclude,excludeName對應(yīng)的值存儲到set中
          • checkExcludedClasses 當(dāng)前類在classLoader中,但是不在候選列表中內(nèi)拋出異常
          • configurations.removeAll(exclusions)移除需要排除的配置項
          • filter 過濾不滿足條件的自動裝配組件
          • fireAutoConfigurationImportEvents 發(fā)送@EnableAutoConfiguration的自動裝配事件

          getCandidateConfiguration獲取候選裝配組件

          protected?List?getCandidateConfigurations(AnnotationMetadata?metadata,?AnnotationAttributes?attributes)?{
          ???List?configurations?=?SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
          ?????????getBeanClassLoader());
          ???Assert.notEmpty(configurations,?"No?auto?configuration?classes?found?in?META-INF/spring.factories.?If?you?"
          ?????????+?"are?using?a?custom?packaging,?make?sure?that?file?is?correct.");
          ???return?configurations;
          }

          查看 SpringFactoriesLoader源碼如下:

          public?final?class?SpringFactoriesLoader?{
          ????//省略部分代碼
          ?public?static?final?String?FACTORIES_RESOURCE_LOCATION?
          ?????????????????????????????????????=?"META-INF/spring.factories";
          ?
          ?public?static?List?loadFactoryNames(
          ?????????????Class?factoryClass,?@Nullable?ClassLoader?classLoader)?{
          ???String?factoryClassName?=?factoryClass.getName();
          ???return?loadSpringFactories(classLoader)
          ???????????????????.getOrDefault(factoryClassName,?Collections.emptyList());
          }

          private?static?Map>?loadSpringFactories(
          ????????????????????????????????@Nullable?ClassLoader?classLoader)?{
          ???MultiValueMap?result?=?cache.get(classLoader);
          ???if?(result?!=?null)?{
          ??????return?result;
          ???}
          ???try?{
          ??????Enumeration?urls?=?(classLoader?!=?null??
          ????????????classLoader.getResources(FACTORIES_RESOURCE_LOCATION)?:
          ????????????ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
          ??????result?=?new?LinkedMultiValueMap<>();
          ??????while?(urls.hasMoreElements())?{
          ?????????URL?url?=?urls.nextElement();
          ?????????UrlResource?resource?=?new?UrlResource(url);
          ?????????Properties?properties?=?PropertiesLoaderUtils
          ?????????????????????????????????????????.loadProperties(resource);
          ?????????for?(Map.Entry?entry?:?properties.entrySet())?{
          ????????????String?factoryClassName?=?((String)?entry.getKey()).trim();
          ????????????for?(String?factoryName?:?StringUtils
          ????????????????????????.commaDelimitedListToStringArray(
          ????????????????????????????????????(String)?entry.getValue()))?{
          ???????????????result.add(factoryClassName,?factoryName.trim());
          ????????????}
          ?????????}
          ??????}
          ??????cache.put(classLoader,?result);
          ??????return?result;
          ???}
          ???catch?(IOException?ex)?{
          ??????throw?new?IllegalArgumentException(
          ??????????????????"Unable?to?load?factories?from?location?["?+
          ????????????????????????????FACTORIES_RESOURCE_LOCATION?+?"]",?ex);
          ???}
          }????????????????
          }????

          讀取指定ClassLoader下的所有的,META-INF/spring.factories資源內(nèi)容,合并一個key為全類名,Value為實(shí)現(xiàn)類名列表的Map,從Map中找到指定的key對應(yīng)的實(shí)現(xiàn)類全類名列表

          filter 過濾不滿足條件的自動裝配組件

          public?class?AutoConfigurationImportSelector{
          private?List?filter(List?configurations,?AutoConfigurationMetadata?autoConfigurationMetadata)?{
          ???long?startTime?=?System.nanoTime();
          ???String[]?candidates?=?StringUtils.toStringArray(configurations);
          ???boolean[]?skip?=?new?boolean[candidates.length];
          ???boolean?skipped?=?false;
          ???for?(AutoConfigurationImportFilter?filter?:?getAutoConfigurationImportFilters())?{
          ??????invokeAwareMethods(filter);
          ??????boolean[]?match?=?filter.match(candidates,?autoConfigurationMetadata);
          ??????for?(int?i?=?0;?i??????????if?(!match[i])?{
          ????????????skip[i]?=?true;
          ????????????candidates[i]?=?null;
          ????????????skipped?=?true;
          ?????????}
          ??????}
          ???}
          ???if?(!skipped)?{
          ??????return?configurations;
          ???}
          ???List?result?=?new?ArrayList<>(candidates.length);
          ???for?(int?i?=?0;?i???????if?(!skip[i])?{
          ?????????result.add(candidates[i]);
          ??????}
          ???}
          ???if?(logger.isTraceEnabled())?{
          ??????int?numberFiltered?=?configurations.size()?-?result.size();
          ??????logger.trace("Filtered?"?+?numberFiltered?+?"?auto?configuration?class?in?"
          ????????????+?TimeUnit.NANOSECONDS.toMillis(System.nanoTime()?-?startTime)?+?"?ms");
          ???}
          ???return?new?ArrayList<>(result);
          }
          }

          AutoConfigurationImportFilter#match沒有匹配上的過濾掉

          查看getAutoConfigurationImportFilters()源碼如下:

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

          SpringFactoriesLoader.loadFactories(...),調(diào)用SpringFactoriesLoader.loadFactoryNames(...)后,將列表中的逐個實(shí)例化,排序后返回。

          查看spring.factories

          img

          AutoConfigurationImportFilter的實(shí)現(xiàn)類,有

          OnClassCondition類,onBeanCondition,OnWebApplicationCondition等。

          擴(kuò)展-查閱OnClassCondition的match方法

          img

          OnClassCondition代碼大致邏輯如下:

          OnClassCondition#match調(diào)用了autoConfigurationMetadata.getSet獲取當(dāng)前配置類的ConditionOnClass屬性。

          舉例說明OnClassCondition#getOutcomes

          例如:

          加載AConfigurationClass,AConfigurationClass的ConditionOnClass=XXX,

          如果XXX不在當(dāng)前classloader下,排除 AConfigurationClass

          selectImports

          public?class?AutoConfigurationImportSelector{
          private?static?class?AutoConfigurationGroup{
          @Override
          public?Iterable?selectImports()?{
          ???if?(this.autoConfigurationEntries.isEmpty())?{
          ??????return?Collections.emptyList();
          ???}
          ???Set?allExclusions?=?this.autoConfigurationEntries.stream()
          ?????????.map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet());
          ???Set?processedConfigurations?=?
          ???this.autoConfigurationEntries.stream()
          ?????????.map(AutoConfigurationEntry::getConfigurations)
          ?????????.flatMap(Collection::stream)
          ?????????.collect(Collectors.toCollection(LinkedHashSet::new));
          ???processedConfigurations.removeAll(allExclusions);

          ???return?sortAutoConfigurations(processedConfigurations,?getAutoConfigurationMetadata()).stream()
          ?????????.map((importClassName)?->?new?Entry(this.entries.get(importClassName),?importClassName))
          ?????????.collect(Collectors.toList());
          }
          }
          }
          }
          }

          概要邏輯如下

          排除不必要的項

          sortAutoConfigurations排序自動配置項。

          查看sortAutoConfigurations

          public?class?AutoConfigurationImportSelector{
          private?static?class?AutoConfigurationGroup{
          private?List?sortAutoConfigurations(Set?configurations,
          ??????AutoConfigurationMetadata?autoConfigurationMetadata)?{
          ???return?new?AutoConfigurationSorter(getMetadataReaderFactory(),?autoConfigurationMetadata)
          ?????????.getInPriorityOrder(configurations);
          }
          }
          }

          核心在AutoConfigurationSorter#getInPriorityOrder中

          class?AutoConfigurationSorter?{
          ????//省略其他代碼
          public?List?getInPriorityOrder(Collection?classNames)?{
          ???AutoConfigurationClasses?classes?=?new?AutoConfigurationClasses(this.metadataReaderFactory,
          ?????????this.autoConfigurationMetadata,?classNames);
          ???List?orderedClassNames?=?new?ArrayList<>(classNames);
          ???//?Initially?sort?alphabetically
          ???Collections.sort(orderedClassNames);
          ???//?Then?sort?by?order
          ???orderedClassNames.sort((o1,?o2)?->?{
          ??????int?i1?=?classes.get(o1).getOrder();
          ??????int?i2?=?classes.get(o2).getOrder();
          ??????return?Integer.compare(i1,?i2);
          ???});
          ???//?Then?respect?@AutoConfigureBefore?@AutoConfigureAfter
          ???orderedClassNames?=?sortByAnnotation(classes,?orderedClassNames);
          ???return?orderedClassNames;
          }
          }

          排序規(guī)則如下:

          按照字母順序加載,否則先按AutoCOnfigureOrder,再按@AutoConfigureBefore,@AutoConfigureAfter進(jìn)行排序。

          注:

          AutoCOnfigurationMetadata(META-INF/spring-autoconfigure-metadata.properties)中包含AutoConfigureOrder,AutoConfigureBefore,AutoConfigureAfter信息

          名稱注釋是否推薦
          @AutoCOnfigureOrder絕對自動裝配順序
          @AutoConfigureBefore,@AutoConfigureAfter相對自動裝配順序,建議使用name屬性

          整體流程到這里就看完了,但是自定義配置如何覆蓋自動裝配的呢?

          查漏補(bǔ)缺

          為了查找自定義配置如何覆蓋自動裝配,我再次翻閱代碼

          img

          查看shouldSkip,結(jié)合各種Conditional注解進(jìn)行過濾

          @Conditional 是個多個條件注解的元注解,例如:@ConditionalOnMissingBean,@ConditionalOnClass等。

          具體邏輯,就不在這里贅述了,如果想查閱,可以查看org.springframework.boot.autoconfigure.condition.OnClassCondition這個類,它是@ConditionalOnClass的邏輯處理類。

          總結(jié)

          1. @EnableAutoConfiguration的核心處理類是AutoConfigurationImportSelector
          2. AutoConfigurationImportSelector實(shí)現(xiàn)了DeferredImportSelector
          3. ImportSelector的解析在ConfigurationClassParser#processImports中處理在其中this.deferredImportSelectorHandler.handle(..)j將DeferredImportSelector放入隊列,延后處理。DeferredImportSelector處理邏輯在ConfigurationClassParser#parse中的this.deferredImportSelectorHandler.process()中。
          4. AutoConfigurationImportSelector.AutoConfigurationGroup的process,selectImports就是我們需要關(guān)注的核心方法。
          5. ConfigurationClassParser#doProcessConfigurationClass中的!this.conditionEvaluator.shouldSkip(...) 結(jié)合了各種Condition注解,實(shí)現(xiàn)了自定義配置覆蓋自動裝配。



          往期推薦

          BeanUtils 是用 Spring 的還是 Apache 的好?

          MySQL 如何優(yōu)化 CPU 消耗?

          Spring Boot 中 @EnableXXX 注解的驅(qū)動邏輯

          有比 ReadWriteLock更快的鎖?

          認(rèn)真看看, 以后寫 SQL 就爽多了:MyBatis 動態(tài) SQL:


          掃一掃,關(guān)注我

          一起學(xué)習(xí),一起進(jìn)步

          瀏覽 49
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  狠狠干狠狠插 | 日韩一级一 | 97电影院肏逼 | 日韩精品视频在线观看免费 | 亚洲精品乱伦视频 |