美文网首页
SpringBoot中 AutoConfiguration 是如

SpringBoot中 AutoConfiguration 是如

作者: AlanSun2 | 来源:发表于2020-01-04 18:08 被阅读0次

    本文基于
    Spring 5.1.7.release
    SpringBoot 2.1.5.release

    在之前的文章 SpringBoot 中的 Bean 定义是如何被注册的? 中已经讲过 bean 定义的加载和注册过程,但你可能有个疑惑 SpringBoot 的 AutoConfiguration 配置类是如何被加载的呢?这篇文章就带你揭开这个面纱。

    用过 SpringBoot 的朋友都知道,SpringBoot 有一个重要的注解就是 @SpringBootApplication。奥秘就在这个注解的元注解 @EnableAutoConfiguration 上,看下 @EnableAutoConfiguration 的结构:

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @AutoConfigurationPackage
    @Import(AutoConfigurationImportSelector.class)
    public @interface EnableAutoConfiguration {
    
        String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
    
        /**
         * Exclude specific auto-configuration classes such that they will never be applied.
         * @return the classes to exclude
         */
        Class<?>[] exclude() default {};
    
        /**
         * Exclude specific auto-configuration class names such that they will never be
         * applied.
         * @return the class names to exclude
         * @since 1.3.0
         */
        String[] excludeName() default {};
    
    }
    

    可以看到它的上面有个 @Import 的注解,value = AutoConfigurationImportSelector.class,它是一个 DeferredImportSelector(ImportSelector),如果你看过了 SpringBoot 中的 Bean 定义是如何被注册的? 你就应该知道,它会在 ConfigurationClassParser#doProcessConfigurationClass#processImports 中被解析。另外它是一个 DeferredImportSelector 表示它是一个延后处理的 ImportSelector。

    调用过程

    1. ConfigurationClassParser#parse(Set<BeanDefinitionHolder> configCandidates)

    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);
            }
        }
        // 在这一步处理延后处理的 ImportSelector,也就是实现了 DeferredImportSelector 的类
        this.deferredImportSelectorHandler.process();
    }
    

    2. DeferredImportSelectorHandler#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);
                // 处理
                deferredImports.forEach(handler::register);
                handler.processGroupImports();
            }
        }
        finally {
            this.deferredImportSelectors = new ArrayList<>();
        }
    }
    

    3. DeferredImportSelectorGroupingHandler#register

    public void register(DeferredImportSelectorHolder deferredImport) {
        // 获取 group class类。AutoConfigurationImportSelector 返回的是一个 AutoConfigurationImportSelector&AutoConfigurationGroup.class
        Class<? extends Group> group = deferredImport.getImportSelector()
                .getImportGroup();
        // 放入 groupings,记住这个 groupings,后面会用到
        // 如果 group == null,则使用默认的 group DefaultDeferredImportSelectorGroup
        DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent(
                (group != null ? group : deferredImport),
                key -> new DeferredImportSelectorGrouping(createGroup(group)));
        grouping.add(deferredImport);
        this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(),
                deferredImport.getConfigurationClass());
    }
    

    4. DeferredImportSelectorGroupingHandler#processGroupImports

    public void processGroupImports() {
        for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
            // grouping.getImports()  加载 AutoConfiguration 
            grouping.getImports().forEach(entry -> {
                ConfigurationClass configurationClass = this.configurationClasses.get(
                        entry.getMetadata());
                try {
                    // 处理 import
                    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);
                }
            });
        }
    }
    

    这里是加载和注册 AutoConfiguration 最重要的一步,所有的奥秘都在 grouping.getImports() 这个方法中。
    grouping.getImports() 获取所有的 AutoConfiguration 类,processImports 处理。

    5. DeferredImportSelectorGrouping#getImports

    public Iterable<Group.Entry> getImports() {
        for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
            // 处理所有的 AutoConfiguration
            this.group.process(deferredImport.getConfigurationClass().getMetadata(),
                    deferredImport.getImportSelector());
        }
        // 获取
        return this.group.selectImports();
    }
    

    这里的 group 是 AutoConfigurationGroup

    6. AutoConfigurationGroup#process

    @Override
    public void process(AnnotationMetadata annotationMetadata,
            DeferredImportSelector deferredImportSelector) {
        Assert.state(
                deferredImportSelector instanceof AutoConfigurationImportSelector,
                () -> String.format("Only %s implementations are supported, got %s",
                        AutoConfigurationImportSelector.class.getSimpleName(),
                        deferredImportSelector.getClass().getName()));
        // 这一步获取所有的 EnableAutoConfiguration
        AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
                .getAutoConfigurationEntry(getAutoConfigurationMetadata(),
                        annotationMetadata);
        this.autoConfigurationEntries.add(autoConfigurationEntry);
        for (String importClassName : autoConfigurationEntry.getConfigurations()) {
            this.entries.putIfAbsent(importClassName, annotationMetadata);
        }
    }
    
    private AutoConfigurationMetadata getAutoConfigurationMetadata() {
        if (this.autoConfigurationMetadata == null) {
            this.autoConfigurationMetadata = AutoConfigurationMetadataLoader
                    .loadMetadata(this.beanClassLoader);
        }
        return this.autoConfigurationMetadata;
    }
    
    

    注意:getAutoConfigurationMetadata() 会获取 spring-autoconfigure-metadata.properties 中的内容用于过滤 AutoConfiguration

    AutoConfigurationMetadataLoader#loadMetadata
    // PATH = "META-INF/spring-autoconfigure-metadata.properties"
    public static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
        return loadMetadata(classLoader, PATH);
    }
    

    7. AutoConfigurationImportSelector#getAutoConfigurationEntry

    protected AutoConfigurationEntry getAutoConfigurationEntry(
            AutoConfigurationMetadata autoConfigurationMetadata,
            AnnotationMetadata annotationMetadata) {
        // 判断是否开启了自动配置,可以在属性里配置
        if (!isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        }
        // 获取需要排除加载的类,在 @EnableAutoConfiguration 中配置
        AnnotationAttributes attributes = getAttributes(annotationMetadata);
        // 使用 SpringFactoriesLoader 加载 META-INF/spring.factories 文件中 EnableAutoConfiguration 的类
        List<String> configurations = getCandidateConfigurations(annotationMetadata,
                attributes);
        // 使用 Set 去重
        configurations = removeDuplicates(configurations);
        //  获取需要排除加载的类名
        Set<String> exclusions = getExclusions(annotationMetadata, attributes);
        // 校验排除类是否是 auto-configuration 类,如果不是则报错。
        // 也就是说排除的类必须是 META-INF/spring.factories 文件中 EnableAutoConfiguration 的类
        checkExcludedClasses(configurations, exclusions);
        // 去除排除类
        configurations.removeAll(exclusions);
        // 通过 Condition 过滤掉不符合条件的类,减少后续的操作,加快启动速度
        configurations = filter(configurations, autoConfigurationMetadata);
        // 把符合条件和不符合条件的 config 放入 ConditionEvaluationReport
        // 题外话:ConditionEvaluationReport 会在 ConditionEvaluationReportLoggingListener#ConditionEvaluationReportListener 监听器中被触发
        // ConditionEvaluationReportLoggingListener 的初始化在 SpringApplication 构造方法中
        fireAutoConfigurationImportEvents(configurations, exclusions);
        return new AutoConfigurationEntry(configurations, exclusions);
    }
    
    private List<String> filter(List<String> configurations,
            AutoConfigurationMetadata autoConfigurationMetadata) {
        long startTime = System.nanoTime();
        String[] candidates = StringUtils.toStringArray(configurations);
        boolean[] skip = new boolean[candidates.length];
        boolean skipped = false;
        // SpringFactoriesLoader 加载 AutoConfigurationImportFilter 的实现,一般至少有三个,分别是
        // OnClassCondition,
        // OnWebApplicationCondition,
        // OnBeanCondition
        // 使用 Condition 过滤,这里详细的不说的,有兴趣的朋友自己看下吧。
        for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
            invokeAwareMethods(filter);
            boolean[] match = filter.match(candidates, autoConfigurationMetadata);
            for (int i = 0; i < match.length; i++) {
                if (!match[i]) {
                    skip[i] = true;
                    candidates[i] = null;
                    skipped = true;
                }
            }
        }
        // 如果没有要跳过的则直接返回
        if (!skipped) {
            return configurations;
        }
        // 有跳过的
        List<String> result = new ArrayList<>(candidates.length);
        for (int i = 0; i < candidates.length; i++) {
            if (!skip[i]) {
                result.add(candidates[i]);
            }
        }
        if (logger.isTraceEnabled()) {
            int numberFiltered = configurations.size() - result.size();
            logger.trace("Filtered " + numberFiltered + " auto configuration class in "
                    + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime)
                    + " ms");
        }
        return new ArrayList<>(result);
    }
    

    Conclusion

    1. 通过以上,我们也可以定义自己的 AutoConfiguration,步骤也很简单,只需要在 spring.factories 文件中如下定义:
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration,\
    org.springframework.cloud.autoconfigure.LifecycleMvcEndpointAutoConfiguration,\
    

    如果你还想配置条件的话,可以在 spring-autoconfigure-metadata.properties 文件中配置如下:

    org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration.ConditionalOnClass=org.springframework.security.crypto.encrypt.TextEncryptor
    

    ConditionalOnClass 及以后的内可以根据需要替换

    1. 关闭 AutoConfiguration :只要在系统属性中配置 spring.boot.enableautoconfiguration=false 就可以了

    相关文章

      网友评论

          本文标题:SpringBoot中 AutoConfiguration 是如

          本文链接:https://www.haomeiwen.com/subject/djgxactx.html