一文搞定Springboot的自动装配机制

清疚 2024-03-23 15:46 154阅读 0赞

format_png

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

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

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

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

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

知识盲区脑图如下。

format_png 1


前言

何谓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文件。

  1. org.springframework.context.ApplicationListener=\
  2. org.springframework.boot.autoconfigure.BackgroundPreinitializer
  3. org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  4. org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
  5. org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
  6. org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
  7. 复制代码

啥意思呢,其实就是第三方组件的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注解的组成。

  1. @SpringBootConfiguration
  2. @EnableAutoConfiguration
  3. @ComponentScan
  4. public @interface SpringBootApplication {}
  5. 复制代码

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

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

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

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

  1. @AutoConfigurationPackage
  2. @Import(AutoConfigurationImportSelector.class)
  3. public @interface EnableAutoConfiguration {}
  4. 复制代码

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

二. @AutoConfigurationPackage注解

先给出结论。

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

下面开始分析原理。

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

format_png 2

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

  1. public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
  2. register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
  3. }
  4. 复制代码

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

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

  1. public static void register(BeanDefinitionRegistry registry, String... packageNames) {
  2. if (registry.containsBeanDefinition(BEAN)) {
  3. BasePackagesBeanDefinition beanDefinition = (BasePackagesBeanDefinition) registry.getBeanDefinition(BEAN);
  4. beanDefinition.addBasePackages(packageNames);
  5. }
  6. else {
  7. registry.registerBeanDefinition(BEAN, new BasePackagesBeanDefinition(packageNames));
  8. }
  9. }
  10. 复制代码

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

  1. BasePackagesBeanDefinition(String... basePackages) {
  2. setBeanClass(BasePackages.class);
  3. setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
  4. addBasePackages(basePackages);
  5. }
  6. 复制代码

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

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

format_png 3

三. AutoConfigurationImportSelector

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

format_png 4

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

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

  1. public void parse(Set<BeanDefinitionHolder> configCandidates) {
  2. for (BeanDefinitionHolder holder : configCandidates) {
  3. BeanDefinition bd = holder.getBeanDefinition();
  4. try {
  5. if (bd instanceof AnnotatedBeanDefinition) {
  6. // 初始配置类对应的BeanDefinition为AnnotatedGenericBeanDefinition
  7. // AnnotatedGenericBeanDefinition实现了AnnotatedBeanDefinition接口
  8. parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
  9. }
  10. else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
  11. parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
  12. }
  13. else {
  14. parse(bd.getBeanClassName(), holder.getBeanName());
  15. }
  16. }
  17. catch (BeanDefinitionStoreException ex) {
  18. throw ex;
  19. }
  20. catch (Throwable ex) {
  21. throw new BeanDefinitionStoreException(
  22. "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
  23. }
  24. }
  25. // 延迟处理DeferredImportSelector
  26. this.deferredImportSelectorHandler.process();
  27. }
  28. 复制代码

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

  1. public void process() {
  2. // DeferredImportSelectorHolder对ConfigurationClass和DeferredImportSelector进行了简单封装
  3. List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
  4. this.deferredImportSelectors = null;
  5. try {
  6. if (deferredImports != null) {
  7. // DeferredImportSelectorGroupingHandler是ConfigurationClassParser的内部类
  8. // 用于分组处理DeferredImportSelector,这里的组指DeferredImportSelector的内部接口Group
  9. // Group接口的作用是用于对不同ImportSelector的导入结果进行分组
  10. DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
  11. // 对deferredImports进行排序
  12. deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
  13. // 分组地将DeferredImportSelector注册到DeferredImportSelectorGroupingHandler中
  14. deferredImports.forEach(handler::register);
  15. // 调用DeferredImportSelectorGroupingHandler的processGroupImports()方法分组处理DeferredImportSelector
  16. handler.processGroupImports();
  17. }
  18. }
  19. finally {
  20. this.deferredImportSelectors = new ArrayList<>();
  21. }
  22. }
  23. 复制代码

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

  1. public class MyDeferredImportSelector implements DeferredImportSelector {
  2. public String[] selectImports(AnnotationMetadata importingClassMetadata) {
  3. return new String[0];
  4. }
  5. public Predicate<String> getExclusionFilter() {
  6. return null;
  7. }
  8. public Class<? extends Group> getImportGroup() {
  9. // 返回MyGroup的Class对象
  10. return MyGroup.class;
  11. }
  12. // 定义一个内部类并且实现DeferredImportSelector的内部接口Group
  13. private static class MyGroup implements DeferredImportSelector.Group {
  14. @Override
  15. public void process(AnnotationMetadata metadata, DeferredImportSelector selector) {
  16. }
  17. @Override
  18. public Iterable<Entry> selectImports() {
  19. return new ArrayList<>();
  20. }
  21. }
  22. }
  23. 复制代码

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

DeferredImportSelectorGroupingHandler#register方法如下所示。

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

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

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

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

  1. public void processGroupImports() {
  2. // 遍历每一个DeferredImportSelectorGrouping,即按组来处理DeferredImportSelector
  3. for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
  4. Predicate<String> exclusionFilter = grouping.getCandidateFilter();
  5. // 通过grouping.getImports()获取到当前组里所有DeferredImportSelector导入的类
  6. // 然后将每个导入的类封装成一个ConfigurationClass然后进行解析
  7. grouping.getImports().forEach(entry -> {
  8. ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
  9. try {
  10. // 调用到ConfigurationClassParser的processImports()方法进入递归解析流程
  11. processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter),
  12. Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)),
  13. exclusionFilter, false);
  14. }
  15. catch (BeanDefinitionStoreException ex) {
  16. throw ex;
  17. }
  18. catch (Throwable ex) {
  19. throw new BeanDefinitionStoreException(
  20. "Failed to process import candidates for configuration class [" +
  21. configurationClass.getMetadata().getClassName() + "]", ex);
  22. }
  23. });
  24. }
  25. }
  26. 复制代码

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

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

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

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

  1. public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
  2. Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
  3. () -> String.format("Only %s implementations are supported, got %s",
  4. AutoConfigurationImportSelector.class.getSimpleName(),
  5. deferredImportSelector.getClass().getName()));
  6. // 调用AutoConfigurationImportSelector的getAutoConfigurationEntry()方法获取所有需要自动装配的组件的配置类的全限定名
  7. AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
  8. .getAutoConfigurationEntry(annotationMetadata);
  9. // 添加到组对象AutoConfigurationGroup的autoConfigurationEntries集合中
  10. this.autoConfigurationEntries.add(autoConfigurationEntry);
  11. for (String importClassName : autoConfigurationEntry.getConfigurations()) {
  12. this.entries.putIfAbsent(importClassName, annotationMetadata);
  13. }
  14. }
  15. 复制代码

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

  1. protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
  2. if (!isEnabled(annotationMetadata)) {
  3. return EMPTY_ENTRY;
  4. }
  5. // 获取@EnableAutoConfiguration注解的元数据属性
  6. AnnotationAttributes attributes = getAttributes(annotationMetadata);
  7. // 将需要自动装配的组件的配置类的全限定名获取出来
  8. List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
  9. // 去除重复的组件
  10. configurations = removeDuplicates(configurations);
  11. // 去除被排除的组件
  12. Set<String> exclusions = getExclusions(annotationMetadata, attributes);
  13. checkExcludedClasses(configurations, exclusions);
  14. configurations.removeAll(exclusions);
  15. // 去除依赖项不满足的组件
  16. configurations = getConfigurationClassFilter().filter(configurations);
  17. fireAutoConfigurationImportEvents(configurations, exclusions);
  18. // 返回剩余的需要自动装配的组件的配置类的全限定名
  19. return new AutoConfigurationEntry(configurations, exclusions);
  20. }
  21. 复制代码

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

  1. protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
  2. // getSpringFactoriesLoaderFactoryClass()的返回值是EnableAutoConfiguration.class
  3. List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
  4. getBeanClassLoader());
  5. Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
  6. + "are using a custom packaging, make sure that file is correct.");
  7. return configurations;
  8. }
  9. 复制代码

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

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

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

  1. private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
  2. // 先根据ClassLoader从缓存中获取
  3. // Map[factoryTypeName, List[自动装配的组件的配置类的全限定名]]
  4. Map<String, List<String>> result = cache.get(classLoader);
  5. if (result != null) {
  6. return result;
  7. }
  8. result = new HashMap<>();
  9. try {
  10. // 将所有META-INF/spring.factories文件路径获取出来
  11. Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
  12. while (urls.hasMoreElements()) {
  13. URL url = urls.nextElement();
  14. UrlResource resource = new UrlResource(url);
  15. // 将spring.factories文件的内容读取成Properties,Properties本质是一个HashTable
  16. Properties properties = PropertiesLoaderUtils.loadProperties(resource);
  17. // 将Properties的内容添加到result
  18. for (Map.Entry<?, ?> entry : properties.entrySet()) {
  19. String factoryTypeName = ((String) entry.getKey()).trim();
  20. String[] factoryImplementationNames =
  21. StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
  22. for (String factoryImplementationName : factoryImplementationNames) {
  23. result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
  24. .add(factoryImplementationName.trim());
  25. }
  26. }
  27. }
  28. result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
  29. .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
  30. // 缓存到cache中
  31. cache.put(classLoader, result);
  32. }
  33. catch (IOException ex) {
  34. throw new IllegalArgumentException("Unable to load factories from location [" +
  35. FACTORIES_RESOURCE_LOCATION + "]", ex);
  36. }
  37. return result;
  38. }
  39. 复制代码

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

  1. # Initializers
  2. org.springframework.context.ApplicationContextInitializer=\
  3. org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
  4. org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
  5. # Application Listeners
  6. org.springframework.context.ApplicationListener=\
  7. org.springframework.boot.autoconfigure.BackgroundPreinitializer
  8. # Auto Configuration Import Listeners
  9. org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
  10. org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener
  11. # Auto Configuration Import Filters
  12. org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
  13. org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
  14. org.springframework.boot.autoconfigure.condition.OnClassCondition,\
  15. org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition
  16. # Auto Configure
  17. org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  18. org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
  19. org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
  20. org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
  21. org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
  22. org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
  23. org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
  24. ......
  25. 复制代码

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

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

  1. public Iterable<Entry> selectImports() {
  2. if (this.autoConfigurationEntries.isEmpty()) {
  3. return Collections.emptyList();
  4. }
  5. Set<String> allExclusions = this.autoConfigurationEntries.stream()
  6. .map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet());
  7. // 将组中每个DeferredImportSelector导入的类的全限定名获取出来
  8. // 这里导入的类就是需要自动装配的组件的配置类
  9. Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
  10. .map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream)
  11. .collect(Collectors.toCollection(LinkedHashSet::new));
  12. processedConfigurations.removeAll(allExclusions);
  13. return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
  14. .map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
  15. .collect(Collectors.toList());
  16. }
  17. 复制代码

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

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

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

format_png 5

总结

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机制的实现。

发表评论

表情:
评论列表 (有 0 条评论,154人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Scala

    第一节:概述 为什么学习Scala ? Apache Spark 是专为大规模数据快速实时处理的计算引擎/内存级大数据计算框架。Apache Spark 是由Sca