【Spring Boot 源码学习】自动装配流程源码解析(上)

简介: 本篇从Spring Boot 自动装配源码入手,分析自动配置组件加载的流程

微信图片_20231031101034.png

引言

上篇博文,笔者带大家从整体上了解了AutoConfigurationImportSelector 自动装配逻辑的核心功能及流程,由于篇幅有限,更加细化的功能及流程详解还没有介绍。本篇开始将从其源码入手,重点解析细化后的自动装配流程源码。

主要内容

下面就让我们从 AutoConfigurationImportSelectorselectImports 方法源码入手,开始了解自动装配流程。

下面来看一下 selectImports 的相关源码【Spring Boot 2.7.9】:

@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
   
   
    if (!isEnabled(annotationMetadata)) {
   
   
        return NO_IMPORTS;
    }
    AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
    return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}

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);
}

1. 自动配置开关

检查自动配置是否开启的逻辑位于 AutoConfigurationImportSelectorselectImports 方法中的第一段代码。如果开启自动配置,则继续执行后续操作;否则就返回空数组。

检查自动配置是否开启的源码,如下所示:

@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
   
   
    if (!isEnabled(annotationMetadata)) {
   
   
          return NO_IMPORTS;
       }
    // ...省略
}

从上面的源码可以看出,这里主要使用了 isEnabled 方法来判断自动配置是否开启;其中该方法返回 true,表示开启自动配置,返回 false,表示关闭自动配置。

我们来看一下它的源码,如下所示:

protected boolean isEnabled(AnnotationMetadata metadata) {
   
   
    if (getClass() == AutoConfigurationImportSelector.class) {
   
   
        return getEnvironment().getProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class, true);
    }
    return true;
}

通过阅读上述 isEnabled 方法源码,我们可以看出,如果当前类为 AutoConfigurationImportSelector,会从环境中获取 keyEnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY 的配置属性,而笔者前面的系列博文如果大家看过的话,介绍 EnableAutoConfiguration 注解时,就说了这个常量的值为 spring.boot.enableautoconfiguration

image.png

如果该属性的配置获取不到,则默认为 true,也就是默认会开启自动配置。如果当前类为其他类,也则默认直接返回 true

如果想覆盖或重置该配置,我们可以在 application.propertiesapplication.yml 中针对 spring.boot.enableautoconfiguration 参数进行配置。

application.properties 配置关闭自动配置为例,如下所示 :

spring.boot.enableautoconfiguration=false

2. 加载自动配置组件

接下来,我们看到调用 getAutoConfigurationEntry 的代码,它是用来封装将被引入的自动配置信息:

AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);

然后我们进入 getAutoConfigurationEntry 方法,看到了获取注解属性的逻辑,如下所示:

AnnotationAttributes attributes = getAttributes(annotationMetadata);

// 从 AnnotationMetadata 返回适当的 AnnotationAttributes。默认情况下,此方法将返回 getAnnotationClass() 的属性。
protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) {
   
   
    String name = getAnnotationClass().getName();
    AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true));
    Assert.notNull(attributes, () -> "No auto-configuration attributes found. Is " + metadata.getClassName()
            + " annotated with " + ClassUtils.getShortName(name) + "?");
    return attributes;
}

我们启动先前建的 Spring Boot 项目的应用类,在 getAttributes 方法最后 return 处打个断点,我们可以看到如下的逻辑:

image.png

了解到这,可以开始加载自动配置的组件了,也就是下面的代码:

// 通过 SpringFactoriesLoader 类提供的方法加载类路径中META-INF目录下的
// spring.factories文件中针对 EnableAutoConfiguration 的注解配置类
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);

我们进入 getCandidateConfigurations 方法中, 相关源码如下所示:

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
   
   
    List<String> configurations = new ArrayList<>(
            SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()));
    ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()).forEach(configurations::add);
    Assert.notEmpty(configurations,
            "No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you "
                    + "are using a custom packaging, make sure that file is correct.");
    return configurations;
}

通过阅读上述源码,我们可以看出这里使用了 SpringFactoriesLoader 类提供的loadFactoryNames 方法来加载的。其中 loadFactoryNames 方法的第一个参数为 getSpringFactoriesLoaderFactoryClass 方法返回的 EnableAutoConfiguration.class

protected Class<?> getSpringFactoriesLoaderFactoryClass() {
   
   
    return EnableAutoConfiguration.class;
}

通过翻看 loadFactoryNames 方法对应的源码,我们可以知道它是读取的 META-INF/spring.factories 中的配置,并且只会读取配置文件中针对自动配置的注册类【即 EnableAutoConfiguration 相关的配置信息】。

SpringFactoriesLoader 类的 loadFactoryNames 方法相关源码,由于篇幅有限,这里就不贴出来了,感兴趣的朋友可以自行查阅,Spring Boot 中后续会有很多地方用到它,比如用于监听的 Listeners 和用于过滤的 Filters

实际上 在 Spring Boot 2.7.9 版本中, 它自己内部的 META-INF/spring.factories 中有关自动配置的注册类的配置信息已经被去除掉了,不过其他外围的 jar 中可能有自己的 META-INF/spring.factories 文件,它里面也有关于自动配置注册类的配置信息;

另外我们在 getCandidateConfigurations 方法中,也看到了另一行代码获取自动配置注册类的信息,如下所示:

ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()).forEach(configurations::add);

这里的代码其实就是读取的如下截图的配置信息【同 META-INF/spring.factories 一样,下面的配置也可能存在于不同的 jar 包中 】:

image.png

我们启动先前建的 Spring Boot 项目的应用类,在 getCandidateConfigurations 方法 ImportCandidates 类调用处打个断点,我们可以看到如下的截图【这里 configurations 目前还是空数据,说明从 META-INF/spring.factories 没有获取到自动配置注册类的相关信息,因为我们只引入了 Spring Boot 项目,并且它内部的 META-INF/spring.factories 中的确删除了自动配置注册类的相关信息】:

image.png

getCandidateConfigurations 方法 最后 return 处打个断点,我们可以看到如下的截图【这里 configurations 中加载的都是自动配置的注册类,也就是 上述 ImportCandidates##load 加载的信息,这里读取的是 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 中的配置信息】:

image.png

3. 自动配置组件去重

因为上述加载的自动配置注册类,默认加载的是 ClassLoader 下面的所有 META-INF/spring.factoriesMETA-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件中的配置,所以就有可能出现在不同的jar包中有重复配置的情况。

Spring Boot 中则使用了 Java 中 Set 集合数据不可重复的特点,来实现去重处理,如下所示:

// 对获得的注解配置类集合进行去重处理,防止多个项目引入同样的配置类
configurations = removeDuplicates(configurations);

// 利用 Set 集合数据不可重复的特点,来实现去重处理
protected final <T> List<T> removeDuplicates(List<T> list) {
   
   
    return new ArrayList<>(new LinkedHashSet<>(list));
}

总结

本篇 Huazie 带大家通读了 Spring Boot 自动装配逻辑的部分源码,详细分析了加载自动装配的流程,剩下排除和过滤自动配置的流程将在下一篇继续讲解。内容较多,能够看到这的小伙伴,Huazie 在这感谢各位的支持。后续我将持续输出有关 Spring Boot 源码学习系列的博文,想要及时了解更新的朋友,订阅这里即可

目录
相关文章
|
4月前
|
JavaScript 前端开发 Java
制造业ERP源码,工厂ERP管理系统,前端框架:Vue,后端框架:SpringBoot
这是一套基于SpringBoot+Vue技术栈开发的ERP企业管理系统,采用Java语言与vscode工具。系统涵盖采购/销售、出入库、生产、品质管理等功能,整合客户与供应商数据,支持在线协同和业务全流程管控。同时提供主数据管理、权限控制、工作流审批、报表自定义及打印、在线报表开发和自定义表单功能,助力企业实现高效自动化管理,并通过UniAPP实现移动端支持,满足多场景应用需求。
422 1
|
5月前
|
前端开发 Java 关系型数据库
基于Java+Springboot+Vue开发的鲜花商城管理系统源码+运行
基于Java+Springboot+Vue开发的鲜花商城管理系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Java编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Java的鲜花商城管理系统项目,大学生可以在实践中学习和提升自己的能力,为以后的职业发展打下坚实基础。技术学习共同进步
411 7
|
5月前
|
前端开发 Java 物联网
智慧班牌源码,采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署
智慧班牌系统是一款基于信息化与物联网技术的校园管理工具,集成电子屏显示、人脸识别及数据交互功能,实现班级信息展示、智能考勤与家校互通。系统采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署与私有化定制。核心功能涵盖信息发布、考勤管理、教务处理及数据分析,助力校园文化建设与教学优化。其综合性和可扩展性有效打破数据孤岛,提升交互体验并降低管理成本,适用于日常教学、考试管理和应急场景,为智慧校园建设提供全面解决方案。
376 70
|
4月前
|
供应链 JavaScript BI
ERP系统源码,基于SpringBoot+Vue+ElementUI+UniAPP开发
这是一款专为小微企业打造的 SaaS ERP 管理系统,基于 SpringBoot+Vue+ElementUI+UniAPP 技术栈开发,帮助企业轻松上云。系统覆盖进销存、采购、销售、生产、财务、品质、OA 办公及 CRM 等核心功能,业务流程清晰且操作简便。支持二次开发与商用,提供自定义界面、审批流配置及灵活报表设计,助力企业高效管理与数字化转型。
428 2
ERP系统源码,基于SpringBoot+Vue+ElementUI+UniAPP开发
|
3月前
|
机器学习/深度学习 数据采集 人机交互
springboot+redis互联网医院智能导诊系统源码,基于医疗大模型、知识图谱、人机交互方式实现
智能导诊系统基于医疗大模型、知识图谱与人机交互技术,解决患者“知症不知病”“挂错号”等问题。通过多模态交互(语音、文字、图片等)收集病情信息,结合医学知识图谱和深度推理,实现精准的科室推荐和分级诊疗引导。系统支持基于规则模板和数据模型两种开发原理:前者依赖人工设定症状-科室规则,后者通过机器学习或深度学习分析问诊数据。其特点包括快速病情收集、智能病症关联推理、最佳就医推荐、分级导流以及与院内平台联动,提升患者就诊效率和服务体验。技术架构采用 SpringBoot+Redis+MyBatis Plus+MySQL+RocketMQ,确保高效稳定运行。
238 0
|
10月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
261 2
|
6月前
|
算法 测试技术 C语言
深入理解HTTP/2:nghttp2库源码解析及客户端实现示例
通过解析nghttp2库的源码和实现一个简单的HTTP/2客户端示例,本文详细介绍了HTTP/2的关键特性和nghttp2的核心实现。了解这些内容可以帮助开发者更好地理解HTTP/2协议,提高Web应用的性能和用户体验。对于实际开发中的应用,可以根据需要进一步优化和扩展代码,以满足具体需求。
610 29
|
6月前
|
前端开发 数据安全/隐私保护 CDN
二次元聚合短视频解析去水印系统源码
二次元聚合短视频解析去水印系统源码
178 4
|
6月前
|
JavaScript 算法 前端开发
JS数组操作方法全景图,全网最全构建完整知识网络!js数组操作方法全集(实现筛选转换、随机排序洗牌算法、复杂数据处理统计等情景详解,附大量源码和易错点解析)
这些方法提供了对数组的全面操作,包括搜索、遍历、转换和聚合等。通过分为原地操作方法、非原地操作方法和其他方法便于您理解和记忆,并熟悉他们各自的使用方法与使用范围。详细的案例与进阶使用,方便您理解数组操作的底层原理。链式调用的几个案例,让您玩转数组操作。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
6月前
|
移动开发 前端开发 JavaScript
从入门到精通:H5游戏源码开发技术全解析与未来趋势洞察
H5游戏凭借其跨平台、易传播和开发成本低的优势,近年来发展迅猛。接下来,让我们深入了解 H5 游戏源码开发的技术教程以及未来的发展趋势。

热门文章

最新文章

推荐镜像

更多
  • DNS