SpringBoot自动配置原理解析(三)

简介: SpringBoot自动配置原理解析(三)

关联博文:

SpringBoot自动配置原理解析(一)

SpringBoot自动配置原理解析(二)

SpringBoot自动配置原理解析(三)

SpringBoot自动配置原理解析(四)

SpringBoot自动配置原理解析(五)

接上文SpringBoot自动配置原理解析(二)我们继续往下分析。


如下是ConfigurationClassParser的parse方法,当内部parse方法执行完后,就该执行this.deferredImportSelectorHandler.process();方法。这个时候我们的@Bean方法还只是configClass中的BeanMethod,还没有被注册为BeanDefinition。

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());
      }
      else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
        parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
      }
      else {
        parse(bd.getBeanClassName(), holder.getBeanName());
      }
    }
    //...两个catch
  }
  this.deferredImportSelectorHandler.process();
}


我们来看看deferredImportSelectorHandler的process方法。


【1】DeferredImportSelectorHandler

DeferredImportSelectorHandler是ConfigurationClassParser的内部类,维护了一个成员deferredImportSelectors 存储DeferredImportSelectorHolder。

private List<DeferredImportSelectorHolder> deferredImportSelectors = new ArrayList<>();


前文我们看到了,我们AutoConfigurationImportSelector被实例化为DeferredImportSelectorHolder放到了deferredImportSelectors 中。


前面我们触发的是其handle方法,这里我们看下其process方法。

public void process() {
// 做一下置换,将deferredImportSelectors 赋予null
  List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
  this.deferredImportSelectors = null;
  try {
    if (deferredImports != null) {
    //实例化DeferredImportSelectorGroupingHandler 
      DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
      // 排序
      deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
      //对每一个DeferredImportSelectorHolder触发
      //DeferredImportSelectorGroupingHandler 的register方法
      deferredImports.forEach(handler::register);
      // 这里是核心
      handler.processGroupImports();
    }
  }
  finally {
    this.deferredImportSelectors = new ArrayList<>();
  }
}



这里有两个方法需要我们注意,分别是register和processGroupImports。我们首先看一下deferredImports.forEach(handler::register);

① register


这里核心就是对List<DeferredImportSelectorHolder> deferredImports进行分组放到groupings中,并将所属configClass放到configurationClasses中。

// ConfigurationClassParser.DeferredImportSelectorGroupingHandler#register
public void register(DeferredImportSelectorHolder deferredImport) {
// 本文这里获取的group是AutoConfigurationImportSelector$AutoConfigurationGroup
  Class<? extends Group> group = deferredImport.getImportSelector()
      .getImportGroup();
//放入groupings并将value返回
  DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent(
      (group != null ? group : deferredImport),
      key -> new DeferredImportSelectorGrouping(createGroup(group)));
  grouping.add(deferredImport);
//放入configurationClasses中
  this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(),
      deferredImport.getConfigurationClass());
}



方法如上所示,首先得到ImportSelector的ImportGroup,本文这里得到的是AutoConfigurationImportSelector$AutoConfigurationGroup。


然后根据group 得到grouping ,将当前DeferredImportSelectorHolder deferredImport放到grouping 中。本文这里的deferredImport如下所示,其包含了所属configClass和引入的AutoConfigurationImportSelector。


当前this中groupings如下所示:



当前this中configurationClasses如下所示:


注意,这里的this指的是DeferredImportSelectorGroupingHandler。如下所示,DeferredImportSelectorGroupingHandler维护了两个Map。

private final Map<Object, DeferredImportSelectorGrouping> groupings = new LinkedHashMap<>();
private final Map<AnnotationMetadata, ConfigurationClass> configurationClasses = new HashMap<>();



② processGroupImports



register方法可以跳过,但是这个processGroupImports方法是核心。我们继续看DeferredImportSelectorGroupingHandler的processGroupImports方法。首先遍历触发每个grouping的getImports方法然后遍历触发processImports方法。当然本文这里我们只有一个分组就是AutoConfigurationGroup。

public void processGroupImports() {
  for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
  //本文我们得到的候选配置有45个
    grouping.getImports().forEach(entry -> {
      ConfigurationClass configurationClass = this.configurationClasses.get(
          entry.getMetadata());
      try {
      // 这里先说一下,执行完这个方法后候选配置有244个
      // 我们从spring.factories得到的配置类在这个processImports中被处理
        processImports(configurationClass, asSourceClass(configurationClass),
            asSourceClasses(entry.getImportClassName()), false);
      }
      //...一堆catch
    });
  }
}


entry里面存放的是什么呢?如下图所示:



processImports方法就是前文我们分析过的那个processImports方法。这里我们可以简单说一下,当我们获取到那些配置类比如MessageSourceAutoConfiguration时,这里会触发下面这行代码。

processImports(configurationClass, asSourceClass(configurationClass),
            asSourceClasses(entry.getImportClassName()), false);


唯一需要注意的是asSourceClasses(entry.getImportClassName())其实也就是说我们的XXXXAutoConfiguration会被作为候选导入类Collection<SourceClass> importCandidates扔给processImports方法。前文我们提到过importCandidates是可以被作为配置类触发解析过程的,也是可以在reader.loadBeanDefinitions过程中被注册为BeanDefinition的。有同学可能好奇,configurationClass是什么?如下图所示这里是我们的主启动类。



ok,接下来我们看一下getImports方法。这里触发的是DeferredImportSelectorGrouping 的getImports方法。


【2】DeferredImportSelectorGrouping

DeferredImportSelectorGrouping 如下所示,内部维护了group和与group相关的List<DeferredImportSelectorHolder> deferredImports。

private static class DeferredImportSelectorGrouping {
//维护了group
  private final DeferredImportSelector.Group group;
//维护了集合deferredImports 
  private final List<DeferredImportSelectorHolder> deferredImports = new ArrayList<>();
  DeferredImportSelectorGrouping(Group group) {
    this.group = group;
  }
//提供了add方法
  public void add(DeferredImportSelectorHolder deferredImport) {
    this.deferredImports.add(deferredImport);
  }
  /**
   * Return the imports defined by the group.
   * @return each import with its associated configuration class
   */
   // 对deferredImports进行遍历,对group触发其process方法
  public Iterable<Group.Entry> getImports() {
    for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
      this.group.process(deferredImport.getConfigurationClass().getMetadata(),
          deferredImport.getImportSelector());
    }
    // 触发group的selectImports方法
    return this.group.selectImports();
  }
}


前面我们提到了,这里group就是我们的AutoConfigurationGroup。下面我们分析process()selectImports()方法。

① process


这里触发的是AutoConfigurationGroup的process方法。

// .AutoConfigurationImportSelector.AutoConfigurationGroup#process
@Override
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
  //...省略一个Assert.state,下面这句话是核心
  AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
      .getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
//将得到autoConfigurationEntry放入ArrayList autoConfigurationEntries中
  this.autoConfigurationEntries.add(autoConfigurationEntry);
//遍历放入Map<String, AnnotationMetadata> entries = new LinkedHashMap<>()
  for (String importClassName : autoConfigurationEntry.getConfigurations()) {
    this.entries.putIfAbsent(importClassName, annotationMetadata);
  }
}


getAutoConfigurationMetadata()


首先触发getAutoConfigurationMetadata()方法,用来获取AutoConfigurationMetadata 。


AutoConfigurationImportSelector.AutoConfigurationGroup的getAutoConfigurationMetadata方法会实例化得到一个AutoConfigurationMetadata autoConfigurationMetadata。


其将会搜索并加载META-INF/spring-autoconfigure-metadata.properties配置信息得到其Properties然后根据Properties实例化得到PropertiesAutoConfigurationMetadata对象。

(1.2)核心方法getAutoConfigurationEntry

//// AutoConfigurationImportSelector#getAutoConfigurationEntry
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
    AnnotationMetadata annotationMetadata) {
// 如果环境配置了spring.boot.enableautoconfiguration为false,则直接返回
  if (!isEnabled(annotationMetadata)) {
    return EMPTY_ENTRY;
  }
  // 得到注解的属性
  AnnotationAttributes attributes = getAttributes(annotationMetadata);
  //获取候选配置类
  List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
  //移除重复的
  configurations = removeDuplicates(configurations);
  //获取配置的排除信息并从configurations移除排除的
  Set<String> exclusions = getExclusions(annotationMetadata, attributes);
  checkExcludedClasses(configurations, exclusions);
  configurations.removeAll(exclusions);
  //过滤configurations
  configurations = filter(configurations, autoConfigurationMetadata);
  // 触发AutoConfigurationImportListener监听的onAutoConfigurationImportEvent方法
  //可以简单理解为AutoConfigurationImportEvent事件被相应的监听器处理
  fireAutoConfigurationImportEvents(configurations, exclusions);
  //返回AutoConfigurationEntry
  return new AutoConfigurationEntry(configurations, exclusions);
}

方法流程梳理如下


如果环境配置了spring.boot.enableautoconfiguration为false,则直接返回

得到注解的属性

获取候选配置类–核心方法

移除重复的

获取配置的排除信息并从configurations移除排除的

过滤configurations

触发AutoConfigurationImportListener监听的onAutoConfigurationImportEvent方法,可以简单理解为AutoConfigurationImportEvent事件被相应的监听器处理

返回AutoConfigurationEntry


那么如何得到候选配置类?我们继续追踪getCandidateConfigurations(annotationMetadata, attributes);这个方法。


这里来到了AutoConfigurationImportSelectorgetCandidateConfigurations,很明了,从META-INF/spring.factories检索EnableAutoConfiguration配置信息!!

// AutoConfigurationImportSelector#getCandidateConfigurations
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
// 从META-INF/spring.factories检索EnableAutoConfiguration配置信息
  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;
}
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
  return EnableAutoConfiguration.class;
}

这里得到的configurations如下所示,有130个:


如何过滤configurations呢?这里会从环境中检索AutoConfigurationImportFilter使用得到的Filter进行过滤。

protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
  return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);
}
// META-INF/spring.factories配置的AutoConfigurationImportFilter如下
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition


如下所示,过滤完这里本文这里还剩下45个:


OK,我们继续回到DeferredImportSelectorGroupinggetImports方法,接下来分析selectImports方法。

② selectImports


AutoConfigurationImportSelector.AutoConfigurationGroup的selectImports方法。就是对autoConfigurationEntries维护的配置类进行了整体筛选然后排序然后映射得到一个List。

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());
// 获取到autoConfigurationEntries中所有配置类
  Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
      .map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream)
      .collect(Collectors.toCollection(LinkedHashSet::new));
// 从中移除排他配置
  processedConfigurations.removeAll(allExclusions);
//排序 然后映射得到Entry这样一个List
  return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
      .map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
      .collect(Collectors.toList());
}

当执行完所有的候选配置类后,也就是对我们从META-INF\spring.factories加载并过滤后的配置类执行完processImports方法后,来到了this.deferredImportSelectors = new ArrayList<>();,这时我们解析器里面的候选配置类有244个了!


e40f995331a541198866b4e31d7d55c9.png


得到一个entry 的集合后就开始遍历对每一个entry 触发processImports方法。

grouping.getImports().forEach(entry -> {
  ConfigurationClass configurationClass = this.configurationClasses.get(
      entry.getMetadata());
  try {
  // 这里先说一下,执行完这个方法后候选配置有244个
  // 我们从spring.factories得到的配置类在这个processImports中被处理
    processImports(configurationClass, asSourceClass(configurationClass),
        asSourceClasses(entry.getImportClassName()), false);
}


接下来就该ConfigurationClassBeanDefinitionReader使用加载BeanDefinition了,然后将registry.getBeanDefinitionCount() > candidateNames.length进行判断循环处理。

目录
相关文章
|
14小时前
|
安全 Java 开发者
深入理解Spring Boot配置绑定及其实战应用
【4月更文挑战第10天】本文详细探讨了Spring Boot中配置绑定的核心概念,并结合实战示例,展示了如何在项目中有效地使用这些技术来管理和绑定配置属性。
11 1
|
13小时前
|
机器学习/深度学习 存储 算法
卷积神经网络(CNN)的数学原理解析
卷积神经网络(CNN)的数学原理解析
31 1
卷积神经网络(CNN)的数学原理解析
|
13小时前
|
传感器 数据采集 存储
岩土工程监测仪器之一:振弦采集仪的工作原理解析
岩土工程监测仪器之一:振弦采集仪的工作原理解析
岩土工程监测仪器之一:振弦采集仪的工作原理解析
|
13小时前
|
XML JavaScript 数据格式
Beautiful Soup 库的工作原理基于解析器和 DOM(文档对象模型)树的概念
【5月更文挑战第10天】Beautiful Soup 使用解析器(如 html.parser, lxml, html5lib)解析HTML/XML文档,构建DOM树。它提供方法查询和操作DOM,如find(), find_all()查找元素,get_text(), get()提取信息。还能修改DOM,添加、修改或删除元素,并通过prettify()输出格式化字符串。它是处理网页数据的利器,尤其在处理不规则结构时。
33 2
|
13小时前
|
机器学习/深度学习 人工智能 数据可视化
号称能打败MLP的KAN到底行不行?数学核心原理全面解析
Kolmogorov-Arnold Networks (KANs) 是一种新型神经网络架构,挑战了多层感知器(mlp)的基础,通过在权重而非节点上使用可学习的激活函数(如b样条),提高了准确性和可解释性。KANs利用Kolmogorov-Arnold表示定理,将复杂函数分解为简单函数的组合,简化了神经网络的近似过程。与mlp相比,KAN在参数量较少的情况下能达到类似或更好的性能,并能直观地可视化,增强了模型的可解释性。尽管仍需更多研究验证其优势,KAN为深度学习领域带来了新的思路。
103 5
|
14小时前
|
敏捷开发 测试技术 持续交付
极限编程(XP)原理与技巧:深入解析与实践
【5月更文挑战第8天】极限编程(XP)是一种敏捷开发方法,注重快速反馈、迭代开发和简单设计,以提高软件质量和项目灵活性。关键原则包括客户合作、集体代码所有权、持续集成等。实践中,使用故事卡片描述需求,遵循编程约定,实行TDD,持续重构,结对编程,并定期举行迭代会议。通过理解和应用XP,团队能提升效率,应对变化。
|
13小时前
|
Java 文件存储 Spring
【springboot】logback配置
【springboot】logback配置
19 1
|
13小时前
|
缓存 自然语言处理 JavaScript
万字长文深度解析JDK序列化原理及Fury高度兼容的极致性能实现
Fury是一个基于JIT动态编译的高性能多语言原生序列化框架,支持Java/Python/Golang/C++/JavaScript等语言,提供全自动的对象多语言/跨语言序列化能力,以及相比于别的框架最高20~200倍的性能。
168470 0
|
13小时前
|
Java 微服务 Spring
Spring Boot中获取配置参数的几种方法
Spring Boot中获取配置参数的几种方法
22 2
|
14小时前
|
存储 芯片
【期末不挂科-单片机考前速过系列P11】(第十一章:15题速过串行口的工作原理和应用)经典例题盘点(带图解析)
【期末不挂科-单片机考前速过系列P11】(第十一章:15题速过串行口的工作原理和应用)经典例题盘点(带图解析)

推荐镜像

更多