zyllt / project

随便写写
1 stars 0 forks source link

spring-boot2.X之AutoConfigurationImportSelector解析 #17

Open zyllt opened 5 years ago

zyllt commented 5 years ago

实现了DeferredImportSelector接口,用于处理@EnableAutoConfiguration,如果需要做一些定制化操作,可以重写该类

zyllt commented 5 years ago

该类的核心方法就是selectImports,下面是源码和一些自己的阅读注释,英文渣可能不准确

    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        /**
         * {@link EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY} = false时此自动配置不启用,默认true
         */
        if (!isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        }
        /**
         * 加载{@link AutoConfigurationMetadataLoader.PATH}文件(此文件是spring boot autoconfiguration模块编译时自动生成,保存所有的autoconfiguration的conditional )
         * 在调用AutoConfigurationImportFilter被使用(OnClassCondition)
         */
        AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
                .loadMetadata(this.beanClassLoader);
        /**
         *  *获取{@link EnableAutoConfiguration} 属性,肯定有值
         */
        AnnotationAttributes attributes = getAttributes(annotationMetadata);
        //获取自动配置的类名称集合,使用SpringFactoriesLoader
        List<String> configurations = getCandidateConfigurations(annotationMetadata,
                attributes);
        //去重,转set
        configurations = removeDuplicates(configurations);
        //获取需要排除的类,通过注解或者配置
        Set<String> exclusions = getExclusions(annotationMetadata, attributes);
        //校验是否有无效的排除类,有的话就抛出异常 (暂时不明白这么做的意义)
        checkExcludedClasses(configurations, exclusions);
        //从需要自动配置的类中删除需要排除的类
        configurations.removeAll(exclusions);
        /**
         * 执行 {@link AutoConfigurationImportFilter},此时也可以排除不想使用自动配置的的类
         *
         * 其实是触发了 {@link org.springframework.boot.autoconfigure.condition.OnClassCondition}排除了
         * {@link org.springframework.boot.autoconfigure.condition.ConditionalOnClass} 不存在的类
         *
         */
        configurations = filter(configurations, autoConfigurationMetadata);
        //加载、触发 AutoConfigurationImportListener
        fireAutoConfigurationImportEvents(configurations, exclusions);
        return StringUtils.toStringArray(configurations);
    }
zyllt commented 5 years ago

这个方法比较简单,几个逻辑都是很容易理解的。其中比较核心的逻辑就是获取所有的@EnableAutoConfiguration类名称(使用SpringFactoriesLoader来加载,一般我们也可以这么做)

    /**
     * Return the auto-configuration class names that should be considered. By default
     * this method will load candidates using {@link SpringFactoriesLoader} with
     * {@link #getSpringFactoriesLoaderFactoryClass()}.
     * @param metadata the source metadata
     * @param attributes the {@link #getAttributes(AnnotationMetadata) annotation
     * attributes}
     * @return a list of candidate configurations
     */
    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
            AnnotationAttributes attributes) {
        List<String> 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;
    }

    /**
     * Return the class used by {@link SpringFactoriesLoader} to load configuration
     * candidates.
     * @return the factory class
     */
    protected Class<?> getSpringFactoriesLoaderFactoryClass() {
        return EnableAutoConfiguration.class;
    }
zyllt commented 5 years ago

还有就是filter方法,该方法会验证所有的 @EnableAutoConfiguration类然后排除不符合条件的自动配置类(配置了ConditionalOnClass注解的类不存在)

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;
        //获取 AutoConfigurationImportFilter,默认是 OnClassCondition
        for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
            //调用filter的aware方法,如果存在
            invokeAwareMethods(filter);
            boolean[] match = filter.match(candidates, autoConfigurationMetadata);
            for (int i = 0; i < match.length; i++) {
                if (!match[i]) {
                    skip[i] = true;
                    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);
    }
zyllt commented 5 years ago

其中默认存在的AutoConfigurationImportFilter只有OnClassCondition,通过这个filter来验证配置了注解ConditionalOnClass是否合法

    @Override
    public boolean[] match(String[] autoConfigurationClasses,
            AutoConfigurationMetadata autoConfigurationMetadata) {
        ConditionEvaluationReport report = getConditionEvaluationReport();
        //返回验证结果,这个结果的长度和autoConfigurationClasses相同,如果验证通过对应的结果为null或者ConditionOutcome.match=true
        ConditionOutcome[] outcomes = getOutcomes(autoConfigurationClasses,
                autoConfigurationMetadata);
        boolean[] match = new boolean[outcomes.length];
        for (int i = 0; i < outcomes.length; i++) {
            match[i] = (outcomes[i] == null || outcomes[i].isMatch());
            if (!match[i] && outcomes[i] != null) {
                //验证不通过的输出log
                logOutcome(autoConfigurationClasses[i], outcomes[i]);
                if (report != null) {
                    report.recordConditionEvaluation(autoConfigurationClasses[i], this,
                            outcomes[i]);
                }
            }
        }
        return match;
    }
zyllt commented 5 years ago
    private ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses,
            AutoConfigurationMetadata autoConfigurationMetadata) {
        // Split the work and perform half in a background thread. Using a single
        // additional thread seems to offer the best performance. More threads make
        // things worse
        //有个独立的线程处理一半,主线程处理一半
        int split = autoConfigurationClasses.length / 2;
        //创建StandardOutcomesResolver,并且启动一个独立线程来执行一半任务
        OutcomesResolver firstHalfResolver = createOutcomesResolver(
                autoConfigurationClasses, 0, split, autoConfigurationMetadata);

        //主线程执行StandardOutcomesResolver
        OutcomesResolver secondHalfResolver = new StandardOutcomesResolver(
                autoConfigurationClasses, split, autoConfigurationClasses.length,
                autoConfigurationMetadata, this.beanClassLoader);
        ConditionOutcome[] secondHalf = secondHalfResolver.resolveOutcomes();
        //这个实际是用thread.join()来保证独立线程任务执行完成了
        ConditionOutcome[] firstHalf = firstHalfResolver.resolveOutcomes();
        //合并2个结果
        ConditionOutcome[] outcomes = new ConditionOutcome[autoConfigurationClasses.length];
        System.arraycopy(firstHalf, 0, outcomes, 0, firstHalf.length);
        System.arraycopy(secondHalf, 0, outcomes, split, secondHalf.length);
        return outcomes;
    }
zyllt commented 5 years ago

StandardOutcomesResolverresolveOutcomes()方法执行逻辑,其实就是验证ConditonalOnClass配置的class是否存在

        @Override
        public ConditionOutcome[] resolveOutcomes() {
            return getOutcomes(this.autoConfigurationClasses, this.start, this.end,
                    this.autoConfigurationMetadata);
        }

        private ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses,
                int start, int end, AutoConfigurationMetadata autoConfigurationMetadata) {
            ConditionOutcome[] outcomes = new ConditionOutcome[end - start];
            for (int i = start; i < end; i++) {
                String autoConfigurationClass = autoConfigurationClasses[i];
                //从PropertiesAutoConfigurationMetadata 注解ConditionalOnClass的类,其实就是获取这个类有使用@ConditionalOnClass修饰时设置的类
                Set<String> candidates = autoConfigurationMetadata
                        .getSet(autoConfigurationClass, "ConditionalOnClass");
                if (candidates != null) {
                    //判断这个类是否存在,如果存在就返回null
                    outcomes[i - start] = getOutcome(candidates);
                }
            }
            return outcomes;
        }
zyllt commented 5 years ago

从以上可以看出如果想试用自动配置,首先要@EnableAutoConfiguration,然后还要配置META-INF/spring.factories ,例如

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
zyllt commented 5 years ago

另外还有一种编程式自动配置的方案就是使用@ImportAutoConfiguration注解。这个注解的实现方式比较简单,是通过ImportAutoConfigurationImportSelector,这个类继承了AutoConfigurationImportSelector然后重写了其获取自动配置类的部分,其他没啥变化