史上最全!!!大厂面试真题-SpringBoot自动装配的原理是什么?

我想你也在真实面试中被问过无数次这个问题了,我也是,但是不管你怎么搜,都只有那几篇八股文的答案,你问GPT它都解释不清楚,我决定自己写一篇详细的,避免遗忘也想帮助一下患难中的兄弟姐妹们,能把这篇文章搞懂,这个面试题必过!!!!祝各位早日上岸

SpringBoot实现自动装配的原理是其核心特性之一,它极大地简化了Spring应用程序的配置过程,让开发者能够快速构建Spring应用。下面详细解释SpringBoot自动装配的原理,并尝试结合源代码进行说明(注意,由于直接引用完整源代码篇幅较长且涉及版权问题,这里将基于原理和关键组件进行说明)。

SpringBoot自动装配原理概述

SpringBoot自动装配主要通过以下几个关键组件和机制实现:

  1. @SpringBootApplication注解
    • 这是一个组合注解,包含了@SpringBootConfiguration、@EnableAutoConfiguration和@ComponentScan三个核心注解。
    • @SpringBootConfiguration:表明该类是一个配置类,它实际上是一个@Configuration注解的派生注解,用于定义Bean和配置应用程序。
    • @EnableAutoConfiguration:开启自动配置功能,通过@Import注解引入AutoConfigurationImportSelector类,该类负责扫描并加载自动配置类。
    • @ComponentScan:指定Spring Boot扫描组件的基础包,用于注册带有@Controller、@Service、@Repository、@Component等注解的类为Spring管理的Bean。
  2. AutoConfigurationImportSelector
    • 实现了ImportSelector接口,其selectImports方法会根据应用的上下文环境和类路径中存在的依赖,动态地选择需要导入的自动配置类。
    • 这一过程主要依赖于META-INF/spring.factories文件中的配置,该文件位于spring-boot-autoconfigure项目的JAR包中,包含了大量自动配置类的全限定名。
  3. spring.factories文件
    • 这是一个位于JAR包META-INF目录下的文件,以key=value的形式列出了自动配置类的全限定名。
    • 当@EnableAutoConfiguration注解生效时,Spring Boot会读取这个文件,并根据其中的配置加载相应的自动配置类。
  4. 条件注解(@Conditional注解及其扩展)
    • 自动配置类中使用了大量条件注解(如@ConditionalOnClass、@ConditionalOnMissingBean、@ConditionalOnProperty等),这些注解用于控制Bean的创建和配置类的激活。
    • 例如,@ConditionalOnClass注解会检查类路径中是否存在某个类,如果存在,则满足条件;@ConditionalOnMissingBean注解会检查Spring容器中是否已经存在某个Bean,如果不存在,则满足条件。

简单总结一下就是:(1)@SpringBootApplication引入了@EnableAutoConfiguration(负责启动自动配置功能) 

(2)@EnableAutoConfiguration引入了@Import,里面引入了AutoConfigurationImportSelector,z这个类又实现了DeferredImportSelector(咱可以给它翻译成延迟导入选择器),它会使SpringBoot的自动配置类的顺序放在最后,方便扩展和覆盖(比如我们有很多的ContidionalOnBean,Bean解析之后,自动配置类才能准确的更好的发挥作用)

(3)DeferredImportSelector才用类似SPI的机制(SPI对应的名字应该是接口名字,另外应该在/META-INF/service下)实现导入classpath下的所有的/META-INF/spring.factories文件

源代码层面的简要说明(非完整代码)

由于直接展示完整源代码篇幅过长,这里仅对关键部分进行简要说明:

  1. @EnableAutoConfiguration注解

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @AutoConfigurationPackage
    @Import(AutoConfigurationImportSelector.class)
    public @interface EnableAutoConfiguration {/*** Environment property that can be used to override when auto-configuration is* enabled.*/String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";/*** Exclude specific auto-configuration classes such that they will never be applied.* @return the classes to exclude*/Class<?>[] exclude() default {};/*** Exclude specific auto-configuration class names such that they will never be* applied.* @return the class names to exclude* @since 1.3.0*/String[] excludeName() default {};}
     

    注意@Import(AutoConfigurationImportSelector.class),这里导入了AutoConfigurationImportSelector类,用于后续的自动配置类加载。

  2. AutoConfigurationImportSelector类

    public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered { 
    // ... 其他方法和属性 @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {if (!isEnabled(annotationMetadata)) {return NO_IMPORTS;}
    // 根据条件加载自动配置类 AutoConfigurationEntry autoConfigurationEntry=getAutoConfigurationEntry(annotationMetadata);return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }// ... selectImports方法的具体实现细节会涉及到读取spring.factories文件等 
    }


    selectImports方法是自动配置加载的核心,它会根据应用的上下文环境和类路径中的依赖,动态地选择需要导入的自动配置类。里面会调用getAutoConfigurationEntry,以下是方法的具体代码

        

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

里面最重要的源码是调用了getCandidateConfigurations,看具体代码

/*** Return the auto-configuration class names that should be considered. By default* this method will load candidates using {@link SpringFactoriesLoader} with* {@link #getSpringFactoriesLoaderFactoryClass()}.* @param metadata the source metadata* @param attributes the {@link #getAttributes(AnnotationMetadata) annotation* attributes}* @return a list of candidate configurations*/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;}

loadFactoryNames就是我们的主要导入配置的方法了

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {ClassLoader classLoaderToUse = classLoader;if (classLoader == null) {classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();}String factoryTypeName = factoryType.getName();return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());}

 里面有几个重要的点需要我们关注一下:(1)factoryType来自我们传入的第一个参数也就是getSpringFactoriesLoaderFactoryClass(),代码如下

/*** Return the class used by {@link SpringFactoriesLoader} to load configuration* candidates.* @return the factory class*/protected Class<?> getSpringFactoriesLoaderFactoryClass() {return EnableAutoConfiguration.class;}

说明我们要导入的EnableAutoConfiguration这个类对应的value

(2)loadSpringFactroies是实际load的过程,代码一看就能知道为什么说我们导入的是classpath下的所有的spring.factories

private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {Map<String, List<String>> result = (Map)cache.get(classLoader);if (result != null) {return result;} else {HashMap result = new HashMap();try {Enumeration urls = classLoader.getResources("META-INF/spring.factories");while(urls.hasMoreElements()) {URL url = (URL)urls.nextElement();UrlResource resource = new UrlResource(url);Properties properties = PropertiesLoaderUtils.loadProperties(resource);Iterator var6 = properties.entrySet().iterator();while(var6.hasNext()) {Entry<?, ?> entry = (Entry)var6.next();String factoryTypeName = ((String)entry.getKey()).trim();String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());String[] var10 = factoryImplementationNames;int var11 = factoryImplementationNames.length;for(int var12 = 0; var12 < var11; ++var12) {String factoryImplementationName = var10[var12];((List)result.computeIfAbsent(factoryTypeName, (key) -> {return new ArrayList();})).add(factoryImplementationName.trim());}}}result.replaceAll((factoryType, implementations) -> {return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));});cache.put(classLoader, result);return result;} catch (IOException var14) {throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14);}}}

spring.factories在很多jar包中都有,也可以自己定义

 我们要导入的就是所有这些文件里对应的key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的(截图文件是org\springframework\boot\spring-boot-autoconfigure\2.4.13\spring-boot-autoconfigure-2.4.13.jar!\META-INF\spring.factories)

讲到这我们就得继续回到开头的getAutoConfigurationEntry那个方法里了,代码我再放这里一遍,省得往上翻了

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

过滤完之后SpringBoot原生的应该有23个

 我这里有自定义的,所以有74个

总结

SpringBoot通过@SpringBootApplication注解开启自动配置,利用AutoConfigurationImportSelector类动态加载spring.factories文件中指定的自动配置类,同时结合条件注解控制Bean的创建和配置类的激活,从而实现了自动装配。这种机制极大地简化了Spring应用程序的配置过程,提高了开发效率。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/147735.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

struct的精确用法

目录 我终于回来啦&#xff01; 1,可以创造根据结构体格式的成员或数组。 普通成员 数组成员 2,可以用指针遍历成员 3,使用typedef --------------------------------------------------------------------------------------------------------------------------------…

代码随想录Day 52|题目:101.孤岛的面积、102.沉没孤岛、103.水流问题、104.建造最大岛屿

提示&#xff1a;DDU&#xff0c;供自己复习使用。欢迎大家前来讨论~ 文章目录 图论part03题目一&#xff1a;101.孤岛的总面积解题思路DFS**BFS** 题目二&#xff1a;102. 沉没孤岛解题思路 题目三&#xff1a;103. 水流问题解题思路优化 题目四&#xff1a;104.建造最大岛屿…

[Linux]用户管理指令

开机/重启/登录/注销 进入xhsell 或者虚拟系统中, 右键桌面打开终端, 在终端执行命令, 重启或关机linux系统 建议使用普通账号登录, 如果权限不够时, 使用 su - 用户名 命令切换到超管, 然后再使用 logout命令退回到普通账号, logout 不能在图形界面的终端中使用 用户管理 Li…

Centos7.9 使用 Kubeadm 自动化部署 K8S 集群(一个脚本)

文章目录 一、环境准备1、硬件准备&#xff08;虚拟主机&#xff09;2、操作系统版本3、硬件配置4、网络 二、注意点1、主机命名格式2、网络插件 flannel 镜像拉取2.1、主机生成公私钥2.2、为啥有 Github 还用 Gitee2.3、将主机公钥添加到 Gitee2.3.1、复制主机上的公钥2.3.2、…

最佳植树距离 - 华为OD统一考试(E卷)

2024华为OD机试&#xff08;C卷D卷E卷&#xff09;最新题库【超值优惠】Java/Python/C合集 题目描述 按照环保公司要求&#xff0c;小明需要在沙化严重的地区进行植树防沙工作&#xff0c;初步目标是种植一条直线的树带。由于有些区域目前不适合种植树木&#xff0c;所以只能在…

电脑提示找不到msvcp110.dll怎么办?全方面详细解答

msvcp110.dll 是 Microsoft Visual C 2012 Redistributable Package 中的一个动态链接库文件。它是运行使用 Visual C 2012 开发的应用程序所必需的&#xff0c;包含了许多 C 标准库函数的实现。这些函数主要用于支持字符串处理、内存管理、输入输出流、异常处理等功能。 1.ms…

Clion使用vcpkg管理C/C++包

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、Clion安装vcpkg二、使用步骤1.切换到清单模式2.开始安装包 三、测试代码总结 前言 Linux上的库基本都可以通过apt或yum等包管理工具来在线安装包&#xff…

C语言深入理解指针(四)

目录 字符指针变量数组指针变量数组指针变量是什么数组指针变量怎么初始化 二维数组传参的本质函数指针变量函数指针变量的创建函数指针变量的使用代码typedef关键字 函数指针数组转移表 字符指针变量 字符指针在之前我们有提到过&#xff0c;&#xff08;字符&#xff09;&am…

NLP 文本分类核心问题

解决思路 分解为多个独立二分类任务将多标签分类转化为多分类问题更换 loss 直接由模型进行多标签分类 数据稀疏问题 标注更多数据&#xff0c;核心解决方案&#xff1a; 自己构造训练样本 数据增强&#xff0c;如使用 chatGPT 来构造数据更换模型 减少数据需求增加规则弥补…

MELON的难题- 华为OD统一考试(E卷)

2024华为OD机试&#xff08;C卷D卷&#xff09;最新题库【超值优惠】Java/Python/C合集 题目描述 MELON 有一堆精美的雨花石&#xff08;数量为 n&#xff0c;重量各异&#xff09;&#xff0c;准备送给 S和 W&#xff0c;MELON 希望送给俩人的雨花石重量是一致的。请你设计一…

爬虫 ----hook

目录 定义&#xff1a; 了解什么是hook? 举例 hook XHR请求 XMLHttpRequest 案例地址&#xff1a; Interceptors-拦截器 HOOK cookie操作 cookie 示范 常见的hook代码总结 1.Hook Cookie 2.Hook Header 3.Hook URL 4.Hook JSON.stringify 5.Hook JSON.parse 6.Ho…

Mac使用gradle编译springboot-2.7.x源码

1 开发环境&#xff1a; JDK8 ideaIU-2024.2.2 gradle-7.6.3 代理网络 2 下载springboot源码 代码仓库网址 git clone -b 2.7.x https://github.com/spring-projects/spring-boot.git3 安装gradle gradle下载网址 https://services.gradle.org/distributions/ 安装此文件指…

C语言 | Leetcode C语言题解之第415题字符串相加

题目&#xff1a; 题解&#xff1a; char* addStrings(char* num1, char* num2) {int i strlen(num1) - 1, j strlen(num2) - 1, add 0;char* ans (char*)malloc(sizeof(char) * (fmax(i, j) 3));int len 0;while (i > 0 || j > 0 || add ! 0) {int x i > 0 ?…

lsof可以查看当前系统中正在被使用的文件,包括动态库

lsof的英文是 list open files lsof直接回车&#xff0c;会显示很多&#xff0c;可以配合more命令查看 lsof | more -10 sudo lsof | more -20 lsof查看正在使用某个动态库的进程 lsof /lib/x86_64-linux-gnu/libc.so.6 lsof /usr/lib/x86_64-linux-gnu/libc.so.6 l…

如何优化苹果CMS 泛目录的缓存管理?

在使用苹果CMS进行内容管理时&#xff0c;缓存管理是提升网站性能的重要环节。随着技术的不断发展&#xff0c;泛目录插件的缓存机制也逐渐变得不再必要。&#xff08;maccmscn&#xff09;本文将探讨如何在不使用缓存的情况下&#xff0c;优化苹果CMS泛目录的性能&#xff0c;…

(学习记录)使用 STM32CubeMX——配置时钟(入门)

使用STM32CubeMX配置STM32F103C8T6时钟部分 选择芯片 ①&#xff1a;选择MCU型号 ①&#xff1a;这里使用英文输入法&#xff0c;输入你想要的芯片型号&#xff0c;我这里采用STM32F103C8T6 ②&#xff1a;这里能看到搜索后出来的芯片具体型号&#xff0c;选择匹配度最高的一个…

MySQL-排名函数ROW_NUMBER(),RANK(),DENSE_RANK()函数的异同

MySQL-排名函数ROW_NUMBER()&#xff0c;RANK()&#xff0c;DENSE_RANK()函数的异同 前言 假设有如下表结构与数据&#xff0c;class_id表示班级&#xff0c;需求&#xff1a;现在要按照班级分组&#xff0c;每个班级的学生进行年龄从小到大排序 一、ROW_NUMBER()函数 ROW_NUM…

Linux中的调度算法

nice值的范围有限&#xff0c;即为[-20, 19]&#xff0c;也就是40个数字&#xff0c;优先级为[60, 99]即一共40个优先级 目前谈论的Linux操作系统叫做分时操作系统&#xff0c;调度的时候主要强调公平&#xff0c;还有一种是实时操作系统&#xff0c;比如智能汽车里面必须装有这…

【面经】查找中常见的树数据结构

查找中常见的树数据结构 一、二叉排序&#xff08;搜索、查找&#xff09;树&#xff08;BST&#xff0c;Binary Search Tree&#xff09;&#xff08;1&#xff09;二叉排序树的查找、插入和删除过程&#xff08;2&#xff09;叉树排序树的缺陷&#xff08;3&#xff09;二叉排…

Spark原理及调优

spark官档 hints&#xff1a;https://spark.apache.org/docs/3.0.0/sql-ref-syntax-qry-select-hints.html调优参数&#xff1a;https://spark.apache.org/docs/latest/sql-performance-tuning.html#join-strategy-hints-for-sql-queries作者几乎把所有的RDD API查了个遍&…