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

news2024/11/15 13:50:02

我想你也在真实面试中被问过无数次这个问题了,我也是,但是不管你怎么搜,都只有那几篇八股文的答案,你问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.coloradmin.cn/o/2155306.html

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

相关文章

读取t x t文件生成exce

读取t x t文件生成excel package com.moka.api.custom.core.controller; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermode…

NLP 文本分类核心问题

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

ReKep——李飞飞团队提出的让机器人具备空间智能:基于视觉语言模型GPT-4o和关系关键点约束

前言 由于工厂、车厂的任务需求场景非常明确&#xff0c;加之自今年年初以来&#xff0c;我司在机器人这个方向的持续大力度投入(包括南京、长沙两地机器人开发团队的先后组建)&#xff0c;使得近期我司七月接到了不少来自车厂/工厂的订单&#xff0c;比如其中的三个例子&…

草莓团队创造了o1 - Building OpenAI o1 (Extended Cut) 观后笔记

美妙的事物往往需要世界去创造&#xff0c;商业希望大模型越来越快给出回答。或许花费几个月几年的时间持续思考&#xff0c;大模型能够解决更复杂的问题&#xff0c;而不只是回答42 刚发现凌晨OpenAI发布了一个22多分钟的采访&#xff0c;将构建出O1的整个团队拉到一个小屋子&…

基于单片机红外感应智能卫生间系统仿真

文章目录 前言资料获取设计介绍功能介绍设计程序具体实现截图设计获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师&#xff0c;一名热衷于单片机技术探索与分享的博主、专注于 精通51/STM32/MSP430/AVR等单片机设计 主要对…

基于微信小程序的智慧物业管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码 精品专栏&#xff1a;Java精选实战项目…

设置VsCode搜索时排除文件,文件列表中隐藏文件

按照《VsCode gdb gdbserver远程调试C程序》中介绍的方法&#xff0c;配置好VsCode后&#xff0c;打开一个C/C工程&#xff0c;发现左侧的面板会显示编译时生成的中间文件&#xff08;比如.d和.o文件&#xff09;。我们可以通过设置隐藏掉一些我们不需要打开的文件以简洁面板…

Class path contains multiple SLF4J bindings.

最近由于要改kafka成datahub&#xff0c;于是在pom文件上引入了 <dependency><groupId>com.aliyun.datahub</groupId><artifactId>aliyun-sdk-datahub</artifactId><version>2.25.1</version> </dependency> 然后让我去测试…

C语言中if else组合

一 bool变量与“零值”进行比较 bool 变量与“零值”进行比较的 if 语句怎么写&#xff1f; bool bTestFlag FALSE;//想想为什么一般初始化为 FALSE 比较好&#xff1f; A), if(bTestFlag 0); if(bTestFlag 1); B), if(bTestFlag TRUE); if(bTestFlag FLASE); C), if(b…

WPF DataGrid 动态修改某一个单元格的样式

WPF DataGrid 动态修改某一个单元格的样式 <DataGrid Name"main_datagrid_display" Width"1267" Height"193" Grid.Column"1"ItemsSource"{Binding DataGridModels}"><DataGrid.Columns><!--ElementStyle 设…

libreoffice word转pdf

一、准备一个word文件 运行&#xff1a; cd /root libreoffice --headless --convert-to pdf --outdir /root/output doc1.docx 发现中文乱码&#xff1a; 此时我们需要给linux 上添加中文字体&#xff1a; centos7 添加中文字体 再次运行正常&#xff1a; libreoffice --h…

61. 旋转链表【 力扣(LeetCode) 】

零、原题链接 61. 旋转链表 一、题目描述 给你一个链表的头节点 head &#xff0c;旋转链表&#xff0c;将链表每个节点向右移动 k 个位置。 二、测试用例 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], k 2 输出&#xff1a;[4,5,1,2,3]示例 2&#xff1a; 输入…

828华为云征文 | 在Huawei Cloud EulerOS系统中安装Docker的详细步骤与常见问题解决

前言 Docker是一种轻量级的容器技术&#xff0c;广泛用于应用程序的开发、部署和运维。在华为云的欧拉&#xff08;Huawei Cloud EulerOS&#xff09;系统上安装和运行Docker&#xff0c;虽然与CentOS有相似之处&#xff0c;但在具体实现过程中&#xff0c;可能会遇到一些系统…

【ArcGIS微课1000例】0123:数据库中要素类批量转为shapefile

除了ArcGIS之外的其他GIS平台,想要打开ArcGIS数据库,可能无法直接打开,为了便于使用shp,建议直接将数据库中要素类批量转为shapefile。 文章目录 一、连接至数据库二、要素批量转shp一、连接至数据库 打开ArcMap,或者打开ArcCatalog,找到数据库连接,如下图: 数据库为个…

Maven的详细解读和配置

目录 一、Maven 1.1 引言 1.2 介绍 1.3 下载安装 1.3.1 解压 1.3.2 配置环境变量 1.3.3 测试 1.4 仓库[了解] 1.5 Maven配置 1.5.1 修改仓库位置 1.5.2 设置镜像 二、IDEA - MAVEN 2.1 idea关联maven 2.2 为新项目设置 2.2 创建java项目[重点] 2.3 java项目结构…

连续数组问题

目录 一题目&#xff1a; 二思路&#xff1a; 三代码&#xff1a; 一题目&#xff1a; leetcode链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 二思路&#xff1a; 思路&#xff1a;前缀和&#xff08;第二种&#xff09;化0为-1hash&#xff1a; 这样可以把…

C++入门12——详解多态1

目录 1.多态的概念 2.多态的定义与实现 2.1多态构成的条件 2.2虚函数 2.3虚函数的重写 虚函数重写的两个例外&#xff1a; 1.协变(基类与派生类虚函数返回值类型不同) 2.析构函数的重写(基类与派生类析构函数的名字不同) 2.4 C11 override 和 final 2.5 重载、覆盖(…

机器学习周报(9.16-9.22)-Pytorch学习(四)

文章目录 摘要Abstract1 完整模型训练套路及模型验证套路1.1 模型及训练代码1.2 利用GPU训练模型1.3 完整的模型验证&#xff08;测试&#xff09;套路 2 CNN 实现mnist手写数字识别2.1 网络模型搭建2.2 测试训练好的模型 总结 摘要 通过学习CNN模型的训练及验证套路&#xff…

【服务器入门】Linux系统基础知识

【服务器入门】Linux系统基础知识 远程登录与文件传输基础命令与文本编辑vi/vim使用shell脚本基本命令1、目录操作2、文件创建与删改3、文件连接与查看 参考 目前超算使用的系统以Linux系统为主&#xff0c;肯定需要了解一些相关知识。本博客就以本人运行WRF模型所需&#xff0…

LeetCode[中等] 155. 最小栈

设计一个支持 push &#xff0c;pop &#xff0c;top 操作&#xff0c;并能在常数时间内检索到最小元素的栈。 实现 MinStack 类: MinStack() 初始化堆栈对象。void push(int val) 将元素val推入堆栈。void pop() 删除堆栈顶部的元素。int top() 获取堆栈顶部的元素。int get…