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

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: 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进行判断循环处理。

目录
相关文章
|
22天前
|
存储 缓存 算法
HashMap深度解析:从原理到实战
HashMap,作为Java集合框架中的一个核心组件,以其高效的键值对存储和检索机制,在软件开发中扮演着举足轻重的角色。作为一名资深的AI工程师,深入理解HashMap的原理、历史、业务场景以及实战应用,对于提升数据处理和算法实现的效率至关重要。本文将通过手绘结构图、流程图,结合Java代码示例,全方位解析HashMap,帮助读者从理论到实践全面掌握这一关键技术。
69 13
|
2月前
|
运维 持续交付 云计算
深入解析云计算中的微服务架构:原理、优势与实践
深入解析云计算中的微服务架构:原理、优势与实践
77 1
|
1天前
|
JavaScript Java 程序员
SpringBoot自动配置及自定义Starter
Java程序员依赖Spring框架简化开发,但复杂的配置文件增加了负担。SpringBoot以“约定大于配置”理念简化了这一过程,通过引入各种Starter并加载默认配置,几乎做到开箱即用。
29 10
SpringBoot自动配置及自定义Starter
|
15天前
|
Java Maven Spring
SpringBoot配置跨模块扫描问题解决方案
在分布式项目中,使用Maven进行多模块开发时,某些模块(如xxx-common)没有启动类。如何将这些模块中的类注册为Spring管理的Bean对象?本文通过案例分析,介绍了两种解决方案:常规方案是通过`@SpringBootApplication(scanBasePackages)`指定扫描路径;推荐方案是保持各模块包结构一致(如com.xxx),利用SpringBoot默认扫描规则自动识别其他模块中的组件,简化配置。
SpringBoot配置跨模块扫描问题解决方案
|
8天前
|
存储 物联网 大数据
探索阿里云 Flink 物化表:原理、优势与应用场景全解析
阿里云Flink的物化表是流批一体化平台中的关键特性,支持低延迟实时更新、灵活查询性能、无缝流批处理和高容错性。它广泛应用于电商、物联网和金融等领域,助力企业高效处理实时数据,提升业务决策能力。实践案例表明,物化表显著提高了交易欺诈损失率的控制和信贷审批效率,推动企业在数字化转型中取得竞争优势。
48 14
|
22天前
|
NoSQL Java Redis
Spring Boot 自动配置机制:从原理到自定义
Spring Boot 的自动配置机制通过 `spring.factories` 文件和 `@EnableAutoConfiguration` 注解,根据类路径中的依赖和条件注解自动配置所需的 Bean,大大简化了开发过程。本文深入探讨了自动配置的原理、条件化配置、自定义自动配置以及实际应用案例,帮助开发者更好地理解和利用这一强大特性。
76 14
|
16天前
|
网络协议 安全 网络安全
探索网络模型与协议:从OSI到HTTPs的原理解析
OSI七层网络模型和TCP/IP四层模型是理解和设计计算机网络的框架。OSI模型包括物理层、数据链路层、网络层、传输层、会话层、表示层和应用层,而TCP/IP模型则简化为链路层、网络层、传输层和 HTTPS协议基于HTTP并通过TLS/SSL加密数据,确保安全传输。其连接过程涉及TCP三次握手、SSL证书验证、对称密钥交换等步骤,以保障通信的安全性和完整性。数字信封技术使用非对称加密和数字证书确保数据的机密性和身份认证。 浏览器通过Https访问网站的过程包括输入网址、DNS解析、建立TCP连接、发送HTTPS请求、接收响应、验证证书和解析网页内容等步骤,确保用户与服务器之间的安全通信。
71 1
|
2月前
|
缓存 IDE Java
SpringBoot入门(7)- 配置热部署devtools工具
SpringBoot入门(7)- 配置热部署devtools工具
62 1
SpringBoot入门(7)- 配置热部署devtools工具
|
2月前
|
存储 前端开发 JavaScript
springboot中路径默认配置与重定向/转发所存在的域对象
Spring Boot 提供了简便的路径默认配置和强大的重定向/转发机制,通过合理使用这些功能,可以实现灵活的请求处理和数据传递。理解并掌握不同域对象的生命周期和使用场景,是构建高效、健壮 Web 应用的关键。通过上述详细介绍和示例,相信读者能够更好地应用这些知识,优化自己的 Spring Boot 应用。
46 3
|
2月前
|
运维 持续交付 虚拟化
深入解析Docker容器化技术的核心原理
深入解析Docker容器化技术的核心原理
59 1

推荐镜像

更多
下一篇
开通oss服务