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的属性进行绑定
相关推荐
Chrikk1 小时前
Go-性能调优实战案例
开发语言·后端·golang
幼儿园老大*1 小时前
Go的环境搭建以及GoLand安装教程
开发语言·经验分享·后端·golang·go
canyuemanyue1 小时前
go语言连续监控事件并回调处理
开发语言·后端·golang
杜杜的man1 小时前
【go从零单排】go语言中的指针
开发语言·后端·golang
测开小菜鸟1 小时前
使用python向钉钉群聊发送消息
java·python·钉钉
P.H. Infinity2 小时前
【RabbitMQ】04-发送者可靠性
java·rabbitmq·java-rabbitmq
生命几十年3万天2 小时前
java的threadlocal为何内存泄漏
java
caridle3 小时前
教程:使用 InterBase Express 访问数据库(五):TIBTransaction
java·数据库·express
^velpro^3 小时前
数据库连接池的创建
java·开发语言·数据库
苹果醋33 小时前
Java8->Java19的初步探索
java·运维·spring boot·mysql·nginx