zoukankan      html  css  js  c++  java
  • springboot启动流程(十)springboot自动配置机制

    所有文章

    https://www.cnblogs.com/lay2017/p/11478237.html

    正文

    第七篇文章中我们了解到,refresh过程将会调用ConfigurationClassPostProcessor这个后置处理器,而这个后置处理器将会去调用ConfigurationClassParser这个配置类的解析器,而第一个被解析的配置类就是我们main方法所在的主类(主类是在refresh之前的,prepareRefresh方法加载成为BeanDefinition到BeanFactory中的)。

    而后,在第八篇文章中我们主要看了看ConfigurationClassParser是怎么解析配置类的@ComponentScan这个注解的。

    那么本文将继续从ConfigurationClassParser这个过程开始,看看parse的处理过程关于自动配置的内容。

    自动配置入口

    首先,我们回到ConfigurationClassParser的parse方法。

    public void parse(Set<BeanDefinitionHolder> configCandidates) {
        for (BeanDefinitionHolder holder : configCandidates) {
            BeanDefinition bd = holder.getBeanDefinition();
            try {
                if (bd instanceof AnnotatedBeanDefinition) {
                    // 解析主类的入口
                    parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
                } else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
                    parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
                } else {
                    parse(bd.getBeanClassName(), holder.getBeanName());
                }
            } catch (BeanDefinitionStoreException ex) {
                throw ex;
            } catch (Throwable ex) {
                throw new BeanDefinitionStoreException(
                        "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
            }
        }
        // 处理自动配置的入口
        this.deferredImportSelectorHandler.process();
    }

    springboot解析过程将从解析main方法所在的主类开始,所以我们先跟进parse方法

    protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
        processConfigurationClass(new ConfigurationClass(metadata, beanName));
    }

    再跟进processConfigurationClass方法

    protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
        // 判断当前配置类是否应该跳过解析
        if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
            return;
        }
    
        // 省略
    
        // 向父类递归解析
        SourceClass sourceClass = asSourceClass(configClass);
        do {
            // 解析当前配置类的核心逻辑
            sourceClass = doProcessConfigurationClass(configClass, sourceClass);
        }
        while (sourceClass != null);
    
        //
    }

    这里先做了一个判断,是否跳过当前配置类的解析(后面会提及)。而后就是对配置类的递归解析,如果有父类将会递归解析。

    跟进doProcessConfigurationClass方法,我们省略其它内容

    protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
            throws IOException {
    
        // 
    
        // 处理@Import注解
        processImports(configClass, sourceClass, getImports(sourceClass), true);
    
        //
        return null;
    }

    我们看到,解析逻辑中包含着一个processImports方法,用于处理@Import注解。我们知道,每一个springboot程序将会注解一个@SpringBootApplication注解,这个注解是一个组合注解,我们看看该注解。

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @SpringBootConfiguration
    @EnableAutoConfiguration
    @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
            @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
    public @interface SpringBootApplication {
        // 省略
    }

    @SpringBootApplication注解组合了@EnableAutoConfiguration注解,我们再看看@EnableAutoConfiguration注解

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @AutoConfigurationPackage
    @Import(AutoConfigurationImportSelector.class)
    public @interface EnableAutoConfiguration {
        // 省略
    }

    可以看到,@Import注解导入了一个AutoConfigurationImportSelector类。

    我们再回到doProcessConfigurationClass方法

    protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
            throws IOException {
    
        // 
    
        // 处理@Import注解
        processImports(configClass, sourceClass, getImports(sourceClass), true);
    
        //
        return null;
    }

    processImports之前,会调用getImports方法获取当前配置类的@Import,也包含组合的注解。所以@SpringBootApplication组合的@Import注解导入的配置类AutoConfigurationImportSelector将在这里被获取。

    我们跟进getImport方法看看

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

    再跟进collectImports看看是怎么搜集类的

    private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)
            throws IOException {
    
        if (visited.add(sourceClass)) {
            // 获取所有注解
            for (SourceClass annotation : sourceClass.getAnnotations()) {
                String annName = annotation.getMetadata().getClassName();
                // 非@Import注解的,递归看看有没有组合@Import注解
                if (!annName.equals(Import.class.getName())) {
                    collectImports(annotation, imports, visited);
                }
            }
            imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
        }
    }

    很显然,搜集过程将对所有注解递归处理,这样我们就获得了main方法所在主类的所有@Import导入的类。

    再回到doProcessConfigurationClass方法

    protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
            throws IOException {
    
        // 
    
        // 处理@Import注解
        processImports(configClass, sourceClass, getImports(sourceClass), true);
    
        //
        return null;
    }

    跟进processImports方法

    private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
            Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
        //
    
        if (checkForCircularImports && isChainedImportOnStack(configClass)) {
            //
        }
        else {
            //
            try {
                for (SourceClass candidate : importCandidates) {
                    if (candidate.isAssignable(ImportSelector.class)) {
                        Class<?> candidateClass = candidate.loadClass();
                        ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
                        //
                        if (selector instanceof DeferredImportSelector) {
                            this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
                        }
                        else {
                            //
                        }
                    }
                    else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
                        //
                    }
                    else {
                        // 
                        processConfigurationClass(candidate.asConfigClass(configClass));
                    }
                }
            }
            // 
        }
    }

    这里遍历了我们getImports方法获取到的类,但是目前我们还只有AutoConfiguratonImportSelector这个类。这个类实现了DeferredImportSelector接口,所以我们继续跟进handle方法

    public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {
        DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(configClass, importSelector);
        if (this.deferredImportSelectors == null) {
            //
        } else {
            // 添加到集合
            this.deferredImportSelectors.add(holder);
        }
    }

    AutoConfigurationImportSelector被包装已经添加到了集合中。那么这个被添加到集合中的类是什么时候被处理的呢?

    我们回到本文最开始的代码片段,parse方法

    public void parse(Set<BeanDefinitionHolder> configCandidates) {
        for (BeanDefinitionHolder holder : configCandidates) {
            BeanDefinition bd = holder.getBeanDefinition();
            try {
                if (bd instanceof AnnotatedBeanDefinition) {
                    // 解析主类的入口
                    parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
                } else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
                    parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
                } else {
                    parse(bd.getBeanClassName(), holder.getBeanName());
                }
            } catch (BeanDefinitionStoreException ex) {
                throw ex;
            } catch (Throwable ex) {
                throw new BeanDefinitionStoreException(
                        "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
            }
        }
        // 处理自动配置的入口
        this.deferredImportSelectorHandler.process();
    }

    可以看到,parse方法的最后一行,调用了process方法,将会对这些类进行处理,这也就是自动配置的入口方法了。

    处理AutoConfigurationImportSelector

    我们跟进process方法,看看处理过程

    public void process() {
        List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
        this.deferredImportSelectors = null;
        try {
            if (deferredImports != null) {
                DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
                deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
                // 遍历导入的类,将这些类注册到handler中
                deferredImports.forEach(handler::register);
                // 处理handler中的导入类
                handler.processGroupImports();
            }
        } finally {
            //
        }
    }

    这里先将导入类调用handler的register方法进行注册,然后集中处理。我们稍微瞄一眼register方法

    public void register(DeferredImportSelectorHolder deferredImport) {
        Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup();
        DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent((group != null ? group : deferredImport), key -> new DeferredImportSelectorGrouping(createGroup(group)));
        // 添加到group中
        grouping.add(deferredImport);
        this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getConfigurationClass());
    }

    跟进add方法

    private final List<DeferredImportSelectorHolder> deferredImports = new ArrayList<>();
    
    public void add(DeferredImportSelectorHolder deferredImport) {
        this.deferredImports.add(deferredImport);
    }

    添加到一个集合中存放起来

    我们回到process方法

    public void process() {
        List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
        this.deferredImportSelectors = null;
        try {
            if (deferredImports != null) {
                DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
                deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
                // 遍历导入的类,将这些类注册到handler中
                deferredImports.forEach(handler::register);
                // 处理handler中的导入类
                handler.processGroupImports();
            }
        } finally {
            //
        }
    }

    register完毕以后,将会processGroupImports,我们跟进processGroupImports方法

    public void processGroupImports() {
        for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
            // 获取所有AutoConfigurationImportSelector返回的待处理的配置类,并遍历
            grouping.getImports().forEach(entry -> {
                ConfigurationClass configurationClass = this.configurationClasses.get(
                        entry.getMetadata());
                try {
                    // 处理所有配置类
                    processImports(configurationClass, asSourceClass(configurationClass),
                            asSourceClasses(entry.getImportClassName()), false);
                }
                catch (BeanDefinitionStoreException ex) {
                    throw ex;
                }
                catch (Throwable ex) {
                    throw new BeanDefinitionStoreException(
                            "Failed to process import candidates for configuration class [" +
                                    configurationClass.getMetadata().getClassName() + "]", ex);
                }
            });
        }
    }

    AutoConfigurationImportSelector需要进行自动配置的类将会在这里的getImports方法中返回,而后processImports方法将会处理所有这些需要自动配置的类

    我们先跟进getImports方法,看看是怎么获取所有待处理的配置类的

    public Iterable<Group.Entry> getImports() {
        for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
            // 处理生成结果
            this.group.process(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getImportSelector());
        }
        // 返回结果
        return this.group.selectImports();
    }

    我们主要看process方法,进入到AutoConfigurationGroup的process方法

    public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
        AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector).getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
        this.autoConfigurationEntries.add(autoConfigurationEntry);
        for (String importClassName : autoConfigurationEntry.getConfigurations()) {
            this.entries.putIfAbsent(importClassName, annotationMetadata);
        }
    }

    再跟进getAutoConfigurationEntry

    protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
            AnnotationMetadata annotationMetadata) {
        //
        AnnotationAttributes attributes = getAttributes(annotationMetadata);
        List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
        //
    
        // 过滤
        configurations = filter(configurations, autoConfigurationMetadata);
        //
        return new AutoConfigurationEntry(configurations, exclusions);
    }

    继续跟进getCandidateConfigurations

    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
                getBeanClassLoader());
        //
        return configurations;
    }

    我们看到一个熟悉的方法loadFactoryNames(不熟悉的话,请阅读辅助内容),看看getSpringFactoriesLoaderFactoryClass返回什么

    protected Class<?> getSpringFactoriesLoaderFactoryClass() {
        return EnableAutoConfiguration.class;
    }

    我们可以随机打开一个spring.factories看看EnableAutoConfiguration作为key的配置

    可以看到spring.factories中将需要进行自动配置的类作为value配置在这里,所以getCandidateConfigurations方法将会把这些配置类返回。我们再回到getAutoConfigurationEntry方法

    protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
            AnnotationMetadata annotationMetadata) {
        //
        AnnotationAttributes attributes = getAttributes(annotationMetadata);
        List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
        //
    
        // 过滤
        configurations = filter(configurations, autoConfigurationMetadata);
        //
        return new AutoConfigurationEntry(configurations, exclusions);
    }

    获取完configurations后,将会进行一次过滤操作,这样可以避免大量的不需要配置的类被加载。

    再回到AutoConfiguration的process方法

    private final Map<String, AnnotationMetadata> entries = new LinkedHashMap<>();
    
    public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
        AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector).getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
        this.autoConfigurationEntries.add(autoConfigurationEntry);
        for (String importClassName : autoConfigurationEntry.getConfigurations()) {
            this.entries.putIfAbsent(importClassName, annotationMetadata);
        }
    }

    我们调用getAutoConfigurationEntry获得需要自动配置的类,然后再这里会被添加到一个Map集合中存放起来。

    到这里,AutoConfigurationImportSelector的getImports方法的process过程就结束了。我们回到getImports方法

    public Iterable<Group.Entry> getImports() {
        for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
            // 处理生成结果
            this.group.process(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getImportSelector());
        }
        // 返回结果
        return this.group.selectImports();
    }

    process方法获取了Imports,而selectImports将返回结果。

    到这里,我们的getImports方法就获取了可能需要进行自动配置的类,回到DeferredImportSelectorGroupingHandler类的processGroupImports方法

    public void processGroupImports() {
        for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
            // 获取所有AutoConfigurationImportSelector返回的待处理的配置类,并遍历
            grouping.getImports().forEach(entry -> {
                ConfigurationClass configurationClass = this.configurationClasses.get(
                        entry.getMetadata());
                try {
                    // 处理所有配置类
                    processImports(configurationClass, asSourceClass(configurationClass),
                            asSourceClasses(entry.getImportClassName()), false);
                }
                catch (BeanDefinitionStoreException ex) {
                    throw ex;
                }
                catch (Throwable ex) {
                    throw new BeanDefinitionStoreException(
                            "Failed to process import candidates for configuration class [" +
                                    configurationClass.getMetadata().getClassName() + "]", ex);
                }
            });
        }
    }

    跟进processImports

    private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
            Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
        //
        if (checkForCircularImports && isChainedImportOnStack(configClass)) {
            //
        }
        else {
            //
            try {
                for (SourceClass candidate : importCandidates) {
                    if (candidate.isAssignable(ImportSelector.class)) {
                        //
                    }
                    else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
                        //
                    }
                    else {
                        // 处理配置类
                        processConfigurationClass(candidate.asConfigClass(configClass));
                    }
                }
            }
            //
        }
    }

    导入的类作为配置类来处理,跟进processConfigurationClass方法

    protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
        // 是否要进行配置解析
        if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
            return;
        }
    
        //
    
        SourceClass sourceClass = asSourceClass(configClass);
        do {
            // 解析的逻辑
            sourceClass = doProcessConfigurationClass(configClass, sourceClass);
        }
        while (sourceClass != null);
        // 
    }

    我们久违的processConfigurationClass方法,一开始我们关注的是doProcessConfigurationClass看它解析过程的。现在我们来看看shouldSkip方法,看看是怎么判断当前配置类是否要进行解析的。

    跟进shouldSkip方法

    public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {
        //
    
        List<Condition> conditions = new ArrayList<>();
        // 获取配置类中所有@Conditional以及组合@Conditional的条件
        for (String[] conditionClasses : getConditionClasses(metadata)) {
            for (String conditionClass : conditionClasses) {
                Condition condition = getCondition(conditionClass, this.context.getClassLoader());
                conditions.add(condition);
            }
        }
    
        //
    
        // 遍历这些条件
        for (Condition condition : conditions) {
            ConfigurationPhase requiredPhase = null;
            if (condition instanceof ConfigurationCondition) {
                requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
            }
            // 判断条件是否不匹配
            if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) {
                return true;
            }
        }
    
        return false;
    }

    shouldSkip方法,将会拿到当前配置类的所有@Conditional或者组合了@Conditional的注解,并将注解生成Condition条件,再遍历这些条件看是否有不满足条件的将返回true。

    总结

    springboot的自动配置将从解析main方法所在的主类开始,ConfigurationClassParser在解析@Import的时候会获取到AutoConfigurationImportSelector类。AutoConfigurationImportSelector将会获取到所有可能需要进行自动配置的类,而后每个配置类将被和main方法所在的主类一样准备解析,在解析之前会根据像@Conditional或者组合@Conditional的注解来生成判断条件Condition,根据是否满足Condition来决定是否要进行自动配置。

  • 相关阅读:
    多线程、方便扩展的Windows服务程序框架
    C#并行开发_Thread/ThreadPool, Task/TaskFactory, Parallel
    C#并行编程-Task
    C#线程篇---Task(任务)和线程池不得不说的秘密(5)
    C# 线程知识--使用Task执行异步操作
    C# 线程池执行操作例子
    c#子线程执行完怎么通知主线程
    C#子线程执行完后通知主线程
    再送一波干货,测试2000线程并发下同时查询1000万条数据库表及索引优化
    熵的函数为什么用H,而熵的英文是entropy,好像没关系。实际原因是
  • 原文地址:https://www.cnblogs.com/lay2017/p/11518086.html
Copyright © 2011-2022 走看看