@AutoConfigureAfter和@AutoConfigureBefore排序失效了

@AutoConfigureAfter和@AutoConfigureBefore排序失效了? 正文
聊聊SpringBoot Bean加载和 自动配置的顺序问题。
实际误区 来看看实际开发中存在的问题。我们想要的结果A-->B-->C,下面写法是最常见的错误。

@Configuration public class ConfigurationA {ConfigurationA(){ System.out.println("CofigurationA已经被初始化!"); } }@Configuration @AutoConfigureAfter(ConfigurationA.class) public class ConfigurationB {ConfigurationB(){ System.out.println("ConfigurationB已经被初始化!"); } }@Configuration @AutoConfigureAfter(ConfigurationB.class) public class ConfigurationC {ConfigurationC(){ System.out.println("CofigurationC已经被初始化!"); } }

来来来执行一把。。。。
执行结果:CofigurationA已经被初始化! ConfigurationB已经被初始化! CofigurationC已经被初始化!

【@AutoConfigureAfter和@AutoConfigureBefore排序失效了】WTF!!!!!!!!不是说有问题吗?
@AutoConfigureAfter和@AutoConfigureBefore排序失效了
文章图片

别急!!调整下代码,想要的结果是C-->B-->A,来将代码调整下:
@Configuration @AutoConfigureAfter(ConfigurationB.class) public class ConfigurationA {ConfigurationA(){ System.out.println("CofigurationA已经被初始化!"); } }@Configuration @AutoConfigureAfter(ConfigurationC.class) public class ConfigurationB {ConfigurationB(){ System.out.println("ConfigurationB已经被初始化!"); } }@Configuration public class ConfigurationC {ConfigurationC(){ System.out.println("CofigurationC已经被初始化!"); } }

来来开席开席!!!!
执行结果:CofigurationA已经被初始化! ConfigurationB已经被初始化! CofigurationC已经被初始化!

从上面的结果是可以看出@AutoConfigureAfter并没有什么卵用。第一次能够得到正确结果是老天保佑。。。。。


所以来看看源码:*@AutoConfigureAfter归结来说得益于SpringBoot自动配置(@EnableAutoConfiguration); @EnableAutoConfiguration通过@Import将EnableAutoConfigurationImportSelector引入,
这边主要看下EnableAutoConfigurationImportSelector-->selectImports()方法*
public String[] selectImports(AnnotationMetadata metadata) { if (!isEnabled(metadata)) { return NO_IMPORTS; } try { AnnotationAttributes attributes = getAttributes(metadata); //加载META-INF下spring.factories配置类 List configurations = getCandidateConfigurations(metadata, attributes); configurations = removeDuplicates(configurations); Set exclusions = getExclusions(metadata, attributes); configurations.removeAll(exclusions); //将配置类进行排序,sort方法最终会调用到AutoConfigurationSorter中的getInPriorityOrder() configurations = sort(configurations); recordWithConditionEvaluationReport(configurations, exclusions); return configurations.toArray(new String[configurations.size()]); } catch (IOException ex) { throw new IllegalStateException(ex); } }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; }

看到这里法外狂徒 "张三"默默的在META-INF下spring.factories加上了.......
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.*******.Demo.ConfigurationA,\ com.*******.Demo.ConfigurationB,\ com.*******.Demo.ConfigurationC

张三:就这,就这......?张三启动了核按钮:
执行结果:CofigurationA已经被初始化! ConfigurationB已经被初始化! CofigurationC已经被初始化!

@AutoConfigureAfter和@AutoConfigureBefore排序失效了
文章图片

这里就存在另一个易错点。简单的理解,当配置类在Spring扫描路径里面(scanBasePackages)会优先解析,后面在通过ImportSelector(spring.factories加载就是通过实现这个接口加载的配置类)加载进来的配置类就不会处理了,相当于一个类有两种加载方式,谁先加载谁就厉害。。。
空口无凭上菜:ConfigurationClassParser-->doProcessConfigurationClass()方法加载顺序是@ComponentScan(扫描文件路径,路径里面元注解为@Component(@Cofiguration元注解也是@Component)都会被扫描到)--->加载@Import注解(配合ImportSelector接口)--->加载 @ImportResource--->加载@Bean--->.......
大致顺序理清楚了。
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException { // Recursively process any member (nested) classes first processMemberClasses(configClass, sourceClass); // Process any @PropertySource annotations for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), PropertySources.class, org.springframework.context.annotation.PropertySource.class)) { if (this.environment instanceof ConfigurableEnvironment) { processPropertySource(propertySource); } else { logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() + "]. Reason: Environment must implement ConfigurableEnvironment"); } }// Process any @ComponentScan annotations Set componentScans = AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class); if (!componentScans.isEmpty() && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) { for (AnnotationAttributes componentScan : componentScans) { // The config class is annotated with @ComponentScan -> perform the scan immediately //@1@Configuration注解的类会被解析到 Set scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName()); // Check the set of scanned definitions for any further config classes and parse recursively if necessary for (BeanDefinitionHolder holder : scannedBeanDefinitions) { if (ConfigurationClassUtils.checkConfigurationClassCandidate(holder.getBeanDefinition(), this.metadataReaderFactory)) { //@2对加载的类进行必要的解析 parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName()); } } } }// Process any @Import annotations processImports(configClass, sourceClass, getImports(sourceClass), true); // Process any @ImportResource annotations if (sourceClass.getMetadata().isAnnotated(ImportResource.class.getName())) { AnnotationAttributes importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class); String[] resources = importResource.getStringArray("locations"); Class readerClass = importResource.getClass("reader"); for (String resource : resources) { String resolvedResource = this.environment.resolveRequiredPlaceholders(resource); configClass.addImportedResource(resolvedResource, readerClass); } }// Process individual @Bean methods Set beanMethods = sourceClass.getMetadata().getAnnotatedMethods(Bean.class.getName()); for (MethodMetadata methodMetadata : beanMethods) { configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); }// Process default methods on interfaces processInterfaces(configClass, sourceClass); // Process superclass, if any if (sourceClass.getMetadata().hasSuperClass()) { String superclass = sourceClass.getMetadata().getSuperClassName(); if (!superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) { this.knownSuperclasses.put(superclass, configClass); // Superclass found, return its annotation metadata and recurse return sourceClass.getSuperClass(); } }// No superclass -> processing is complete return null; }

@1会将@Configurtion注解扫描处理 @2会将@1扫描处理的类进行必要的加工,@2最后会调用到ConfigurationClassParser-->processConfigurationClass()方法
这个方法里会将已经解析的类放入configurationClasses缓存中,当相同类通过不同的方法解析的时候会进行合并或者跳过。
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException { if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) { return; }ConfigurationClass existingClass = this.configurationClasses.get(configClass); if (existingClass != null) { if (configClass.isImported()) { if (existingClass.isImported()) { existingClass.mergeImportedBy(configClass); } // Otherwise ignore new imported config class; existing non-imported class overrides it. return; } else { //省略 } } }// Recursively process the configuration class and its superclass hierarchy. SourceClass sourceClass = asSourceClass(configClass); do { sourceClass = doProcessConfigurationClass(configClass, sourceClass); } while (sourceClass != null); this.configurationClasses.put(configClass, configClass); }

来来来,那个张三你知道了吗?
张三:知道了,处理方案有两种
1.将我们的配置类放在Spring扫描路径里外面(scanBasePackages)
2.如果还是想放在扫描路径里面就需要将@Configuration注解去掉

    推荐阅读