SpringBoot自动配置@EnableAutoConfiguration和@ImportAutoConfiguration的实现原理

SpringBoot核心特性——你所要知道的自动装配 本文主要讲解SpringBoot的自动装配特性,这个特性使得第三方应用很容易就集成到SpringBoot中,这也是为什么使用SpringBoot的时候,只需要引入依赖,稍微修改配置,就能迅速使用这些第三方应用进行开发的原因。 阅读详情
@SpringBootApplication
class App {

}

// @SpringBootApplication
@SpringBootConfiguration
@EnableAutoConfiguration
@ImportAutoConfiguration
@ComponentScan(excludeFilters = {
        @ComponentScan.Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @ComponentScan.Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class)})
@interface SpringBootApplication {

}

// 该注解就是一个普通的配置类标识
@Configuration
@interface SpringBootConfiguration {

}

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
@interface EnableAutoConfiguration {

}

// 该类和EnableAutoConfiguration效果差不多,ImportAutoConfigurationImportSelector也继承AutoConfigurationImportSelector
@Import(ImportAutoConfigurationImportSelector.class)
public @interface ImportAutoConfiguration {

}

/**
 * 导入Registrar类,该类主要是导入BasePackages类,保存了需要扫描的包的信息
 *
 * @see {@link AutoConfigurationPackages.BasePackages}
 * @see {@link AutoConfigurationPackages.Registrar#registerBeanDefinitions}
 * 该类的作用就是将标@AutoConfigurationPackage注解的类的包名封装成BasePackages对象,然后注册为Bean
 * <p/>
 * 对于其他外部,如果需要获取到SpringBoot需要自动扫描的包,则可以获取BasePackages这个Bean
 * @see {@link AutoConfigurationPackages#get}
 * BeanName固定为AutoConfigurationPackages.class.getName
 */
@Import(AutoConfigurationPackages.Registrar.class)
@interface AutoConfigurationPackage {

}

// 自动配置标注了@AutoConfigurationPackage类作为需要扫描的包名
class AutoConfigurationPackages {
    private static final String BEAN = AutoConfigurationPackages.class.getName();

    static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
        @Override
        public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
            // 注册BasePackages的Bean对象,保存了标注@AutoConfigurationPackage注解的类对应的包名
            AutoConfigurationPackages.register(registry, new PackageImport(metadata).getPackageName());
        }

        // 确定导入包的信息
        @Override
        public Set<Object> determineImports(AnnotationMetadata metadata) {
            return Collections.singleton(new PackageImport(metadata));
        }
    }

    // 是否存在BasePackages的Bean对象
    public static boolean has(BeanFactory beanFactory) {
        return beanFactory.containsBean(BEAN) && !AutoConfigurationPackages.get(beanFactory).isEmpty();
    }

    // 获取需要扫描的包对应的Bean对象
    public static List<String> get(BeanFactory beanFactory) {
        try {
            return beanFactory.getBean(BEAN, BasePackages.class).get();
        } catch (NoSuchBeanDefinitionException ex) {
            throw new IllegalStateException("Unable to retrieve @EnableAutoConfiguration base packages");
        }
    }

    // 注册BasePackages的Bean对象,保存了标注@AutoConfigurationPackage注解的类对应的包名
    public static void register(BeanDefinitionRegistry registry, String... packageNames) {
        // 如果容器中包含"AutoConfigurationPackages" => BasePackages这个Bean
        if (registry.containsBeanDefinition(BEAN)) {
            // 获取该Bean的BeanDefinition
            BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
            // 添加构造参数
            ConstructorArgumentValues constructorArguments = beanDefinition.getConstructorArgumentValues();
            // 将构造函数参数中的需要扫描的包名与给定的包名进行合并
            String[] packages = AutoConfigurationPackages.addBasePackages(constructorArguments, packageNames);
            // 重新添加进去
            constructorArguments.addIndexedArgumentValue(0, packages);
            return;
        }
        // 如果不存在BasePackagesBean,创建一个BasePackages的Bean对象
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClass(BasePackages.class);
        // 添加构造参数
        beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);
        beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        // 注册Bean
        registry.registerBeanDefinition(BEAN, beanDefinition);
    }

    // 将构造函数参数中的需要扫描的包名与给定的包名进行合并
    private static String[] addBasePackages(ConstructorArgumentValues constructorArguments, String[] packageNames) {
        String[] existing = (String[]) constructorArguments.getIndexedArgumentValue(0, String[].class).getValue();
        Set<String> merged = new LinkedHashSet<>();
        merged.addAll(Arrays.asList(existing));
        merged.addAll(Arrays.asList(packageNames));
        return StringUtils.toStringArray(merged);
    }


    // 保存标注了@AutoConfigurationPackage类的包名
    public static final class PackageImport {

        // 保存标注了@AutoConfigurationPackage类的包名
        private final String packageName;

        // 注解的元信息
        public PackageImport(AnnotationMetadata metadata) {
            // 获取到标注注解的类对应的类名
            this.packageName = ClassUtils.getPackageName(metadata.getClassName());
        }

        public String getPackageName() {
            return this.packageName;
        }
    }

    // 保存所有需要扫描的包名
    // 该对象会作为Spring中的Bean对象
    public static final class BasePackages {
        // 需要扫描的包名,该字段是通过beanDefinition.getConstructorArgumentValues()通过构造参数传递的
        private final List<String> packages;

        public BasePackages(String... names) {
            List<String> packages = new ArrayList<>();
            // 遍历所有的包名
            for (String name : names) {
                // 保存这些需要扫描的包名
                packages.add(name);
            }
            this.packages = packages;
        }

        List<String> get() {
            return this.packages;
        }
    }

}

// 该类是一个延迟导入的类,通过@EnableAutoConfiguration注解导入的,该类会处理spring.factories中的配置类
class AutoConfigurationImportSelector implements DeferredImportSelector {
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        // 是否开启了自动导入功能,默认开启
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        }
        // 加载所有的META-INF/spring-autoconfigure-metadata.properties配置文件,将全部保存到一个properties中
        // 该配置文件存储着自动配置类的装配条件,该配置文件是对自动配置的优化,这样就不需要获取类中的元数据注解再来决定类如何装配,实际上这些与在类中配置的注解效果是一样的
        // 例如: ConditionalOnSingleCandidate,ConditionalOnClass,AutoConfigureAfter
        //      AutoConfigureOrder,ConditionalOnBean,AutoConfigureBefore
        // 并将properties封装成PropertiesAutoConfigurationMetadata对象
        AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
        // 获取所有符合条件的需要自动导入的配置类,以及需要排除的配置类封装成自动导入条目对象返回
        AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
        // 返回所有符合条件的配置类
        return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }

    /**
     * 获取所有符合条件的需要自动导入的配置类,以及需要排除的配置类封装成自动导入条目对象返回
     * <pre>
     *     步骤:
     *          1. 检查自动导入功能是否开启,如果开启了接下一步(key: spring.boot.enableautoconfiguration)
     *          2. 从spring.factories中获取EnableAutoConfiguration的key对应所有类的名称
     * </pre>
     *
     * @param autoConfigurationMetadata // 加载所有的META-INF/spring-autoconfigure-metadata.properties配置文件,将全部保存到一个properties中
     *                                  // 该配置文件存储着自动配置类的装配条件,该配置文件是对自动配置的优化,这样就不需要获取类中的元数据注解再来决定类如何装配,实际上这些与在类中配置的注解效果是一样的
     *                                  // 例如: ConditionalOnSingleCandidate,ConditionalOnClass,AutoConfigureAfter
     *                                  //      AutoConfigureOrder,ConditionalOnBean,AutoConfigureBefore
     *                                  // 并将properties封装成PropertiesAutoConfigurationMetadata对象
     *                                  并将properties封装成PropertiesAutoConfigurationMetadata对象
     * @param annotationMetadata        标注@EnableAutoConfiguration注解所在的类的元数据
     * @return 所有自动导入的条目信息
     */
    protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
        // 是否开启了自动导入功能,默认开启
        if (!this.isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        }
        // 获取@EnableAutoConfiguration的属性信息
        AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
        // 从spring.factories中获取EnableAutoConfiguration的key对应所有类的名称
        List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
        // 删除重复的配置,就是将List使用Set去重
        configurations = this.removeDuplicates(configurations);
        // 获取需要排除的类
        Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
        // 校验排除的类是否是一个正确的类,是在spring.factories中存在的类,如果不存在,抛出异常
        this.checkExcludedClasses(configurations, exclusions);
        // 从所有的配置类中删除要排除的类
        configurations.removeAll(exclusions);
        // 从spring.factories文件中加载AutoConfigurationImportFilter类型的实现类对自己装配的类进行过滤
        // 返回过滤后的配置类集合
        configurations = this.filter(configurations, autoConfigurationMetadata);
        // 从spring.factories文件中加载AutoConfigurationImportListener类型的实现类
        // 并触发这些监听器的onAutoConfigurationImportEvent方法
        this.fireAutoConfigurationImportEvents(configurations, exclusions);
        // 将所有需要导入的配置类以及需要排除的配置类封装成自动导入条目对象返回
        return new AutoConfigurationEntry(configurations, exclusions);
    }

    // 从spring.factories文件中加载AutoConfigurationImportListener类型的实现类
    // 并触发这些监听器的onAutoConfigurationImportEvent方法
    private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
        // 从spring.factories文件中加载AutoConfigurationImportListener类型的实现类
        List<AutoConfigurationImportListener> listeners = this.getAutoConfigurationImportListeners();
        // 如果存在监听器
        if (!listeners.isEmpty()) {
            // 发布一个自动导入配置的事件,将当前类,所有符合条件的配置类,以及需要排除的类封装
            AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
            // 遍历所有的监听器
            for (AutoConfigurationImportListener listener : listeners) {
                // 执行这些Aware回调方法
                this.invokeAwareMethods(listener);
                // 回调监听器的onAutoConfigurationImportEvent方法
                listener.onAutoConfigurationImportEvent(event);
            }
        }
    }

    // 执行对象的Aware接口方法
    private void invokeAwareMethods(Object instance) {
        if (instance instanceof Aware) {
            if (instance instanceof BeanClassLoaderAware) {
                ((BeanClassLoaderAware) instance).setBeanClassLoader(this.beanClassLoader);
            }
            if (instance instanceof BeanFactoryAware) {
                ((BeanFactoryAware) instance).setBeanFactory(this.beanFactory);
            }
            if (instance instanceof EnvironmentAware) {
                ((EnvironmentAware) instance).setEnvironment(this.environment);
            }
            if (instance instanceof ResourceLoaderAware) {
                ((ResourceLoaderAware) instance).setResourceLoader(this.resourceLoader);
            }
        }
    }

    // 从spring.factories文件中加载AutoConfigurationImportListener类型的实现类
    protected List<AutoConfigurationImportListener> getAutoConfigurationImportListeners() {
        return SpringFactoriesLoader.loadFactories(AutoConfigurationImportListener.class, this.beanClassLoader);
    }

    // 从spring.factories文件中加载AutoConfigurationImportFilter类型的实现类
    protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
        return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);
    }

    // 从spring.factories文件中加载AutoConfigurationImportFilter类型的实现类对自己装配的类进行过滤
    // 返回过滤后的配置类集合
    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;
        // 从spring.factories文件中加载AutoConfigurationImportFilter类型的实现类
        List<AutoConfigurationImportFilter> filters = this.getAutoConfigurationImportFilters();
        // 遍历所有的过滤器
        for (AutoConfigurationImportFilter filter : filters) {
            // 执行filter的Aware方法
            // BeanClassLoaderAware,BeanFactoryAware,EnvironmentAware,ResourceLoaderAware
            this.invokeAwareMethods(filter);
            // 调用filter的过滤方法,返回一个对应这所有候选配置类数组是否被排除的数组,一一对应
            // 这里就是FilteringSpringBootCondition条件过滤器
            // 处理ConditionalOnBean,ConditionalOnSingleCandidate,ConditionalOnClass,ConditionalOnMissingClass,ConditionalOnWebApplication这些注解
            boolean[] match = filter.match(candidates, autoConfigurationMetadata);
            // 遍历这个对每一个类匹配的结果
            for (int i = 0; i < match.length; i++) {
                // 如果匹配失败
                if (!match[i]) {
                    // 标识当前配置类需要被跳过
                    skip[i] = true;
                    // 将候选的类置空,因为这是一个循环,还有多个AutoConfigurationImportFilter要进行处理
                    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]);
            }
        }
        // 返回没有被跳过的类
        return new ArrayList<>(result);
    }

    // 校验排除的类是否是一个正确的类,是在spring.factories中存在的类,如果不存在,抛出异常
    private void checkExcludedClasses(List<String> configurations, Set<String> exclusions) {
        List<String> invalidExcludes = new ArrayList<>(exclusions.size());
        // 遍历已经要排除的类
        for (String exclusion : exclusions) {
            // 如果该类确实存在,并且在需要加载的所有配置类中不存在该排序的类
            if (ClassUtils.isPresent(exclusion, getClass().getClassLoader()) && !configurations.contains(exclusion)) {
                // 那么,该类表示无效的排除类
                invalidExcludes.add(exclusion);
            }
        }
        // 具有无效的排除类,抛出异常
        if (!invalidExcludes.isEmpty()) {
            this.handleInvalidExcludes(invalidExcludes);
        }
    }

    // 获取需要排除的类
    protected Set<String> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        Set<String> excluded = new LinkedHashSet<>();
        // 获取@EnableAutoConfiguration属性exclude的值
        excluded.addAll(asList(attributes, "exclude"));
        // 获取@EnableAutoConfiguration属性excludeName的值
        excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName")));
        // 获取环境配置中的spring.autoconfigure.exclude的属性值这个配置的值表示忽略哪些文件
        excluded.addAll(this.getExcludeAutoConfigurationsProperty());
        return excluded;
    }

    // 获取环境配置中的spring.autoconfigure.exclude的属性值这个配置的值表示忽略哪些文件
    private List<String> getExcludeAutoConfigurationsProperty() {
        if (getEnvironment() instanceof ConfigurableEnvironment) {
            Binder binder = Binder.get(getEnvironment());
            // 获取环境对象中spring.autoconfigure.exclude的属性,并绑定到字符串数组中返回
            return binder.bind(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class).map(Arrays::asList).orElse(Collections.emptyList());
        }
        // 获取环境配置spring.autoconfigure.exclude的值
        String[] excludes = getEnvironment().getProperty(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class);
        return (excludes != null) ? Arrays.asList(excludes) : Collections.emptyList();
    }

    // 从spring.factories中获取EnableAutoConfiguration的key对应所有类的名称
    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        // 从spring.factories中获取EnableAutoConfiguration的key对应所有类的名称
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class, getBeanClassLoader());
        return configurations;
    }


    // 是否开启了自动导入功能
    protected boolean isEnabled(AnnotationMetadata metadata) {
        if (getClass() == AutoConfigurationImportSelector.class) {
            // 获取配置属性为"spring.boot.enableautoconfiguration"的值,默认为true
            return getEnvironment().getProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class, true);
        }
        return true;
    }

    // 自动配置的元数据加载器
    public class AutoConfigurationMetadataLoader {
        protected static final String PATH = "META-INF/spring-autoconfigure-metadata.properties";

        // 加载所有的META-INF/spring-autoconfigure-metadata.properties配置文件,将全部保存到一个properties中
        // 该配置文件存储着自动配置类的装配条件,该配置文件是对自动配置的优化,这样就不需要获取类中的元数据注解再来决定类如何装配,实际上这些与在类中配置的注解效果是一样的
        // 并将properties封装成PropertiesAutoConfigurationMetadata对象
        public static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
            return AutoConfigurationMetadataLoader.loadMetadata(classLoader, PATH);
        }


        // 加载所有的META-INF/spring-autoconfigure-metadata.properties配置文件,将全部保存到一个properties中
        // 该配置文件存储着自动配置类的装配条件,该配置文件是对自动配置的优化,这样就不需要获取类中的元数据注解再来决定类如何装配,实际上这些与在类中配置的注解效果是一样的
        // 例如: ConditionalOnSingleCandidate,ConditionalOnClass,AutoConfigureAfter
        //      AutoConfigureOrder,ConditionalOnBean,AutoConfigureBefore
        // 并将properties封装成PropertiesAutoConfigurationMetadata对象
        public static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) {
            // 加载所有的META-INF/spring-autoconfigure-metadata.properties配置文件
            Enumeration<URL> urls = (classLoader != null) ? classLoader.getResources(path) : ClassLoader.getSystemResources(path);
            // 保存META-INF/spring-autoconfigure-metadata.properties中的所有配置
            Properties properties = new Properties();
            // 遍历所有的文件
            while (urls.hasMoreElements()) {
                // 将该配置类中所有的键值对保存到properties中
                properties.putAll(PropertiesLoaderUtils.loadProperties(new UrlResource(urls.nextElement())));
            }
            // 将所有属性配置封装为AutoConfigurationMetadata对象
            return AutoConfigurationMetadataLoader.loadMetadata(properties) {
                // 该对象就是从properties中获取对应的属性值
                return new PropertiesAutoConfigurationMetadata(properties);
            }
        }
    }

    // 延迟导入处理类的分组
    public static class AutoConfigurationGroup implements DeferredImportSelector.Group {
        // 所有需要导入的配置类=>导入的来源类的映射关系
        private final Map<String, AnnotationMetadata> entries = new LinkedHashMap<>();
        // 所有需要自动导入的配置类条目信息,每一个AutoConfigurationEntry对象包含了这个模块对应所有需要导入的类
        private final List<AutoConfigurationEntry> autoConfigurationEntries = new ArrayList<>();

        // 开始处理延迟导入的Bean操作
        public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
            // 将AutoConfigurationImportSelector类解析好的AutoConfigurationEntry获取出来
            AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector).getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
            // 保存到当前AutoConfigurationGroup类中
            this.autoConfigurationEntries.add(autoConfigurationEntry);
            // 遍历所有需要导入的配置类
            for (String importClassName : autoConfigurationEntry.getConfigurations()) {
                // 保存所有需要保存的配置类
                this.entries.putIfAbsent(importClassName, annotationMetadata);
            }
        }

        // 获取需要导入的所有类
        // 将配置类转换为Entry类型,key为: 导入该配置类的来源类 ,值为: 该配置类名称
        public Iterable<Entry> selectImports() {
            // 如果没有需要自动导入的条目
            if (this.autoConfigurationEntries.isEmpty()) {
                return Collections.emptyList();
            }
            // 过滤所有条目中需要排除的类
            Set<String> allExclusions = this.autoConfigurationEntries.stream().map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet());
            // 获取所有条目中需要导入的类
            Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
                    .map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream)
                    .collect(Collectors.toCollection(LinkedHashSet::new));
            // 将所有导入的类中删除需要排除的类
            processedConfigurations.removeAll(allExclusions);
            // 将所有配置类的优先级排序序之后
            return this.sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata())
                    .stream()
                    // 将配置类转换为Entry类型,key为: 导入该配置类的来源类 ,值为: 该配置类名称
                    .map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
                    .collect(Collectors.toList());
        }

        // 返回的是PropertiesAutoConfigurationMetadata
        // 加载所有的META-INF/spring-autoconfigure-metadata.properties配置文件,将全部保存到一个properties中
        // 该配置文件存储着自动配置类的装配条件,该配置文件是对自动配置的优化,这样就不需要获取类中的元数据注解再来决定类如何装配,实际上这些与在类中配置的注解效果是一样的
        // 例如: ConditionalOnSingleCandidate,ConditionalOnClass,AutoConfigureAfter
        //      AutoConfigureOrder,ConditionalOnBean,AutoConfigureBefore
        // 并将properties封装成PropertiesAutoConfigurationMetadata对象
        private AutoConfigurationMetadata getAutoConfigurationMetadata() {
            if (this.autoConfigurationMetadata == null) {
                // 加载所有的META-INF/spring-autoconfigure-metadata.properties配置文件,将全部保存到autoConfigurationMetadata一个properties中
                this.autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
            }
            return this.autoConfigurationMetadata;
        }

        // 对说中配置类进行排序,最终得到所有配置类的加载顺序
        private List<String> sortAutoConfigurations(Set<String> classNames, AutoConfigurationMetadata autoConfigurationMetadata) {
            // 获取自动配置类的排序器
            AutoConfigurationSorter sorter = new AutoConfigurationSorter(getMetadataReaderFactory(), autoConfigurationMetadata);
            // 根据优先级排序,Order接口以及@AutoConfigureBefore @AutoConfigureAfter注解
            // 最终得到所有配置类的加载顺序
            List<String> result = sorter.getInPriorityOrder(classNames);
            return result;
        }

        // 获取"org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory"对应的元数据加载的工厂类的Bean
        private MetadataReaderFactory getMetadataReaderFactory() {
            // 获取"org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory"对应的元数据加载的工厂类的Bean
            return this.beanFactory.getBean(SharedMetadataReaderFactoryContextInitializer.BEAN_NAME, MetadataReaderFactory.class);
        }
    }

    // 自动配置类的排序器
    class AutoConfigurationSorter {
        // 类元数据的解析工厂
        private final MetadataReaderFactory metadataReaderFactory;
        // 自动配置类的属性配置元数据对象
        // 加载所有的META-INF/spring-autoconfigure-metadata.properties配置文件,将全部保存到一个properties中
        // 该配置文件存储着自动配置类的装配条件,该配置文件是对自动配置的优化,这样就不需要获取类中的元数据注解再来决定类如何装配,实际上这些与在类中配置的注解效果是一样的
        // 例如: ConditionalOnSingleCandidate,ConditionalOnClass,AutoConfigureAfter
        //      AutoConfigureOrder,ConditionalOnBean,AutoConfigureBefore
        // 并将properties封装成PropertiesAutoConfigurationMetadata对象
        private final AutoConfigurationMetadata autoConfigurationMetadata;

        // 根据优先级排序,Order接口以及@AutoConfigureBefore @AutoConfigureAfter注解
        // 最终得到所有配置类的加载顺序
        public List<String> getInPriorityOrder(Collection<String> classNames) {
            // 所有需要导入的类名,所有的META-INF/spring-autoconfigure-metadata.properties配置文件的数据对象包装成自动配置的类中AutoConfigurationClasses
            AutoConfigurationClasses classes = new AutoConfigurationClasses(this.metadataReaderFactory, this.autoConfigurationMetadata, classNames);
            List<String> orderedClassNames = new ArrayList<>(classNames);
            // 根据自然顺序进行排序
            Collections.sort(orderedClassNames);
            // 再根据Order接口进行排序
            orderedClassNames.sort((o1, o2) -> {
                int i1 = classes.get(o1).getOrder();
                int i2 = classes.get(o2).getOrder();
                return Integer.compare(i1, i2);
            });
            // 根据注解排序 @AutoConfigureBefore @AutoConfigureAfter
            orderedClassNames = this.sortByAnnotation(classes, orderedClassNames);
            return orderedClassNames;
        }

        // 根据注解排序 @AutoConfigureBefore @AutoConfigureAfter
        private List<String> sortByAnnotation(AutoConfigurationClasses classes, List<String> classNames) {
            // 所有需要排序的类
            List<String> toSort = new ArrayList<>(classNames);
            // 获取所有需要自动装配的配置类,包含了与给定的配置类存在加载关系的类
            toSort.addAll(classes.getAllNames());
            // 已经拍好序类
            Set<String> sorted = new LinkedHashSet<>();
            // 已经处理过的类
            Set<String> processing = new LinkedHashSet<>();
            // 如果有需要排序的类
            while (!toSort.isEmpty()) {
                // 使用@AfterAnnotation进行排序
                this.doSortByAfterAnnotation(classes, toSort, sorted, processing, null);
            }
            // 从已排好序的配置类中,删除在classNames中不存在的配置类
            // 因为我们只需要classNames中的元素
            sorted.retainAll(classNames);
            // 返回排好序的结果
            return new ArrayList<>(sorted);
        }

        //

        /**
         * 使用@AutoConfigureAfter注解进行排序,为什么没有按照@AutoConfigureBefore注解排序方法呢?
         * 因为{@link AutoConfigurationClasses#getClassesRequestedAfter(String)}中,统一时候后置进行排序
         * <pre>
         * 什么意思呢?
         *           如果我在你前面,那么,你就在我后面,因此,按照这种套路,就可以找到所有标记了我在他前面的类
         *           这样,就能拿到我后面的所有类,这样,从第一个类开始,就可以按照先后顺序排到最后一个
         * </pre>
         *
         * @param classes    需要排序的所有的类
         * @param toSort     需要排序的类
         * @param sorted     已经排好序的类
         * @param processing 正在进行排序的类
         * @param current    当前正在排序的类
         */
        private void doSortByAfterAnnotation(AutoConfigurationClasses classes, List<String> toSort, Set<String> sorted, Set<String> processing, String current) {
            // 如果当前排序的类为空,表示第一次使用这个类,取出第一个类开始排序
            // 如果不为空,表示是通过递归处理的
            if (current == null) {
                current = toSort.remove(0);
            }
            // 保存正在处理的类
            processing.add(current);
            // 获取需要在当前类之后加载的类
            Set<String> classesRequestedAfter = classes.getClassesRequestedAfter(current);
            for (String after : classesRequestedAfter) {
                // 如果未拍好序,并且该类需要排序
                if (!sorted.contains(after) && toSort.contains(after)) {
                    // 递归排序,因为要一层一层排
                    // 例如: a要在b后面,a还要在c后面,b又要在c后面,这种顺序逻辑关系
                    // 最终得到 a在b后面,a也在c后面
                    this.doSortByAfterAnnotation(classes, toSort, sorted, processing, after);
                }
            }
            // 该类存储完毕,移除该类
            processing.remove(current);
            // 表示该类已经排好序了
            sorted.add(current);
        }


        // 所有自动配置的类信息
        public static class AutoConfigurationClasses {
            // 保存所有需要自动装配的配置类,包含了与给定的配置类存在加载关系的类
            private final Map<String, AutoConfigurationClass> classes = new HashMap<>();

            /**
             * @param metadataReaderFactory     解析类元数据的工厂
             * @param autoConfigurationMetadata // 加载所有的META-INF/spring-autoconfigure-metadata.properties配置文件,将全部保存到一个properties中
             *                                  // 该配置文件存储着自动配置类的装配条件,该配置文件是对自动配置的优化,这样就不需要获取类中的元数据注解再来决定类如何装配,实际上这些与在类中配置的注解效果是一样的
             *                                  // 例如: ConditionalOnSingleCandidate,ConditionalOnClass,AutoConfigureAfter
             *                                  //      AutoConfigureOrder,ConditionalOnBean,AutoConfigureBefore
             *                                  // 并将properties封装成PropertiesAutoConfigurationMetadata对象
             * @param classNames                所有需要处理的配置类
             */
            public AutoConfigurationClasses(MetadataReaderFactory metadataReaderFactory, AutoConfigurationMetadata autoConfigurationMetadata, Collection<String> classNames) {
                // 将所有符合装配条件的配置类通过META-INF/spring-autoconfigure-metadata.properties或者@AutoConfigureOrder,@AutoConfigureAfter,@AutoConfigureBefore注解描述的关系
                // 将这些存在关系的类也进行保存,到时候也需要加载
                this.addToClasses(metadataReaderFactory, autoConfigurationMetadata, classNames, true);
            }

            /**
             * 将所有符合装配条件的配置类通过META-INF/spring-autoconfigure-metadata.properties或者@AutoConfigureOrder,@AutoConfigureAfter,@AutoConfigureBefore注解描述的关系
             * 将这些存在关系的类也进行保存,到时候也需要加载
             *
             * @param metadataReaderFactory     解析类元数据的工厂
             * @param autoConfigurationMetadata // 加载所有的META-INF/spring-autoconfigure-metadata.properties配置文件,将全部保存到一个properties中
             *                                  // 该配置文件存储着自动配置类的装配条件,该配置文件是对自动配置的优化,这样就不需要获取类中的元数据注解再来决定类如何装配,实际上这些与在类中配置的注解效果是一
             *                                  // 例如: ConditionalOnSingleCandidate,ConditionalOnClass,AutoConfigureAfter
             *                                  //      AutoConfigureOrder,ConditionalOnBean,AutoConfigureBefore
             *                                  // 并将properties封装成PropertiesAutoConfigurationMetadata对象
             * @param classNames                所有需要处理的配置类
             * @param required                  是否必须加载
             */
            private void addToClasses(MetadataReaderFactory metadataReaderFactory, AutoConfigurationMetadata autoConfigurationMetadata, Collection<String> classNames, boolean required) {
                // 遍历所有的类名
                for (String className : classNames) {
                    // 如果该classes没有包含,表示没有处理过该类
                    if (!this.classes.containsKey(className)) {
                        // 创建单个自动配置类的信息,比如,要在那个类之后加载,之前加载等等
                        AutoConfigurationClass autoConfigurationClass = new AutoConfigurationClass(className, metadataReaderFactory, autoConfigurationMetadata);
                        // 该配置类是否可用
                        // 这个类在META-INF/spring-autoconfigure-metadata.properties中存在,或者可以成功加载这个类
                        boolean available = autoConfigurationClass.isAvailable();
                        // 如果必须,或者可用,required: 从构造方法调用的,为true,递归调用的为false
                        if (required || available) {
                            // 保存需要自动装配的配置类
                            this.classes.put(className, autoConfigurationClass);
                        }
                        // 配置类可用
                        if (available) {
                            // 不断递归添加要在该类之前加载的类
                            this.addToClasses(metadataReaderFactory, autoConfigurationMetadata, autoConfigurationClass.getBefore(), false);
                            // 不断递归添加要在该类后加载的类
                            this.addToClasses(metadataReaderFactory, autoConfigurationMetadata, autoConfigurationClass.getAfter(), false);
                        }
                    }
                }
            }

            // 获取所有需要自动装配的配置类,包含了与给定的配置类存在加载关系的类
            public Set<String> getAllNames() {
                return this.classes.keySet();
            }

            // 获取指定配置类的自动装配信息,比如,要在那个类之后加载,之前加载等等
            public AutoConfigurationClass get(String className) {
                return this.classes.get(className);
            }

            // 需要在该指定类之后加载的类
            public Set<String> getClassesRequestedAfter(String className) {
                // 获取需要在给定配置类之后加载的类名
                Set<String> after = this.get(className).getAfter();
                // 获取需要在该指定类之后加载的类
                Set<String> classesRequestedAfter = new LinkedHashSet<>(after);
                // 遍历所有需要加载的配置类信息
                this.classes.forEach((String name, AutoConfigurationClass autoConfigurationClass) -> {
                    // 获取需要在正在遍历的配置类之前加载的类,如果这些类中包含当前指定的类
                    // 那么表示指定的类就需要在正在遍历的类后面加载
                    if (autoConfigurationClass.getBefore().contains(className)) {
                        // 保存需要在当前指定的类后面加载的类
                        // 说白了就是className需要在正在遍历的类之前加载,反过来,正在遍历的类需要在指定的当前类之后加载
                        classesRequestedAfter.add(name);
                    }
                });
                // 需要在该指定类之后加载的类
                return classesRequestedAfter;
            }
        }

        // 单个自动配置类的信息,比如,要在那个类之后加载,之前加载等等
        public static class AutoConfigurationClass {
            // 配置类类名
            private final String className;
            // 解析类的元数据工厂
            private final MetadataReaderFactory metadataReaderFactory;
            // 加载所有的META-INF/spring-autoconfigure-metadata.properties配置文件,将全部保存到一个properties中
            // 该配置文件存储着自动配置类的装配条件,该配置文件是对自动配置的优化,这样就不需要获取类中的元数据注解再来决定类如何装配,实际上这些与在类中配置的注解效果是一样的
            // 例如: ConditionalOnSingleCandidate,ConditionalOnClass,AutoConfigureAfter
            //      AutoConfigureOrder,ConditionalOnBean,AutoConfigureBefore
            // 并将properties封装成PropertiesAutoConfigurationMetadata对象
            private final AutoConfigurationMetadata autoConfigurationMetadata;
            // 该配置类className对应的注解元信息
            private volatile AnnotationMetadata annotationMetadata;
            // 当前配置类在META-INF/spring-autoconfigure-metadata.properties配置的AutoConfigureBefore的类
            // 也就是需要在当前类之前进行配置的类
            private volatile Set<String> before;
            // 当前配置类在META-INF/spring-autoconfigure-metadata.properties配置的AutoConfigureAfter的类
            // 也就是需要在当前类之后进行配置的类
            private volatile Set<String> after;

            public AutoConfigurationClass(String className, MetadataReaderFactory metadataReaderFactory, AutoConfigurationMetadata autoConfigurationMetadata) {
                this.className = className;
                this.metadataReaderFactory = metadataReaderFactory;
                this.autoConfigurationMetadata = autoConfigurationMetadata;
            }

            // 该配置类是否可用
            // 这个类在META-INF/spring-autoconfigure-metadata.properties中存在,或者可以成功加载这个类
            public boolean isAvailable() {
                try {
                    // 该类在META-INF/spring-autoconfigure-metadata.properties是否存在配置信息
                    if (!this.wasProcessed()) {
                        // 获取该配置类对应的注解元信息,如果能获取到,表示该类存在,加载失败可能没引入这个类
                        this.getAnnotationMetadata();
                    }
                    // 这个类在META-INF/spring-autoconfigure-metadata.properties中存在,或者可以成功加载这个类
                    return true;
                } catch (Exception ex) {
                    // 该类不存在
                    return false;
                }
            }

            // 获取当前配置类在META-INF/spring-autoconfigure-metadata.properties配置文件中标识的AutoConfigureBefore对应的类
            public Set<String> getBefore() {
                if (this.before == null) {
                    // 所有的META-INF/spring-autoconfigure-metadata.properties配置文件的数据
                    this.before = (this.wasProcessed() ?
                            this.autoConfigurationMetadata.getSet(this.className, "AutoConfigureBefore", Collections.emptySet())
                            // 如果META-INF/spring-autoconfigure-metadata.properties配置文件中不存在,就只能加载类中的注解来获取
                            // 因为META-INF/spring-autoconfigure-metadata.properties中是对自动配置的优化,不用去解析类的元数据
                            : this.getAnnotationValue(AutoConfigureBefore.class));
                }
                return this.before;
            }

            // 获取当前配置类在META-INF/spring-autoconfigure-metadata.properties配置文件中标识的AutoConfigureAfter对应的类
            public Set<String> getAfter() {
                if (this.after == null) {
                    // 所有的META-INF/spring-autoconfigure-metadata.properties配置文件的数据
                    this.after = (this.wasProcessed() ? this.autoConfigurationMetadata.getSet(this.className, "AutoConfigureAfter", Collections.emptySet())
                            // 如果META-INF/spring-autoconfigure-metadata.properties配置文件中不存在,就只能加载类中的注解来获取
                            // 因为META-INF/spring-autoconfigure-metadata.properties中是对自动配置的优化,不用去解析类的元数据
                            : this.getAnnotationValue(AutoConfigureAfter.class));
                }
                return this.after;
            }

            // 获取当前配置类在META-INF/spring-autoconfigure-metadata.properties配置文件中标识的AutoConfigureOrder对应的类
            private int getOrder() {
                // 该类在META-INF/spring-autoconfigure-metadata.properties是否存在配置信息
                if (this.wasProcessed()) {
                    // 默认Order为0
                    return this.autoConfigurationMetadata.getInteger(this.className, "AutoConfigureOrder", AutoConfigureOrder.DEFAULT_ORDER);
                }
                // 该类在META-INF/spring-autoconfigure-metadata.properties不存在配置,就只能加载类中的注解来获取
                // 因为META-INF/spring-autoconfigure-metadata.properties中是对自动配置的优化,不用去解析类的元数据
                Map<String, Object> attributes = this.getAnnotationMetadata().getAnnotationAttributes(AutoConfigureOrder.class.getName());
                // 获取@AutoConfigureOrder注解对应的属性配置,默认值为:0
                return (attributes != null) ? (Integer) attributes.get("value") : AutoConfigureOrder.DEFAULT_ORDER;
            }

            // 该类在META-INF/spring-autoconfigure-metadata.properties是否存在配置信息
            private boolean wasProcessed() {
                // 该类在META-INF/spring-autoconfigure-metadata.properties中配置了,autoConfigurationMetadata中的properties中存在该类的Key,就表示需要被加载
                return (this.autoConfigurationMetadata != null && this.autoConfigurationMetadata.wasProcessed(this.className));
            }

            /**
             * 获取指定注解的属性,包含@AutoConfigureAfter,@AutoConfigureBefore
             *
             * @param annotation 注解信息,@AutoConfigureAfter,@AutoConfigureBefore
             * @return 该注解配置的value和name值
             */
            private Set<String> getAnnotationValue(Class<?> annotation) {
                // 获取该类的元数据中的指定注解信息
                Map<String, Object> attributes = this.getAnnotationMetadata().getAnnotationAttributes(annotation.getName(), true);
                // 如果不存在指定注解
                if (attributes == null) {
                    return Collections.emptySet();
                }
                // 存在指定注解
                Set<String> value = new LinkedHashSet<>();
                // 保存该注解的value和name值
                Collections.addAll(value, (String[]) attributes.get("value"));
                Collections.addAll(value, (String[]) attributes.get("name"));
                return value;
            }

            // 获取该配置类对应的注解元信息
            private AnnotationMetadata getAnnotationMetadata() {
                if (this.annotationMetadata == null) {
                    // 加载该配置类的元信息
                    MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(this.className);
                    // 获取该配置类对应的注解元信息
                    this.annotationMetadata = metadataReader.getAnnotationMetadata();
                }
                return this.annotationMetadata;
            }

        }

        // 加载所有的META-INF/spring-autoconfigure-metadata.properties配置文件,将全部保存到一个properties中
        // 该配置文件存储着自动配置类的装配条件,这些与在类中配置的注解效果是一样的
        // 例如: ConditionalOnSingleCandidate,ConditionalOnClass,AutoConfigureAfter
        //      AutoConfigureOrder,ConditionalOnBean,AutoConfigureBefore
        // 并将properties封装成PropertiesAutoConfigurationMetadata对象
        public static class PropertiesAutoConfigurationMetadata implements AutoConfigurationMetadata {
            // 保存这所有META-INF/spring-autoconfigure-metadata.properties的配置信息
            private final Properties properties;

            public PropertiesAutoConfigurationMetadata(Properties properties) {
                this.properties = properties;
            }

            // 该类在META-INF/spring-autoconfigure-metadata.properties配置文件是否存在
            public boolean wasProcessed(String className) {
                return this.properties.containsKey(className);
            }

            // 获取int类型的值
            public Integer getInteger(String className, String key) {
                return getInteger(className, key, null);
            }

            public Integer getInteger(String className, String key, Integer defaultValue) {
                String value = get(className, key);
                return (value != null) ? Integer.valueOf(value) : defaultValue;
            }

            // 获取set类型的值
            public Set<String> getSet(String className, String key) {
                return getSet(className, key, null);
            }

            public Set<String> getSet(String className, String key, Set<String> defaultValue) {
                String value = get(className, key);
                return (value != null) ? StringUtils.commaDelimitedListToSet(value) : defaultValue;
            }

            // 获取String类型的值
            public String get(String className, String key) {
                return get(className, key, null);
            }

            public String get(String className, String key, String defaultValue) {
                String value = this.properties.getProperty(className + "." + key);
                return (value != null) ? value : defaultValue;
            }

        }

    }

}

/**
 * 处理@ImportAutoConfiguration注解的类
 *
 * @see {@link AutoConfigurationImportSelector}
 */
class ImportAutoConfigurationImportSelector extends AutoConfigurationImportSelector {
    // 可以解析的注解名
    private static final Set<String> ANNOTATION_NAMES;

    // 该类会处理@ImportAutoConfiguration和测试包的test.ImportAutoConfiguration注解
    static {
        Set<String> names = new LinkedHashSet<>();
        // @ImportAutoConfiguration
        names.add(ImportAutoConfiguration.class.getName());
        // 测试包的@ImportAutoConfiguration注解
        names.add("org.springframework.boot.autoconfigure.test.ImportAutoConfiguration");
        ANNOTATION_NAMES = Collections.unmodifiableSet(names);
    }

    /**
     * 确定需要导入的类
     *
     * @param metadata 标注@ImportAutoConfiguration类的元信息
     */
    public Set<Object> determineImports(AnnotationMetadata metadata) {
        // 获取候选的配置类,该类重写了默认的从spring.factories获取配置,而是从@ImportAutoConfiguration注解中获取需要导入的类
        List<String> candidateConfigurations = this.getCandidateConfigurations(metadata, null);
        Set<String> result = new LinkedHashSet<>(candidateConfigurations);
        // 获取需要排除的类
        Set<String> exclusions = this.getExclusions(metadata, null);
        // 在获取的所有候选配置类中,删除要排除的类
        result.removeAll(exclusions);
        // 返回所有符合条件的配置类
        return Collections.unmodifiableSet(result);
    }

    // 该类不处理
    @Override
    protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) {
        return null;
    }

    /**
     * 获取候选的配置类,该类重写了默认的从spring.factories获取配置
     *
     * @param metadata   标注@ImportAutoConfiguration类的元信息
     * @param attributes 注解属性信息,这个为空,因为当前类覆盖了父类的{@link ImportAutoConfigurationImportSelector#getAttributes}方法
     * @return 所有符合条件的配置类
     */
    @Override
    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        List<String> candidates = new ArrayList<>();
        // 收集入口配置类中所有的@ImportAutoConfiguration注解信息,包括注解中的,父类的
        // Key为出现@ImportAutoConfiguration的类的Class,Value为@ImportAutoConfiguration注解信息
        // 用List作为Value是同时为了处理org.springframework.boot.autoconfigure.test.ImportAutoConfiguration注解
        Map<Class<?>, List<Annotation>> annotations = this.getAnnotations(metadata);
        // 遍历所有收集到了@ImportAutoConfiguration注解信息
        annotations.forEach((source, sourceAnnotations) -> this.collectCandidateConfigurations(source, sourceAnnotations, candidates));
        // 返回所有需要加载的配置类
        return candidates;
    }

    /**
     * 收集@ImportAutoConfiguration注解中需要导入的配置类信息
     *
     * @see {@link ImportAutoConfigurationImportSelector#getConfigurationsForAnnotation}
     */
    private void collectCandidateConfigurations(Class<?> source, List<Annotation> annotations, List<String> candidates) {
        // 遍历该配置类中存在的所有@ImportAutoConfiguration注解
        // 用List作为Value是同时为了处理org.springframework.boot.autoconfigure.test.@ImportAutoConfiguration注解
        // 因为这个配置类可以同时配置这两个注解
        for (Annotation annotation : annotations) {
            // 从@ImportAutoConfiguration注解信息中,获取需要导入的配置类
            Collection<String> forAnnotation = this.getConfigurationsForAnnotation(source, annotation);
            // 保存所有注解中导入的所有类的信息
            candidates.addAll(forAnnotation);
        }
    }

    /**
     * 从@ImportAutoConfiguration注解信息中,获取需要导入的配置类
     * <pre>
     *      1. 如果@ImportAutoConfiguration标注了需要导入的类,那么导入的那个类才是配置类
     *      2. 如果@ImportAutoConfiguration没有标注需要导入的类,只有一个注解
     *           那么会加载标注该注解的类作为Key,从spring.factories获取对应的配置信息
     *           例如:
     *              @ImportAutoConfiguration
     *              class A{}
     *              那么,会自动从spring.factories中找a的全类名对应的配置类信息
     *     2.1 再举个例子: 在自己项目中,创建一个org.springframework.boot.autoconfigure.EnableAutoConfiguration类
     *                   在类中标注@ImportAutoConfiguration注解,并将EnableAutoConfiguration类设置为SpringApplication的配置类
     *                   那么,与@EnableAutoConfiguration的作用几乎一样,但是一般不会这么做
     *  </pre>
     *
     * @param source     标注@ImportAutoConfiguration注解的为了或者注解
     * @param annotation @ImportAutoConfiguration注解信息
     * @return 所有配置类信息
     */
    private Collection<String> getConfigurationsForAnnotation(Class<?> source, Annotation annotation) {
        // 获取@ImportAutoConfiguration注解的信息
        String[] classes = (String[]) AnnotationUtils.getAnnotationAttributes(annotation, true).get("classes");
        // 如果配置了导入的配置类,那么直接返回这个配置类
        if (classes.length > 0) {
            return Arrays.asList(classes);
        }
        // 如果没有配置,那么会加载标注该注解的类作为Key,从spring.factories获取对应的配置信息
        return loadFactoryNames(source);
    }

    // 根据指定类型名从spring.factories中获取该key对应的配置类信息
    protected Collection<String> loadFactoryNames(Class<?> source) {
        return SpringFactoriesLoader.loadFactoryNames(source, getClass().getClassLoader());
    }

    // 获取需要排除的类
    @Override
    protected Set<String> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        // 所有需要的排除的类
        Set<String> exclusions = new LinkedHashSet<>();
        // 解析标注了@ImportAutoConfiguration注解的类(入口配置类)
        Class<?> source = ClassUtils.resolveClassName(metadata.getClassName(), null);
        // 遍历当前类所有的支持处理的注解
        for (String annotationName : ANNOTATION_NAMES) {
            // 获取到注解的属性信息
            AnnotationAttributes merged = AnnotatedElementUtils.getMergedAnnotationAttributes(source, annotationName);
            // 获取注解中需要排除的类
            Class<?>[] exclude = (merged != null) ? merged.getClassArray("exclude") : null;
            // 如果配置了
            if (exclude != null) {
                // 保存需要排除的类名
                for (Class<?> excludeClass : exclude) {
                    exclusions.add(excludeClass.getName());
                }
            }
        }
        // 再解析入口配置类中所有的@ImportAutoConfiguration注解信息,包括注解中的,父类的
        // 获取收集到的所有@ImportAutoConfiguration信息
        List<Annotation>[] values = this.getAnnotations(metadata).values();
        for (List<Annotation> annotations : values) {
            // 遍历所有的@ImportAutoConfiguration注解
            for (Annotation annotation : annotations) {
                // 获取该注解的排除类属性
                String[] exclude = (String[]) AnnotationUtils.getAnnotationAttributes(annotation, true).get("exclude");
                // 如果不为空
                if (!ObjectUtils.isEmpty(exclude)) {
                    // 保存需要排除的类
                    exclusions.addAll(Arrays.asList(exclude));
                }
            }
        }
        return exclusions;
    }

    // 收集入口配置类中所有的@ImportAutoConfiguration注解信息,包括注解中的,父类的
    protected final Map<Class<?>, List<Annotation>> getAnnotations(AnnotationMetadata metadata) {
        MultiValueMap<Class<?>, Annotation> annotations = new LinkedMultiValueMap<>();
        // 解析标注了@ImportAutoConfiguration类的类名
        Class<?> source = ClassUtils.resolveClassName(metadata.getClassName(), null);
        // 收集入口配置类中所有的@ImportAutoConfiguration注解信息,包括注解中的,父类的
        this.collectAnnotations(source, annotations, new HashSet<>());
        return Collections.unmodifiableMap(annotations);
    }

    /**
     * 收集入口配置类中所有的@ImportAutoConfiguration注解信息,包括注解中的,父类的
     *
     * @param source      需要收集@ImportAutoConfiguration注解的类的信息
     * @param annotations 需要保存该source中的所有@ImportAutoConfiguration注解的类
     * @param seen        已经处理过的类
     */
    private void collectAnnotations(Class<?> source, MultiValueMap<Class<?>, Annotation> annotations, HashSet<Class<?>> seen) {
        // 如果需要收集注解的类不为空,并且没有处理过
        if (source != null && seen.add(source)) {
            // 获取类中所有的注解
            for (Annotation annotation : source.getDeclaredAnnotations()) {
                // 如果不是java.lang.annotation包中的注解
                if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotation)) {
                    // 如果存在@ImportAutoConfiguration注解
                    if (ANNOTATION_NAMES.contains(annotation.annotationType().getName())) {
                        // 保存该类与该@ImportAutoConfiguration注解的映射关系
                        annotations.add(source, annotation);
                    }
                    // 再根据当前注解类型再去找注解中的@ImportAutoConfiguration注解信息
                    this.collectAnnotations(annotation.annotationType(), annotations, seen);
                }
            }
            // 找完了当前类,继续收集父类的@ImportAutoConfiguration注解信息
            this.collectAnnotations(source.getSuperclass(), annotations, seen);
        }
    }

    // 在AutoConfigurationImportSelector之前执行
    @Override
    public int getOrder() {
        return super.getOrder() - 1;
    }

    // 处理用户排除的无效的类
    @Override
    protected void handleInvalidExcludes(List<String> invalidExcludes) {
    }
}

Spring Boot中ImportAutoConfiguration注解与Import注解的区别 Spring Boot中提供了一系列的注解比如@ImportAutoConfiguration和@EnableAutoConfiguration,它们最终都组合了@Import注解,那么,它们之间有什么区别呢? @ImportAutoConfiguration是否可以替代@Import注解?@ImportAutoConfiguration和@EnableAutoConfiguration是否效果相... 阅读详情

相关推荐

SpringBoot中 @Import 和 @ImportAutoConfiguration 的区别是什么?

1、@ImportAutoConfiguration 和 @EnableAutoConfiguration 内部都依赖了 @Import 注解,都支持把各个依赖包的 resources 目录下 MATE-INFO 目录下的 spring.factories 文件中配置的自动配置类加载到Spring容器中。1、@Import 注解只能通过 value 属性值进行导入,由于 @Import 注解的value只接受 Class 类型,意味着所导入的类必须在当前模块或所依赖的下游模块中,灵活性有限制。

hkl_Forever的博客 421

java.lang.IllegalStateException: Unable to retrieve @EnableAutoConfiguration base packages

错误:java.lang.IllegalStateException: Unable to retrieve @EnableAutoConfiguration base packages 在做单元测试的时候,出现这个错误,直接上代码和解决方案 这个是出错的代码,目标想用datajpatest测试Elasticsearch,由于datajpatest原生并不包含Elasticsearch的启动项所以需要手动添加 @DataJpaTest( includeFilters = @ComponentScan.Fil

wuqingsssss的博客 4408

VSCode下载、安装+C语言环境配置(超详细)

ps:我的更改部分:"D:/BaiduNetdiskDownload/x86_64-8.1.0-release-win32-seh-rt_v6-rev0/mingw64。③ 找到【系统变量】中的【Path】变量 ---- 双击打开后,将刚刚复制的地址添加进去 ---- 点击【确定】;① 双击【此电脑】---- 点击【属性】---- 进入到下面这个界面,单击【高级系统设置】;② 进入到 系统属性界面 ---- 点击【环境变量】,进入环境变量页面;并进行安装,安装成功后,打开VSCode;

weixin_48820458的博客 1万+

@EnableAutoConfiguration和@ImportAutoConfiguration的区别

是加载所有的自动配置类, 这其中包含了缓存, JPA等功能的自动配置类为key的自动配置类而加载自定义属性classes中的配置类集合或者某一独立功能的自动配置类集合(如因此的相比较而言, 更加灵活, 并且实现了对自动配置的范围定制。

qq_38685072的博客 1002

PyTorch实战:用FGSM攻击MNIST分类器(附可视化对比)

本文通过PyTorch实战演示了如何使用FGSM对抗攻击方法攻击MNIST手写数字分类器。文章详细解析了FGSM攻击原理,提供了从环境搭建、模型准备到攻击实施、可视化对比的完整代码实战流程,并探讨了对抗样本的生成与模型鲁棒性评估。

3a9bq4r8t2y的博客 216

Tesla V100 在 Windows 下的驱动选择与模式切换实战

本文详细解析了Tesla V100专业计算卡在Windows系统下的驱动安装与模式切换实战。针对设备管理器识别但任务管理器“隐身”的常见问题,文章指出其默认TCC模式为纯计算设计,并指导用户正确选择数据中心或GRID驱动。核心在于通过注册表修改或驱动选择,在高效的TCC模式与便于监控的WDDM模式间切换,同时提供了nvidia-smi命令行监控与AI框架测试等稳定使用方案。

weixin_29247219的博客 1802

【源码解析】自动配置的这些细节不知道,别说你会 springboot

spring-boot 相对于 spring,很重要的一个特点就是自动配置,使约定大于配置思想成功落地。xxx-spring-boot-starter 一系列引导器能够开箱即用,或者只需要很少的配置(对于初学人员)就是因为已做了默认的自动配置。 自动配置在一开始就初始化了一些配置,同时提供修改配置的入口。 整体结构 spring-boot-autoconfigure 包是 spring-boot-...

逸飞兮 1122

【异常】飞书OpenClaw机器人 HTTP 401: Invalid Authentication 报错排查与解决方案

HTTP 401 是HTTP协议标准定义的未授权(Unauthorized)状态码,核心含义为请求方身份认证无效,服务端拒绝执行本次请求。飞书开放平台服务端对自建机器人的全链路鉴权校验失败。无论是机器人接收飞书事件推送的上行请求,还是机器人主动调用飞书开放平台API的下行请求,只要身份凭证无效、鉴权逻辑校验不通过,飞书服务端就会返回该报错,并最终透传到飞书客户端会话窗口中。

奶爸的编程之路,也就一周冷个三天 3756

【源码解析】自动配置的这些细节都不知道,别说你会 springboot

spring-boot 相对于 spring,很重要的一个特点就是自动配置,使约定大于配置思想成功落地。xxx-spring-boot-starter 一系列引导器能够开箱即用,或者只需要很少的配置(对于初学人员)就是因为已做了默认的自动配置。 自动配置在一开始就初始化了一些配置,同时提供修改配置的入口。 整体结构 spring-boot-autoconfigure 包是 spring-bo...

微赚淘客开发者博客 248

机器学习实战——疫情数据分析与预测

机器学习如何做到疫情可视化——疫情数据分析与预测实战 本文将带领大家爬取11个国家以及中国31个省(自治区、直辖市)在2022.0101-2022.06.19的新冠疫情数据。并且采用机器学习模型对2022.6.20-2022.6.30每一天的全国确诊人数、死亡人数、治愈人数进行预测,**做出疫情可视化图形并且求出最终的相关系数R2!...

拥有百余位万粉技术博主、200余位可合作代发博主,私域技术社群规模达千人,商务合作私信 10万+

用「OpenClaw」打造新媒体运营神器:小红书/公众号全自动选题→创作→排版→发布全流程

对于新媒体运营的从业者来说,AI不是要取代运营人员,而是把他们从繁琐的重复劳动中解放出来,专注于更核心的内容策略、用户运营、品牌建设,这才是AI在传统行业落地的真正价值。这是最终输出可发布内容的核心智能体,我们通过「OpenClaw」的Prompt工程,结合不同平台的排版规范,实现了一键生成符合要求的排版,完全替代人工重复劳动。这套系统的落地,不是技术的炫技,而是真正用AI解决了新媒体运营的核心痛点:用自动化替代重复的人工劳动,用数据决策替代经验主义,用全闭环执行替代纸上谈兵的建议。

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 196

springboot 注解_Spring Boot – 注解

一场寂寞凭谁诉。算前言,总轻负。概述spring boot注解大部分是在org.springframework.boot.autoconfigure和org.springframework.boot.autoconfigure.condition包中。让我们来了解一些常用的spring boot注解以及它们背后的工作原理。@SpringBootApplicationSpring boot主要功能是...

weixin_39562234的博客 158

20 Embedding 与向量检索:语义为什么能变成距离

Embedding 是 RAG 语义检索的数学基础。本文用工程视角讲清文本如何变成向量,为什么语义相近会表现为距离相近,Top-K 检索如何工作,以及在模型选择、同模型约束、精确匹配和召回评估上的常见坑。

老于的笔记的专栏 6201

SpringBoot自动装配原理与实践指南

自动装配是Spring框架实现依赖注入的核心机制,通过@Conditional条件注解和约定优于配置的原则,实现了组件的智能加载。在SpringBoot中,自动装配技术通过starter模块将这一理念发挥到极致,开发者只需引入相关依赖即可获得开箱即用的功能体验。其底层基于ImportSelector机制和条件评估系统,能够根据类路径、配置属性等运行时状态动态决定Bean的注册行为。这种设计显著提升了开发效率,在微服务架构、企业级应用开发等场景中广泛应用。通过合理使用@Autowired注解和自定义start

weixin_34242509的博客 341

【Burp入门第二篇】Chrome配置BurpSuite代理教程

1、配置Burp代理为127.0.0.1:80802、扩展商店添加插件switchyomega,修改服务器和端口为下图:3、打开代理,选择刚才添加的代理

等风来 8368

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

Spring boot框架

Spring boot框架

2201_75382167的博客 209

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

@ImportAutoConfiguration 和 @EnableAutoConfiguration 注解的区别

Spring Boot中提供了一系列的注解比如@ImportAutoConfiguration和@EnableAutoConfiguration,它们最终都组合了@Import注解。 从根本上来说,@ImportAutoConfiguration是@Import的增强,限制了它使用的特定范围。 使用@EnableAutoConfiguration时会扫描整个类路径下,包括依赖引入的jar包所有的自动配置类(被注解了@Configuration的类),尝试进行自动配置。 而@ImportAutoConf

ludadan的博客 3062

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

Springboot自动转配原理之@Import注解处理过程

springboot

qq_17805707的博客 775

SpringBoot 自动配置探究

这里写目录标题pom.xml启动器主程序@ConditionalOnXXXX 注解yml 文件的配置是如何生效的?总结 pom.xml 当我们初始化一个 SpringBoot 工程,查看 pom.xml配置,发现有如下 依赖 <parent> <groupId>org.springframework.boot</groupId> <arti...

lhg_55的博客 714

SpringBoot-自动配置-@Import注解与@EnableAutoConfiguration注解

SpringBoot-自动配置-@Import注解与@EnableAutoConfiguration注解

汤键的博客 2590

springboot整合mybatis源码分析

springboot整合mybatis源码分析 本文主要讲述mybatis在springboot中是如何被加载执行的,由于涉及的内容会比较多,所以这次只会对调用关系及关键代码点进行讲解,为了避免文章太长,读起来昏昏欲睡,一些不影响整体流程的细节就不涉及了。 1、预备知识 FactoryBean 什么是FactoryBean? 我们先看看FactoryBean的源码 //由 BeanFactory 中使用的对象实现的接口,这些对象本身是单个对象的工厂。如果一个 bean 实现了这个接口,它就被用作一个

wbo 735

【springboot】@Import的用法,与@ImportAutoConfiguration的区别

Import的value值,可以是配置类。

qq_36382225的博客 857

SpringBootApplication-@Import(AutoConfigurationImportSelector.class)

@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage //标记自动配置 @Import(AutoConfigurationImportSelector.class) //使用Import自动导入所有符合自动配置条件的Bean定义并加载到IOC容器 public @interface EnableAutoConfiguration { ..

小城我家 2215

@Import和@ImportAutoConfiguration

`@Import(Config.class)` 和 `@ImportAutoConfiguration(Config.class)` 都是用来将指定的配置类加载到Spring的上下文中,虽然在功能上有一定的重叠——都可以用于导入配置类——但它们在使用场景和具体行为上有差异,因此在某些情况下,`@Import`不能直接替代`@ImportAutoConfiguration`。

Del_Esc的博客 634

SpringBoot中,@EnableAutoConfiguration包含的@AutoConfigurationPackage有什么用,具体做了什么

【代码】SpringBoot中,@EnableAutoConfiguration包含的@AutoConfigurationPackage有什么用,具体做了什么。

JavaMyDream的博客 845

springboot源码3---自动装配

springboot自动装配原理、AutoConfigurationImportSelector

piliang719719的博客 898
上一篇: 使用外置Tomcat部署SpringBoot项目原理
下一篇: SpringBoot中,@EnableAutoConfiguration包含的@AutoConfigurationPackage有什么用,具体做了什么
安缇吖
博客等级 码龄9年 411粉丝 135原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值