@Configuration 配置类排序

(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 的顺序即可

原文地址:https://www.cnblogs.com/kevin-yuan/p/12431791.html