springboot启动(五)——加载所有的自动配置类,@EnableAutoConfiguration 注解生效时机

加载所有的自动配置类,@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注解信息
在这里插入图片描述
看下最后一行代码
imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), “value”));

最终会把符合条件的放在imports集合中(set集合),注意放入时取得是@Import注解属性得value值,最终为@EnableAutoConfiguration和@Registrar
在这里插入图片描述
小结:
getImport最终返回数据在这里插入图片描述

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();
                }
            }
        }
    }

12

3、processDeferredImportSelectors方法调用

处理延迟加载selectImports方法得,该方法在parse方法内部
在这里插入图片描述
代码好短,但是又调用了这个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

在这里插入图片描述
在这里插入图片描述
主要是用properties加载META-INF/spring.factories文件里面的数据
我们看下这个文件
在这里插入图片描述

  • 关注点3
    这个是进行过滤的,就是把不满足的自动配置先去掉
    在这里插入图片描述
    过滤前我们看下,大概有99个自动配置类,进入过滤方法

在这里插入图片描述
获取过滤器主要是从spring.factories文件获取的
在这里插入图片描述
在这里插入图片描述

过滤方法比较简单,但是比较绕
在这里插入图片描述
进入this.getOutcomes方法
在这里插入图片描述
在这里插入图片描述
分段进行处理的我们本文只看一个

OnClassCondition.OutcomesResolver secondHalfResolver对象的创建
在这里插入图片描述
secondHalfResolver.resolveOutcomes()方法调用
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
以RedisRepositoriesAutoConfiguration为例
在这里插入图片描述
在这里插入图片描述
最后看下加载器怎么判断是否环境中有某个Class信息
在这里插入图片描述
过滤器结束后当前满足条件的只有如下23个
在这里插入图片描述
回到processDeferredImportSelectors方法

在这里插入图片描述
processImports方法会重复对这些自动配置类进行处理,加载bean的定义信息等,该方法的分析在和最上面一样,我们要注意到,该方法后面这行代码
this.processConfigurationClass(candidate.asConfigClass(configClass));
这个会将每次处理的Class放入this.configurationClasses.put(configClass, configClass); 中,是ConfigurationClassParser类的属性
在这里插入图片描述

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值