加载所有的自动配置类,@EnableAutoConfiguration 注解生效时机

上一篇文章我们跟踪代码看到了加载启动类目录下的bean的定义信息的过程,今天我们先分析下系统内置的自动配置类到底是啥时候加载进来的,因为tomcat等也是通过自动配置类先创建的工厂,后面才能讨论tomcat等容器的启动

接上文this.doProcessConfigurationClass(configClass, sourceClass);方法(ConfigurationClassParser类中的方法)

在使用componentScanParser扫码完入口类所在的包路径后,
进行调用
this.processImports(configClass, sourceClass, this.getImports(sourceClass),方法

1、this.getImports(sourceClass)

先进入this.getImports方法

private Set<ConfigurationClassParser.SourceClass> getImports(ConfigurationClassParser.SourceClass sourceClass) throws IOException {
        Set<ConfigurationClassParser.SourceClass> imports = new LinkedHashSet();
        Set<ConfigurationClassParser.SourceClass> visited = new LinkedHashSet();
        this.collectImports(sourceClass, imports, visited);
        return imports;
    }

关注this.collectImports(sourceClass, imports, visited);方法,此方法是个自循环方法,非常绕

private void collectImports(ConfigurationClassParser.SourceClass sourceClass, Set<ConfigurationClassParser.SourceClass> imports, Set<ConfigurationClassParser.SourceClass> visited) throws IOException {
        if (visited.add(sourceClass)) {
            //获取当前类的所有注解信息
            Iterator var4 = sourceClass.getAnnotations().iterator();

            while(var4.hasNext()) {
                ConfigurationClassParser.SourceClass annotation = (ConfigurationClassParser.SourceClass)var4.next();
                //获取每个注解的名字
                String annName = annotation.getMetadata().getClassName();
                //如果注解不是以java开头则继续循环调用注解的注解
                if (!annName.startsWith("java") && !annName.equals(Import.class.getName())) {
                    this.collectImports(annotation, imports, visited);
                }
            }

            imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
        }

    }

上面代码中可以看到不是java开头的注解会被处理,下面看下@SpringBootConfiguration注解信息

springboot 程序启动指定配置文件 springboot配置启动类_java


看下最后一行代码

imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), “value”));最终会把符合条件的放在imports集合中(set集合),注意放入时取得是@Import注解属性得value值,最终为@EnableAutoConfiguration和@Registrar

springboot 程序启动指定配置文件 springboot配置启动类_spring_02


小结:

getImport最终返回数据

springboot 程序启动指定配置文件 springboot配置启动类_加载_03

2、this.processImports(configClass, sourceClass, this.getImports(sourceClass)

private void processImports(ConfigurationClass configClass, ConfigurationClassParser.SourceClass currentSourceClass, Collection<ConfigurationClassParser.SourceClass> importCandidates, boolean checkForCircularImports) throws IOException {
        if (!importCandidates.isEmpty()) {
            if (checkForCircularImports && this.isChainedImportOnStack(configClass)) {
                this.problemReporter.error(new ConfigurationClassParser.CircularImportProblem(configClass, this.importStack));
            } else {
                this.importStack.push(configClass);

                try {
                //遍历之前获取得sourceClass(关注 EnableAutoConfigurationImportSelector)
                    Iterator var5 = importCandidates.iterator();

                    while(true) {
                        while(true) {
                            while(var5.hasNext()) {
                                ConfigurationClassParser.SourceClass candidate = (ConfigurationClassParser.SourceClass)var5.next();
                                Class candidateClass;
                                if (candidate.isAssignable(ImportSelector.class)) {
                                    //获取sourceClass得Class信息
                                    
                                    candidateClass = candidate.loadClass();
                                    
                                    //创建对象(EnableAutoConfigurationImportSelector被创建)
                                    
                                    ImportSelector selector = (ImportSelector)BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
                                    //判断是否要延迟加载selectImports方法
                                    if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
                                    
                                    //EnableAutoConfigurationImportSelector走这里
                                    //创建对象封装selector到ConfigurationClassParser内部属性中,见下图
                                        this.deferredImportSelectors.add(new ConfigurationClassParser.DeferredImportSelectorHolder(configClass, (DeferredImportSelector)selector));
                                    } else {
                                        String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                                        Collection<ConfigurationClassParser.SourceClass> importSourceClasses = this.asSourceClasses(importClassNames);
                                        this.processImports(configClass, currentSourceClass, importSourceClasses, false);
                                    }
                                } else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
                                    candidateClass = candidate.loadClass();
                                    ImportBeanDefinitionRegistrar registrar = (ImportBeanDefinitionRegistrar)BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
                                    ParserStrategyUtils.invokeAwareMethods(registrar, this.environment, this.resourceLoader, this.registry);
                                    configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
                                } else {
                                    this.importStack.registerImport(currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
                                    this.processConfigurationClass(candidate.asConfigClass(configClass));
                                }
                            }

                            return;
                        }
                    }
                } catch (BeanDefinitionStoreException var15) {
                    throw var15;
                } catch (Throwable var16) {
                    throw new BeanDefinitionStoreException("Failed to process import candidates for configuration class [" + configClass.getMetadata().getClassName() + "]", var16);
                } finally {
                    this.importStack.pop();
                }
            }
        }
    }

springboot 程序启动指定配置文件 springboot配置启动类_spring boot_04

3、processDeferredImportSelectors方法调用

处理延迟加载selectImports方法得,该方法在parse方法内部

springboot 程序启动指定配置文件 springboot配置启动类_加载_05


代码好短,但是又调用了这个processImports方法

private void processDeferredImportSelectors() {
       //本地属性赋值此时为封装了EnableAutoConfigurationImportSelector得集合
        List<ConfigurationClassParser.DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
        this.deferredImportSelectors = null;
        Iterator var2 = deferredImports.iterator();

        while(var2.hasNext()) {
            ConfigurationClassParser.DeferredImportSelectorHolder deferredImport = (ConfigurationClassParser.DeferredImportSelectorHolder)var2.next();
            ConfigurationClass configClass = deferredImport.getConfigurationClass();
            //重点001
            String[] imports = deferredImport.getImportSelector().selectImports(configClass.getMetadata());
            
            this.processImports(configClass, this.asSourceClass(configClass), this.asSourceClasses(imports), false);
        
        }

    }
  • 重点001

3.1、继续调用了EnableAutoConfigurationImportSelector父类AutoConfigurationImportSelector得selectImports方法

public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
            try {
               //关注点1
                AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
                AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
                //关注点2
                List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
                configurations = this.removeDuplicates(configurations);
                configurations = this.sort(configurations, autoConfigurationMetadata);
                Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
                this.checkExcludedClasses(configurations, exclusions);
                configurations.removeAll(exclusions);
                //关注点3
                configurations = this.filter(configurations, autoConfigurationMetadata);
                this.fireAutoConfigurationImportEvents(configurations, exclusions);
                return (String[])configurations.toArray(new String[configurations.size()]);
            } catch (IOException var6) {
                throw new IllegalStateException(var6);
            }
        }
    }
  • 关注点1

    上图可以看到,加载路径META-INF/spring-autoconfigure-metadata.properties
    继续深入就是把资源使用properties进行加载
    看下这个文件中是啥
  • 关注点2

springboot 程序启动指定配置文件 springboot配置启动类_spring_06


springboot 程序启动指定配置文件 springboot配置启动类_spring_07


主要是用properties加载META-INF/spring.factories文件里面的数据

我们看下这个文件

springboot 程序启动指定配置文件 springboot配置启动类_加载_08

  • 关注点3
    这个是进行过滤的,就是把不满足的自动配置先去掉

    过滤前我们看下,大概有99个自动配置类,进入过滤方法

springboot 程序启动指定配置文件 springboot配置启动类_spring_09


获取过滤器主要是从spring.factories文件获取的

springboot 程序启动指定配置文件 springboot配置启动类_java_10


springboot 程序启动指定配置文件 springboot配置启动类_spring boot_11

过滤方法比较简单,但是比较绕

springboot 程序启动指定配置文件 springboot配置启动类_加载_12


进入this.getOutcomes方法

springboot 程序启动指定配置文件 springboot配置启动类_spring_13


springboot 程序启动指定配置文件 springboot配置启动类_spring_14


分段进行处理的我们本文只看一个OnClassCondition.OutcomesResolver secondHalfResolver对象的创建

springboot 程序启动指定配置文件 springboot配置启动类_java_15


secondHalfResolver.resolveOutcomes()方法调用

springboot 程序启动指定配置文件 springboot配置启动类_加载_16


springboot 程序启动指定配置文件 springboot配置启动类_spring boot_17


springboot 程序启动指定配置文件 springboot配置启动类_加载_18


以RedisRepositoriesAutoConfiguration为例

springboot 程序启动指定配置文件 springboot配置启动类_spring_19


springboot 程序启动指定配置文件 springboot配置启动类_加载_20


最后看下加载器怎么判断是否环境中有某个Class信息

springboot 程序启动指定配置文件 springboot配置启动类_spring boot_21


过滤器结束后当前满足条件的只有如下23个

springboot 程序启动指定配置文件 springboot配置启动类_加载_22


回到processDeferredImportSelectors方法

springboot 程序启动指定配置文件 springboot配置启动类_java_23


processImports方法会重复对这些自动配置类进行处理,加载bean的定义信息等,该方法的分析在和最上面一样,我们要注意到,该方法后面这行代码

this.processConfigurationClass(candidate.asConfigClass(configClass));

这个会将每次处理的Class放入this.configurationClasses.put(configClass, configClass); 中,是ConfigurationClassParser类的属性

springboot 程序启动指定配置文件 springboot配置启动类_spring boot_24