SpringBoot自动配置原理源码分析

SpringBoot的自动配置是对Spring的一大增强,让我们从Spring的繁杂配置中解脱出来,同时这也是SpringBoot的老生常谈的话题了,接下来就来分析自动配置的源码,剖析它的原理

@EnableAutoConfiguration

众所周知,@SpringBootApplication由三个注解组成,一个是**@CompomentScan**,一个是**@SpringBootConfiguration**,还有一个就是**@EnableAutoConfiguration**

其中自动配置是在@EnableAutoConfiguration中完成的

java 复制代码
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration

在@EnableAutoConfiguration的上面,使用了@Import注解引入了一个新的类AutoConfigurationImportSelector

所以可以推测自动配置的逻辑也在AutoConfigurationImportSelector中

但是AutoConfigurationImportSelector与自动配置之间有什么关系呢?为啥自动配置要@Import它呢?@Import做了什么事情呢?@Import在什么时候生效呢?

ConfigurationClassPostProcessor

跟配置类有关的处理器是ConfigurationClassPostProcessor,被@Configuration修饰的配置类在这个处理器中处理并注册到BeanDefinitionMap中

ConfigurationClassPostProcessor是一个BeanFactoryPostProcessor,同时也是BeanDefinitionRegistryPostProcessor,它会在invokeBeanFactoryPostProcessors执行invokeBeanFactoryPostProcessors和invokeBeanDefinitionRegistryPostProcessors

而自动配置的逻辑,就在ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry中,在这里调用了processConfigBeanDefinitions

以下代码是processConfigBeanDefinitions的一部分,这部分代码是用于处理被@Configuration修饰的类

java 复制代码
// org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions

// Parse each @Configuration class
ConfigurationClassParser parser = new ConfigurationClassParser(
      this.metadataReaderFactory, this.problemReporter, this.environment,
      this.resourceLoader, this.componentScanBeanNameGenerator, registry);

Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
   StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");
   parser.parse(candidates);
   parser.validate();

   Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
   configClasses.removeAll(alreadyParsed);

   // Read the model and create bean definitions based on its content
   if (this.reader == null) {
      this.reader = new ConfigurationClassBeanDefinitionReader(
            registry, this.sourceExtractor, this.resourceLoader, this.environment,
            this.importBeanNameGenerator, parser.getImportRegistry());
   }
   this.reader.loadBeanDefinitions(configClasses);
   alreadyParsed.addAll(configClasses);
   processConfig.tag("classCount", () -> String.valueOf(configClasses.size())).end();

   candidates.clear();
   // ...
while (!candidates.isEmpty());

我们关注这一行代码

java 复制代码
parser.parse(candidates);

这里的candidates是一个包含主启动类的BeanDefinition的set(我这次的主启动类就是mybatisDemoApplication)

然后看看parse在做什么

java 复制代码
// org.springframework.context.annotation.ConfigurationClassParser#parse(java.util.Set<org.springframework.beans.factory.config.BeanDefinitionHolder>)

public void parse(Set<BeanDefinitionHolder> configCandidates) {
   for (BeanDefinitionHolder holder : configCandidates) {
      BeanDefinition bd = holder.getBeanDefinition();
      try {
         if (bd instanceof AnnotatedBeanDefinition) {
            parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
         }
         // ...
      }
      // catch
   }

   this.deferredImportSelectorHandler.process();
}

由于主启动类的BeanDefinition属于AnnotatedGenericBeanDefinition,所以走这段逻辑

java 复制代码
if (bd instanceof AnnotatedBeanDefinition) {
   parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}

跟进

java 复制代码
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
   processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER);
}

继续跟进

java 复制代码
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
   if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
      return;
   }

   // ...

   // Recursively process the configuration class and its superclass hierarchy.
   SourceClass sourceClass = asSourceClass(configClass, filter);
   do {
      sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
   }
   while (sourceClass != null);

   this.configurationClasses.put(configClass, configClass);
}

下面这个方法就是真正在处理了

java 复制代码
protected final SourceClass doProcessConfigurationClass(
      ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
      throws IOException {

   // ...

   // Process any @PropertySource annotations
   // ...

   // Process any @ComponentScan annotations
   // ...

   // Process any @Import annotations
   processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

   // Process any @ImportResource annotations
   //...
    
   // Process individual @Bean methods
   // ...

   // ...
    
   // No superclass -> processing is complete
   return null;
}

这个方法中有很多很重要的逻辑,包括我们熟悉的@ComponentScan和@Bean,但是我们先不管它们

我们直接看processImports

java 复制代码
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
      Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
      boolean checkForCircularImports) {

	// ...
         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 = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
                     this.environment, this.resourceLoader, this.registry);
               // ...
               if (selector instanceof DeferredImportSelector) {
                  this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
               }
               // ...
            }
            // ...
      }
	// ...
}

还是关心主要的核心逻辑,别的逻辑我都去掉了,不然真的太乱了

这个importCandidates里面是这两个

也就是主启动类中import的两个类

我们关心AutoConfigurationImportSelector

java 复制代码
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
      ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered

由于AutoConfigurationImportSelector属于DeferredImportSelector

所以会进入这一行

java 复制代码
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
java 复制代码
public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {
   DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(configClass, importSelector);
   if (this.deferredImportSelectors == null) {
      // ...
   }
   else {
      this.deferredImportSelectors.add(holder);
   }
}

这里deferredImportSelectors是非空的

可以看到DeferredImportSelector被放到deferredImportSelectors了

然后一路返回到之前的parse方法

java 复制代码
// org.springframework.context.annotation.ConfigurationClassParser#parse(java.util.Set<org.springframework.beans.factory.config.BeanDefinitionHolder>)

public void parse(Set<BeanDefinitionHolder> configCandidates) {
   for (BeanDefinitionHolder holder : configCandidates) {
      BeanDefinition bd = holder.getBeanDefinition();
      try {
         if (bd instanceof AnnotatedBeanDefinition) {
            parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
         }
         // ...
      }
      // catch
   }

   this.deferredImportSelectorHandler.process();
}
java 复制代码
// deferredImportSelectorHandler.process
public void process() {
   List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
   this.deferredImportSelectors = null;
   try {
      if (deferredImports != null) {
         DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
         deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
         deferredImports.forEach(handler::register);
         handler.processGroupImports();
      }
   }
   finally {
      this.deferredImportSelectors = new ArrayList<>();
   }
}
java 复制代码
public void processGroupImports() {
   for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
      Predicate<String> exclusionFilter = grouping.getCandidateFilter();
      grouping.getImports().forEach(entry -> {
         // 递归处理
      });
   }
}
java 复制代码
public Iterable<Group.Entry> getImports() {
   for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
      this.group.process(deferredImport.getConfigurationClass().getMetadata(),
            deferredImport.getImportSelector());
   }
   return this.group.selectImports();
}
java 复制代码
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
   AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
         .getAutoConfigurationEntry(annotationMetadata);
   this.autoConfigurationEntries.add(autoConfigurationEntry);
   for (String importClassName : autoConfigurationEntry.getConfigurations()) {
      this.entries.putIfAbsent(importClassName, annotationMetadata);
   }
}

然后来到了最末尾

java 复制代码
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
   if (!isEnabled(annotationMetadata)) {
      return EMPTY_ENTRY;
   }
   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);
}
java 复制代码
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
   List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
         getBeanClassLoader());
   Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
         + "are using a custom packaging, make sure that file is correct.");
   return configurations;
}

可以看到这里就是获取autoConfiguration

这个configurations是从哪里来的呢?

可以看到就是从META/spring.factories中加载进来的

但是其实真正符合条件的没有那么多

还要经过一层过滤

java 复制代码
configurations = getConfigurationClassFilter().filter(configurations);

最后全部注册到BeanDefinitionMap里面

这里有79个是因为要处理那些AutoConfiguration中的@Configuration内部类

以上就是把自动配置的Bean加入到BeanDefinitionMap里面,至于什么时候生效呢?

在yml配置的属性会在ConfigurationPropertiesBindingPostProcessor中的postProcessBeforeInitialization去做属性的绑定

java 复制代码
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
   bind(ConfigurationPropertiesBean.get(this.applicationContext, bean, beanName));
   return bean;
}

总结

自动配置的链路还是比较长的,总结一下大致流程如下:

  1. springboot启动的时候,会通过loadSpringFactories把META/spring.factories中的AutoConfiguartion加载进来
  2. 在invokeBeanFactoryPostProcessor会将自动配置类那些AutoConfiguration过滤之后注册到BeanDefinitionMap中
  3. 在初始化bean时,通过ConfigurationPropertiesBindingPostProcessor的postProcessBeforeInitialization将yml的属性进行绑定
相关推荐
kinlon.liu1 分钟前
零信任安全架构--持续验证
java·安全·安全架构·mfa·持续验证
王哲晓22 分钟前
Linux通过yum安装Docker
java·linux·docker
java66666888827 分钟前
如何在Java中实现高效的对象映射:Dozer与MapStruct的比较与优化
java·开发语言
Violet永存28 分钟前
源码分析:LinkedList
java·开发语言
执键行天涯28 分钟前
【经验帖】JAVA中同方法,两次调用Mybatis,一次更新,一次查询,同一事务,第一次修改对第二次的可见性如何
java·数据库·mybatis
Adolf_199342 分钟前
Flask-JWT-Extended登录验证, 不用自定义
后端·python·flask
Jarlen43 分钟前
将本地离线Jar包上传到Maven远程私库上,供项目编译使用
java·maven·jar
蓑 羽1 小时前
力扣438 找到字符串中所有字母异位词 Java版本
java·算法·leetcode
叫我:松哥1 小时前
基于Python flask的医院管理学院,医生能够增加/删除/修改/删除病人的数据信息,有可视化分析
javascript·后端·python·mysql·信息可视化·flask·bootstrap
Reese_Cool1 小时前
【C语言二级考试】循环结构设计
android·java·c语言·开发语言