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