源码级讲解SpringBoot自动装配原理

904 阅读4分钟

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

一.什么是SpringBoot的自动装配:

SpringBoot 定义了一套接口规范,这套规范规定:SpringBoot 在启动时会扫描外部引用 jar 包中的META-INF/spring.factories文件,将文件中配置的类型信息加载到 Spring 容器,并执行类中定义的各种操作。对于外部 jar 来说,只需要按照 SpringBoot 定义的标准,就能将自己的功能装置进 SpringBoot。

没有 SpringBoot 的情况下,如果我们需要引入第三方依赖,需要手动配置,非常麻烦。但是,SpringBoot 中,我们直接引入一个 starter 即可。比如你想要在项目中使用 redis 的话,直接在项目中引入对应的 starter 即可。

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

引入 starter 之后,我们通过少量注解和一些简单的配置就能使用第三方组件提供的功能了。

总的来说:使用Spring Boot时,我们只需引入对应的Starters,Spring Boot启动时便会自动加载相关依赖,配置相应的初始化参数,以最快捷、简单的形式对第三方软件进行集成,这便是Spring Boot的自动配置功能。

二.自动装配的实现原理

自动装配的核心类是ConfigurationClassPostProcessor,此类是BeanFactoryPostProcessor的实现类,在Spring的refresh方法中会执行其postProcessBeanDefinitionRegistry()方法。我们重点分析一下processConfigBeanDefinitions方法的处理流程。

2.1.parse方法

parse方法中调用了processConfigurationClass()方法,然后调用到 doProcessConfigurationClass()方法。

doProcessConfigurationClass()方法里面逻辑复杂,我们在看的时候先梳理清楚它主要做了什么,把功能拆解成多个子功能,然后再对单个子功能进行分析。

方法拆解后,会依次调用下面的方法:

那这些方法到底是做了些什么呢,下面我们就看下流程图:

2.processDeferredImportSelectors方法:

processDeferredImportSelectors代码截图

我们打断点,跟踪代码的执行情况:

deferredImportSelectors中只包含一个importSelector,就是AutoConfigurationImportSelector。

2.1AutoConfigurationImportSelector的由来

在@SpringBootApplication注解中包含了@EnableAutoConfiguration注解,在 @EnableAutoConfiguration注解中又包含了@Import注解。关系如下:

@Import注解的value值为AutoConfigurationImportSelector.class。

2.2自动装置类的读取

第560行grouping.getImports(),会调用AutoConfigurationGroup类的process方法,
AutoConfigurationImportSelector的selectImports方法会执行。selectImports方法如下:

@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
   if (!isEnabled(annotationMetadata)) {
      return NO_IMPORTS;
   }
   AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
         .loadMetadata(this.beanClassLoader);
   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 = filter(configurations, autoConfigurationMetadata);
   fireAutoConfigurationImportEvents(configurations, exclusions);
   return StringUtils.toStringArray(configurations);
}

getCandidateConfigurations方法会从META-INF/spring.factories文件中读取配置的类,只返回org.springframework.boot.autoconfigure.EnableAutoConfiguration为key的集合。

然后对jar包中配置的EnableAutoConfiguration类循环处理,具体调用的就是564行的processImports方法。最终其实又会调用到上面讲到的processConfigurationClass()方法。

三.processImports和processDeferredImportSelectors有什么区别

细心看的朋友们会发现parse方法中有一个processImports()方法用来处理@import注解, processDeferredImportSelectors()也是处理的@import注解里的类。 processImports()方法和processDeferredImportSelectors()方法两者是什么关系呢? 我们看下processImports的代码:

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
      Collection<SourceClass> importCandidates, boolean checkForCircularImports) {

   if (importCandidates.isEmpty()) {
      return;
   }

   if (checkForCircularImports && isChainedImportOnStack(configClass)) {
      this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
   }
   else {
      this.importStack.push(configClass);
      try {
         for (SourceClass candidate : importCandidates) {
            if (candidate.isAssignable(ImportSelector.class)) {
               // Candidate class is an ImportSelector -> delegate to it to determine imports
               Class<?> candidateClass = candidate.loadClass();
               ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
               ParserStrategyUtils.invokeAwareMethods(
                     selector, this.environment, this.resourceLoader, this.registry);
               if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
                  this.deferredImportSelectors.add(
                        new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
               }
               else {
                  String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                  Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
                  processImports(configClass, currentSourceClass, importSourceClasses, false);
               }
            }
            else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
               // Candidate class is an ImportBeanDefinitionRegistrar ->
               // delegate to it to register additional bean definitions
               Class<?> candidateClass = candidate.loadClass();
               ImportBeanDefinitionRegistrar registrar =
                     BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
               ParserStrategyUtils.invokeAwareMethods(
                     registrar, this.environment, this.resourceLoader, this.registry);
               configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
            }
            else {
               // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
               // process it as an @Configuration class
               this.importStack.registerImport(
                     currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
               processConfigurationClass(candidate.asConfigClass(configClass));
            }
         }
      }
      catch (BeanDefinitionStoreException ex) {
         throw ex;
      }
      catch (Throwable ex) {
         throw new BeanDefinitionStoreException(
               "Failed to process import candidates for configuration class [" +
               configClass.getMetadata().getClassName() + "]", ex);
      }
      finally {
         this.importStack.pop();
      }
   }
}
  • 以上代码有两个关键点:
  • 第一、当前被处理的类,如果实现了 DeferredImportSelector 接口,就被加入到集合 deferredImportSelectors 中;
  • 第二、当前被处理的类,如果没有实现 DeferredImportSelector 接口,但是实现了 ImportSelector 接口,就被执行 selectImports 方法;

也就是说,processImports()方法为
processDeferredImportSelectors()方法提供了数据。ImportSelector 与 DeferredImportSelector 的区别,就是 selectImports 方法执行时机有差别,这个差别期间,spring 容器对此 Configuration 类做了些其他的逻辑:包括对 @ImportResource、@Bean 这些注解的处理。

总结:

SpringBoot使用Bean工厂后置处理器
ConfigurationClassPostProcessor,将starter.jar包中spring.factories文件配置的自动装配类读取出来,当某个AutoConfiguration类满足其注解@Conditional指定的生效条件(Starters提供的依赖、配置或Spring容器中是否存在某个Bean等)时,实例化该AutoConfiguration类中定义的Bean(组件等),并注入Spring容器,就可以完成依赖框架的自动配置。