一文学会Springboot的自动装配机制

2,723 阅读12分钟

为了给组里的实习生妹妹讲如何自定义SpringbootStarter

我觉得需要让她先知道Springboot的自动装配机制,

但又感觉是不是得先让她了解SpringSPI机制,

最后又觉得还有必要说明一下一个@Configuration配置类在Spring中如何被解析,这又会引出对ConfigurationClassPostProcessor的讲解,

完了,收不住了,到处都是知识盲区。

知识盲区脑图如下。

Springboot-自定义Starter包知识体系脑图

往期文章

一文学会Spring的@Configuration配置类解析


前言

何谓Springboot的自动装配,简要概括就是:引入第三方组件的starter包后能够自动将第三方组件的bean加载到IOC容器中供用户使用。

自动装配的机制是Springboot提供的,因此第三方组件的starter包在编写的时候,就需要根据Springboot的自动装配的规则来编写starter包,那么这里的规则概括如下。

  1. starter包需要在/META-INF/目录下提供spring.factories文件;
  2. spring.factories文件中以Key-Values的形式来提供需要Springboot去加载的类的全限定名。这里的Key就是Springboot中各种扩展点的全限定名,比如org.springframework.boot.autoconfigure.EnableAutoConfiguration,然后Values就是starter包中提供的扩展点的所有类的全限定名,以逗号隔开。

只要第三方组件的starter包按照上述规则来编写,那么Springboot就能够将starter包提供的各种扩展点的类进行加载,例如下面的这个spring.factories文件。

org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\

啥意思呢,其实就是第三方组件的starter包在告诉Springboot:嘿,亲爱的Springboot,我提供了一个监听器,它的全限定名是org.springframework.boot.autoconfigure.BackgroundPreinitializer,拜托你去加载它;嘿,亲爱的Springboot,我提供了多个自动配置类,它们的全限定名是org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfigurationorg.springframework.boot.autoconfigure.aop.AopAutoConfigurationorg.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,拜托你去加载它们。

Springboot的自动装配机制解释起来很简单,使用起来也巨方便,关于自动装配的具体使用,将在后续的自定义starter包一文中进行演示。

本文的重点是Springboot的自动装配机制的底层原理,废话不多说,开造。

Springboot版本:2.4.1

正文

一. 认识@SpringBootApplication注解

我们新建一个Springboot应用,都需要一个启动类,然后在启动类上添加@SpringBootApplication注解,那么来看一下@SpringBootApplication注解的组成。

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
public @interface SpringBootApplication {}

也就是说@SpringBootApplication是由@SpringBootConfiguration,@EnableAutoConfiguration和@ComponentScan组成的复合注解,下面是对这三个注解的说明。

  • @SpringBootConfiguration表明Springboot启动类是一个配置类;
  • @ComponentScan注解会将指定路径下的被特定注解修饰的类加载为Spring中的bean,这些特定注解为@Component,@Controller,@Service,@Repository和@Configuration注解;
  • @EnableAutoConfiguration注解用于开启Springboot的自动装配。

也就是说,相当于我们在Springboot应用的启动类上添加了一个叫做@EnableAutoConfiguration的注解,从而开启了自动装配功能。

那么再看一下@EnableAutoConfiguration注解的组成。

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

@EnableAutoConfiguration注解也是一个复合注解,主要功能由@AutoConfigurationPackage注解和@Import注解实现,那么肯定的,自动装配,就是这两个注解实现的。

二. @AutoConfigurationPackage注解

先给出结论。

@AutoConfigurationPackage注解作用在Springboot启动类上,会向Spring容器注册一个类型为AutoConfigurationPackages.BasePackagesbean,这个bean中保存了Springboot启动类的包路径,后续Springboot就会扫描这个包路径下由@Component,@Controller,@Service,@Repository和@Configuration注解修饰的类。

下面开始分析原理。

@AutoConfigurationPackage注解的功能由@Import(AutoConfigurationPackages.Registrar.class) 实现,所以看一下AutoConfigurationPackages.Registrar的类图,如下所示。

AutoConfigurationPackages.Registrar类图

所以AutoConfigurationPackages.Registrar实际是一个ImportBeanDefinitionRegistrar,那么在Spring容器启动过程中,会调用到AutoConfigurationPackages.RegistrarregisterBeanDefinitions() 方法向注册表注册一个BeanDefinitionregisterBeanDefinitions() 方法如下所示。

public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
    register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
}

registerBeanDefinitions() 方法中调用了AutoConfigurationPackagesregister() 方法,register() 方法的第二个参数是通过new PackageImports(metadata).getPackageNames() 获取,其实就是将被@AutoConfigurationPackage注解修饰的类的包路径返回。

现在看一下register() 方法的实现,如下所示。

public static void register(BeanDefinitionRegistry registry, String... packageNames) {
    if (registry.containsBeanDefinition(BEAN)) {
        BasePackagesBeanDefinition beanDefinition = (BasePackagesBeanDefinition) registry.getBeanDefinition(BEAN);
        beanDefinition.addBasePackages(packageNames);
    }
    else {
        registry.registerBeanDefinition(BEAN, new BasePackagesBeanDefinition(packageNames));
    }
}

register() 方法中会将包路径packageNames封装成一个BasePackagesBeanDefinition,然后注册到注册表中。下面看一下BasePackagesBeanDefinition的构造方法,如下所示。

BasePackagesBeanDefinition(String... basePackages) {
    setBeanClass(BasePackages.class);
    setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    addBasePackages(basePackages);
}

BasePackagesBeanDefinition的构造方法中,将beanClass设置为了BasePackages.class,那么后续会基于BasePackagesBeanDefinition向容器注册一个类型为BasePackagesbean,并且BasePackagesbean中的packages字段是一个包路径的集合。

最后说明一下,BasePackagesBeanDefinitionBasePackages都是AutoConfigurationPackages的静态内部类,类图如下。

AutoConfigurationPackages类图

三. AutoConfigurationImportSelector

@EnableAutoConfiguration注解实现自动装配主要就是依靠@Import(AutoConfigurationImportSelector.class),下面先看一下AutoConfigurationImportSelector的类图。

AutoConfigurationImportSelector类图

所以AutoConfigurationImportSelector是一个DeferredImportSelector,那么在ConfigurationClassParserparse(Set<BeanDefinitionHolder> configCandidates) 方法中会开启处理AutoConfigurationImportSelector的逻辑。

parse(Set<BeanDefinitionHolder> configCandidates) 如下所示。

public void parse(Set<BeanDefinitionHolder> configCandidates) {
    for (BeanDefinitionHolder holder : configCandidates) {
        BeanDefinition bd = holder.getBeanDefinition();
        try {
            if (bd instanceof AnnotatedBeanDefinition) {
                // 初始配置类对应的BeanDefinition为AnnotatedGenericBeanDefinition
                // AnnotatedGenericBeanDefinition实现了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);
        }
    }
    
    // 延迟处理DeferredImportSelector
    this.deferredImportSelectorHandler.process();
}

deferredImportSelectorHandler字段类型为DeferredImportSelectorHandler,是ConfigurationClassParser的一个内部类,其process() 方法如下所示。

public void process() {
    // DeferredImportSelectorHolder对ConfigurationClass和DeferredImportSelector进行了简单封装
    List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
    this.deferredImportSelectors = null;
    try {
        if (deferredImports != null) {
            // DeferredImportSelectorGroupingHandler是ConfigurationClassParser的内部类
            // 用于分组处理DeferredImportSelector,这里的组指DeferredImportSelector的内部接口Group
            // Group接口的作用是用于对不同ImportSelector的导入结果进行分组
            DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
            // 对deferredImports进行排序
            deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
            // 分组地将DeferredImportSelector注册到DeferredImportSelectorGroupingHandler中
            deferredImports.forEach(handler::register);
            // 调用DeferredImportSelectorGroupingHandler的processGroupImports()方法分组处理DeferredImportSelector
            handler.processGroupImports();
        }
    }
    finally {
        this.deferredImportSelectors = new ArrayList<>();
    }
}

DeferredImportSelectorHolder是对ConfigurationClassDeferredImportSelector进行的简单封装,在DeferredImportSelectorHandler#process方法中,先获取到所有的DeferredImportSelector,然后创建DeferredImportSelectorGroupingHandler用于处理DeferredImportSelectorDeferredImportSelectorGroupingHandler会先注册DeferredImportSelectorDeferredImportSelectorGroupingHandlergroupings字段中,然后调用DeferredImportSelectorGroupingHandler#processGroupImports方法处理groupings字段中的DeferredImportSelector,而且整个处理是按组进行处理的,这里的组其实就是DeferredImportSelector的内部接口GroupDeferredImportSelector接口的实现类实现的getImportGroup() 方法就需要返回一个Group接口的实现类的Class对象,通常如下所示。

public class MyDeferredImportSelector implements DeferredImportSelector {

    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[0];
    }

    public Predicate<String> getExclusionFilter() {
        return null;
    }

    public Class<? extends Group> getImportGroup() {
        // 返回MyGroup的Class对象
        return MyGroup.class;
    }

    // 定义一个内部类并且实现DeferredImportSelector的内部接口Group
    private static class MyGroup implements DeferredImportSelector.Group {
        @Override
        public void process(AnnotationMetadata metadata, DeferredImportSelector selector) {

        }

        @Override
        public Iterable<Entry> selectImports() {
            return new ArrayList<>();
        }
    }

}

那么现在看一下DeferredImportSelectorGroupingHandler是如何注册DeferredImportSelectorDeferredImportSelectorGroupingHandlergroupings字段中的。

DeferredImportSelectorGroupingHandler#register方法如下所示。

public void register(DeferredImportSelectorHolder deferredImport) {
    // 获取Group接口的实现类的Class对象
    Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup();
    // 将Group接口的实现类进行实例化,然后封装成DeferredImportSelectorGrouping对象
    // 以Group接口的实现类的Class对象为键,DeferredImportSelectorGrouping对象为值,注册到groupings字段中
    DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent(
            (group != null ? group : deferredImport),
            key -> new DeferredImportSelectorGrouping(createGroup(group)));
    // 为当前Group接口的实现类对应的DeferredImportSelectorGrouping添加DeferredImportSelector
    grouping.add(deferredImport);
    this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(),
            deferredImport.getConfigurationClass());
}

DeferredImportSelectorGroupingHandlergroupings字段是一个map结构,键是Group接口的实现类的Class对象,值是DeferredImportSelectorGrouping对象,而DeferredImportSelectorGrouping对象是对Group接口的实现类的实例和DeferredImportSelector的封装,那么上面的DeferredImportSelectorGroupingHandler#register方法其实就是将相同组的DeferredImportSelector全部放到同一个DeferredImportSelectorGrouping对象中,然后再将DeferredImportSelectorGrouping对象注册到DeferredImportSelectorGroupingHandlergroupings字段中。

那么怎么才算是相同组的DeferredImportSelector,其实就是DeferredImportSelectorgetImportGroup() 方法返回相同的Class对象。

现在再看一下DeferredImportSelectorGroupingHandler是如何处理DeferredImportSelector的,DeferredImportSelectorGroupingHandler#processGroupImports方法如下所示。

public void processGroupImports() {
    // 遍历每一个DeferredImportSelectorGrouping,即按组来处理DeferredImportSelector
    for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
        Predicate<String> exclusionFilter = grouping.getCandidateFilter();
        // 通过grouping.getImports()获取到当前组里所有DeferredImportSelector导入的类
        // 然后将每个导入的类封装成一个ConfigurationClass然后进行解析
        grouping.getImports().forEach(entry -> {
            ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
            try {
                // 调用到ConfigurationClassParser的processImports()方法进入递归解析流程
                processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter),
                        Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)),
                        exclusionFilter, false);
            }
            catch (BeanDefinitionStoreException ex) {
                throw ex;
            }
            catch (Throwable ex) {
                throw new BeanDefinitionStoreException(
                        "Failed to process import candidates for configuration class [" +
                                configurationClass.getMetadata().getClassName() + "]", ex);
            }
        });
    }
}

DeferredImportSelectorGroupingHandler#processGroupImports方法中是分组进行处理,每次将一组中的所有DeferredImportSelector导入的类获取出来,然后将每个导入的类封装成ConfigurationClass,后续就是处理ConfigurationClass的逻辑。

现在看一下DeferredImportSelectorGroupinggetImports() 方法的具体实现,如下所示。

public Iterable<Group.Entry> getImports() {
    // 先遍历当前组的每个DeferredImportSelector,并通过Group接口的实现类的process()方法进行处理
    for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
        this.group.process(deferredImport.getConfigurationClass().getMetadata(),
                deferredImport.getImportSelector());
    }
    // 然后调用Group接口的实现类的selectImports()方法返回所有需要导入的类
    // 这些需要导入的类会被封装成Group.Entry对象
    return this.group.selectImports();
}

DeferredImportSelectorGroupinggetImports() 方法会先调用Group接口的实现类的process() 方法处理组里的每个DeferredImportSelector,然后再调用Group接口的实现类的selectImports() 方法获取所有需要导入的类,每个需要被导入的类会被封装成Group.Entry对象并返回。先分析AutoConfigurationImportSelector中的组对象AutoConfigurationGroupprocess() 方法,实现如下。

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()));
    // 调用AutoConfigurationImportSelector的getAutoConfigurationEntry()方法获取所有需要自动装配的组件的配置类的全限定名
    AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
            .getAutoConfigurationEntry(annotationMetadata);
    // 添加到组对象AutoConfigurationGroup的autoConfigurationEntries集合中
    this.autoConfigurationEntries.add(autoConfigurationEntry);
    for (String importClassName : autoConfigurationEntry.getConfigurations()) {
        this.entries.putIfAbsent(importClassName, annotationMetadata);
    }
}

继续分析AutoConfigurationImportSelectorgetAutoConfigurationEntry() 方法,看一下所有需要自动装配的组件的配置类的全限定名是如何获取的。

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    }
    // 获取@EnableAutoConfiguration注解的元数据属性
    AnnotationAttributes attributes = getAttributes(annotationMetadata);
    // 将需要自动装配的组件的配置类的全限定名获取出来
    List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    // 去除重复的组件
    configurations = removeDuplicates(configurations);
    // 去除被排除的组件
    Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    checkExcludedClasses(configurations, exclusions);
    configurations.removeAll(exclusions);
    // 去除依赖项不满足的组件
    configurations = getConfigurationClassFilter().filter(configurations);
    fireAutoConfigurationImportEvents(configurations, exclusions);
    // 返回剩余的需要自动装配的组件的配置类的全限定名
    return new AutoConfigurationEntry(configurations, exclusions);
}

AutoConfigurationImportSelectorgetAutoConfigurationEntry() 方法是先将所有需要自动装配的组件的配置类的全限定名获取出来,然后进行去重和条件过滤。AutoConfigurationImportSelectorgetAutoConfigurationEntry() 方法中,最重要的就是getCandidateConfigurations() 方法,其实现如下。

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    // getSpringFactoriesLoaderFactoryClass()的返回值是EnableAutoConfiguration.class
    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;
}

SpringFactoriesLoaderSpringSPI机制的实现类,Springboot能够完成组件自动装配就是依赖的SpringFactoriesLoader,下面分析一下SpringFactoriesLoader#loadFactoryNames方法,如下所示。

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
    ClassLoader classLoaderToUse = classLoader;
    if (classLoaderToUse == null) {
        classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
    }
    // 这里factoryTypeName为org.springframework.boot.autoconfigure.EnableAutoConfiguration
    String factoryTypeName = factoryType.getName();
    // loadSpringFactories()方法会先根据ClassLoader从缓存中获取需要自动装配的组件信息
    // 获取不到则使用ClassLoader将classpath下所有jar包的所有META-INF/spring.factories文件中的信息加载
    // loadSpringFactories()方法获取出来的数据是一个Map
    // 形式为Map[factoryTypeName, List[自动装配的组件的配置类的全限定名]]
    return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}

SpringFactoriesLoader#loadFactoryNames方法中调用了loadSpringFactories() 方法基于ClassLoader来将需要自动装配的组件的配置类的全限定名获取出来,所有classpath下的jar包中的META-INF/spring.factories文件都会被扫描。loadSpringFactories() 方法实现如下。

private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
    // 先根据ClassLoader从缓存中获取
    // Map[factoryTypeName, List[自动装配的组件的配置类的全限定名]]
    Map<String, List<String>> result = cache.get(classLoader);
    if (result != null) {
        return result;
    }

    result = new HashMap<>();
    try {
        // 将所有META-INF/spring.factories文件路径获取出来
        Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
        while (urls.hasMoreElements()) {
            URL url = urls.nextElement();
            UrlResource resource = new UrlResource(url);
            // 将spring.factories文件的内容读取成Properties,Properties本质是一个HashTable
            Properties properties = PropertiesLoaderUtils.loadProperties(resource);
            // 将Properties的内容添加到result
            for (Map.Entry<?, ?> entry : properties.entrySet()) {
                String factoryTypeName = ((String) entry.getKey()).trim();
                String[] factoryImplementationNames =
                        StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
                for (String factoryImplementationName : factoryImplementationNames) {
                    result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
                            .add(factoryImplementationName.trim());
                }
            }
        }

        result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
                .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
        // 缓存到cache中
        cache.put(classLoader, result);
    }
    catch (IOException ex) {
        throw new IllegalArgumentException("Unable to load factories from location [" +
                FACTORIES_RESOURCE_LOCATION + "]", ex);
    }
    return result;
}

下面再给出spring-boot-autoconfigure包下的spring.factories文件的部分内容,就能够知道上面的loadSpringFactories() 方法的返回值具体是什么结构。

# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer

# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener

# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\

......

因此loadSpringFactories() 方法的返回值map的键可以是上述的org.springframework.boot.autoconfigure.AutoConfigurationImportFilter,或者org.springframework.boot.autoconfigure.EnableAutoConfiguration等,值就是一个全限定名的集合。所以在SpringFactoriesLoader#loadFactoryNames方法中能够通过loadSpringFactories() 方法的返回值拿到org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的需要自动装配的配置类的全限定名的集合。

现在继续分析AutoConfigurationImportSelector中的组对象AutoConfigurationGroupselectImports() 方法,如下所示。

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());
    // 将组中每个DeferredImportSelector导入的类的全限定名获取出来
    // 这里导入的类就是需要自动装配的组件的配置类
    Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
            .map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream)
            .collect(Collectors.toCollection(LinkedHashSet::new));
    processedConfigurations.removeAll(allExclusions);

    return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
            .map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
            .collect(Collectors.toList());
}

因为组里会有多个DeferredImportSelector,而每个DeferredImportSelector会在AutoConfigurationGroupprocess() 方法中生成一个AutoConfigurationEntry,所以上面的方法就是将每个DeferredImportSelector对应的AutoConfigurationEntry中的全限定名的集合合并到一个集合中并返回。

至此,AutoConfigurationImportSelector整体的一个处理流程分析完毕。

整个分析流程很长,但是如果耐着性子看完,会发现整个流程可以用下图进行概括。

Springboot-自动装配流程图

总结

Springboot的自动装配功能由@EnableAutoConfiguration注解提供。

而@EnableAutoConfiguration注解的功能主要由以下两部分实现。

一. @AutoConfigurationPackage

@AutoConfigurationPackage注解作用在Springboot启动类上,会向Spring容器注册一个类型为AutoConfigurationPackages.BasePackagesbean,这个bean中保存了Springboot启动类的包路径,后续Springboot就会扫描这个包路径下由@Component,@Controller,@Service,@Repository和@Configuration注解修饰的类。

二. @Import(AutoConfigurationImportSelector.class)

  1. @Import(AutoConfigurationImportSelector.class) 会通过AutoConfigurationImportSelector延迟且分组的向Spring容器导入需要自动装配的组件的配置类,从而在解析这些配置类的时候能够将自动装配的组件的bean注册到容器中;
  2. 所谓的延迟,是因为AutoConfigurationImportSelector实现了DeferredImportSelector接口,其逻辑会在Springboot启动类被解析完毕后才会执行;
  3. 所谓的分组,是因为处理DeferredImportSelector是一组一组的进行的,只要DeferredImportSelector的实现类实现的getImportGroup() 方法返回的Class对象一样,那么这样的DeferredImportSelector的实现类就属于同一组;
  4. AutoConfigurationImportSelector获取到需要自动装配的组件的配置类的全限定名,是通过SpringFactoriesLoader完成的,而SpringFactoriesLoader就是Spring中的SPI机制的实现。

如果觉得本篇文章对你有帮助,求求你点个赞,加个收藏最后再点个关注吧。创作不易,感谢支持!


本文正在参加「金石计划」