(version: SpringBoot 2.2.2.RELEASE)
SpringBoot 會對 spring.factories 中的 @Configuration 類進行排序。
注意:只是對所有 spring.factories 中的 @Configuratin 類排序(也就是通常使用的 starter 里面的配置)
排序使用的注解有:@AutoConfigureOrder、@AutoConfigureBefore、@AutoConfigureAfter
也就是說,@AutoConfigureOrder、@AutoConfigureBefore、@AutoConfigureAfter 這三個注解只對 spring.factories 中的 @Configuration 類生效
org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorGrouping#getImports()
1 public Iterable<Group.Entry> getImports() { 2 for (DeferredImportSelectorHolder deferredImport : this.deferredImports) { 3 // 1. 通過 ImportSelector 加載所有待解析的 @Configuration 類 4 this.group.process(deferredImport.getConfigurationClass().getMetadata(),deferredImport.getImportSelector()); 5 } 6 // 2. 獲取排序過的 @Configuration 類 7 return this.group.selectImports(); 8 }
1. org.springframework.context.annotation.DeferredImportSelector.Group#process() 1.1 org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getAutoConfigurationEntry() 1.1.1 org.springframework.core.io.support.SpringFactoriesLoader#loadFactoryNames() // 加載所有 spring.factories 中的類 2. org.springframework.context.annotation.DeferredImportSelector.Group#selectImports() 2.1 org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#sortAutoConfigurations() // 對 spring.factories 中的 @Configuration 類進行排序 2.1.1 org.springframework.boot.autoconfigure.AutoConfigurationSorter#getInPriorityOrder() // 對 @Configuration 類進行排序
org.springframework.boot.autoconfigure.AutoConfigurationSorter#getInPriorityOrder()
1 List<String> getInPriorityOrder(Collection<String> classNames) { 2 AutoConfigurationClasses classes = new AutoConfigurationClasses(this.metadataReaderFactory, 3 this.autoConfigurationMetadata, classNames); 4 List<String> orderedClassNames = new ArrayList<>(classNames); 5 // Initially sort alphabetically 6 // 1. 按字母順序排 7 Collections.sort(orderedClassNames); 8 // Then sort by order 9 // 2. 按 @AutoConfigureOrder 排 10 orderedClassNames.sort((o1, o2) -> { 11 int i1 = classes.get(o1).getOrder(); 12 int i2 = classes.get(o2).getOrder(); 13 return Integer.compare(i1, i2); 14 }); 15 // Then respect @AutoConfigureBefore @AutoConfigureAfter 16 // 3. 按 @AutoConfigureBefore @AutoConfigureAfter 排 17 orderedClassNames = sortByAnnotation(classes, orderedClassNames); 18 return orderedClassNames; 19 }
場景:
兩個配置類 AConfiguration 和 BConfiguration 都在 spring.factories 中,且兩個配置類中都會去加載同一個 bean ServiceA,且 ServiceA 是按條件加載的(@ConditionalOnMissingBean(ServiceA.class))。現在 AConfiguration 中的 ServiceA 總是優先處理,從而加載了 AConfiguration 中的 ServiceA。而我現在想讓 BConfiguration 中的 ServiceA 優先注冊。
解決辦法:
通過 @AutoConfigureOrder、@AutoConfigureBefore、@AutoConfigureAfter 來調整 BConfiguration 的處理順序,讓它優先處理
實際場景:(springboot 多 context-path 支持時遇到的問題)
spring-boot-autoconfigure-2.2.2.RELEASE.jar 會自動加載 ServletWebServerFactoryAutoConfiguration,它會去加載配置類 ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,從而初始化一個 TomcatServletWebServerFactory 的 bean,它是帶條件加載的:@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)。
而我現在要在 FwEndpointConfiguration 自定義一個 TomcatServletWebServerFactory,按道理 spring-boot-autoconfigure-2.2.2.RELEASE.jar 中的 TomcatServletWebServerFactory 不應該被注冊,因為我自定義了一個 TomcatServletWebServerFactory,但是現實是往容器中注冊了兩個 TomcatServletWebServerFactory,最后報錯了。
原因是:
ServletWebServerFactoryAutoConfiguration 配置類的處理優先級高於 FwEndpointConfiguration,也就是說 EmbeddedTomcat 中的 TomcatServletWebServerFactory 往容器中注冊時,發現容器中還沒有 TomcatServletWebServerFactory 的 BeanDefinition,所以它就注冊進去了。
解決辦法:
將自定義的 FwEndpointConfiguration 的優先級調在 ServletWebServerFactoryAutoConfiguration 之前
注意:
ServletWebServerFactoryAutoConfiguration 本身是通過 spring.factories 加載的
前提是 FwEndpointConfiguration 在 spring.factories 中,才可以調整 FwEndpointConfiguration 的處理順序。
如果 FwEndpointConfiguration 不在 spring.factories 中,但是它是通過 spring.factories 中的 XxCofiguration 間接導入的,則需要調整 XxCofiguration 的順序即可
