SpringBoot 自动装配原理

零、前言

Spring简直是java企业级应用开发人员的春天,我们可以通过Spring提供的ioc容器,避免硬编码带来的程序过度耦合。但是,启动一个Spring应用程序也绝非易事,他需要大量且繁琐的xml配置,开发人员压根不能全身心的投入到业务中去。
因此,SpringBoot诞生了,虽然本质上还是属于Spring,但是SpringBoot的优势在于以下两个特点:

  1. 约定大于配置

SpringBoot定义了项目的基本骨架,例如各个环境的配置文件统一放到resource中,使用active来启用其中一个。配置文件默认为application.properties,或者yaml、yml都可以。

  1. 自动装配

以前在Spring使用到某个组件的时候,需要在xml中对配置好各个属性,之后被Spring扫描后注入进容器。
而有了SpringBoot后,我们仅仅需要引入一个starter,就可以直接使用该组件,如此方便、快捷,得益于自动装配机制。

一、定义

什么是SpringBoot的自动装配?

SpringBoot 自动装配,英文是 Auto-Configuration,是指在Spring Boot启动时,通过一系列机制和注解,将第三方组件或配置类加载到Spring容器中,并生成相应的Bean对象供使用。这一过程极大地简化了开发工作,提高了效率,为 SpringBoot 框架的 “开箱即用”提供了基础支撑;
具体来说,SpringBoot的自动装配主要依赖以下几个关键点

  1. 注解:SpringBoot提供了多个注解来实现自动装配,例如@SpringBootApplication@EnableAutoConfiguration@ComponentScan等。这些注解帮助SpringBoot识别并加载需要自动配置的类。
  2. 条件化配置:SpringBoot利用条件化配置(@Conditional注解)来决定是否进行某些配置。例如,如果类路径下存在某个特定的jar包,则自动配置相应的功能。
  3. 元数据文件:SpringBoot会在启动时扫描META-INF/spring.factories 文件,从中获取需要进行自动装配的类,并执行这些类中的配置操作。
  4. 场景启动器:SpringBoot还引入了场景启动器(如WebMvcAutoConfiguration),根据项目中添加的依赖自动配置相应的功能。
  5. 约定大于配置:SpringBoot遵循“约定大于配置”的原则,通过默认配置减少开发者手动编写配置的必要性。例如,在没有明确配置的情况下,SpringBoot会自动配置数据库连接、视图解析器等。
  6. SPI机制:自动装配与Spring的SPI(Service Provider Interface)机制相似,都是通过反射机制动态加载和实例化组件。更多SPI讲解:深入理解 Java SPI - 概念、原理、应用

二、启动流程

SpringBoot启动流程的简化版代码

public static void run(Class<?> primaryClass) {//1.创建一个 ApplicationContext 实例,即我们常说的IoC容器ApplicationContext context = createApplicationContext();// 2.将主类(primaryClass)注册到IoC容器中(简单但很重要的一步)loadSourceCLass(context, primaryClass);// 3.递归加载并处理所有的配置类processConfigurationClasses(context);// 4.实例化所有的单例Bean(Singleton Bean)instantiateSingletonBeans(context);// 5.如果时web应用,则启动web服务器(如Tomcat)startWebServer(context);
}

图解:
启动流程.png
详解第三步核心流程:
加载配置类流程.png
图解:
加载配置流程图.png

三、原理剖析

一切都从注解 @SpringBootApplication 说起:

@SpringBootApplication
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}
}

@SpringBootApplication

**@SpringBootApplication **注解又是一个组合注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),@Filter(type = FilterType.CUSTOM,classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {...}

其中**@Target**、** @Retention**、@Documented与**@Inherited**是元注解,即对注解的注解;
还包含了@SpringBootConfiguration与@EnableAutoConfiguration
以下注解都将省略这些元注解

@SpringBootConfiguration

@Configuration
public @interface SpringBootConfiguration {
}@Component
public @interface Configuration {@AliasFor(annotation = Component.class)String value() default "";
}

可以看到,@SpringBootConfiguration 注解本质上是一个 @Configuration 注解,表明该类是一个配置类。
@Configuration 又被 @Component 注解修饰,代表任何加了**@Configuration** 注解的配置类,都会被注入进Spring容器中。

@EnableAutoConfiguration

该注解开启了自动配置的功能

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {...}

本身又包含了以下两个注解
@AutoConfigurationPackage@Import(AutoConfigurationImportSelector.class)

@AutoConfigurationPackage

以前我们直接使用Spring的时候,需要在xml中的 context:component-scan 中定义好base-package,那么 Spring 在启动的时候,就会扫描该包下及其子包下被@Controller、@Service与@Component标注的类,并将这些类注入到容器中。
@AutoConfigurationPackage 则会将被注解标注的类,即主配置类,将主配置类所在的包当作base-package,而不用我们自己去手动配置了。
这也就是为什么我们需要将主配置类放在项目的最外层目录中的原因。
那么容器是怎么知道主配置当前所在的包呢?
我们注意到,@AutoConfigurationPackage 中使用到了@Import注解
@Import注解会直接向容器中注入指定的组件
引入了AutoConfigurationPackages类中内部类 Registrar

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {@Overridepublic void registerBeanDefinitions(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {register(registry, new PackageImport(metadata).getPackageName());}@Overridepublic Set<Object> determineImports(AnnotationMetadata metadata) {return Collections.singleton(new PackageImport(metadata));}
}

而getName将会返回主配置类所在的包路径;这样,容器就知道了主配置类所在的包,之后就会扫描该包及其子包。

@Import(AutoConfigurationImportSelector.class)

该注解又引入了AutoConfigurationImportSelector类,而AutoConfigurationImportSelector中有一个可以获取候选配置的方法,即getCandidateConfigurations

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;
}protected Class<?> getSpringFactoriesLoaderFactoryClass() {return EnableAutoConfiguration.class;
}

其中核心方法SpringFactoriesLoader.loadFactoryNames,第一个参数是EnableAutoConfiguration.class

loadFactoryNames方法
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {String factoryClassName = factoryClass.getName();return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {MultiValueMap<String, String> result = cache.get(classLoader);if (result != null) {return result;}try {Enumeration<URL> urls = (classLoader != null ?classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));result = new LinkedMultiValueMap<>();while (urls.hasMoreElements()) {URL url = urls.nextElement();UrlResource resource = new UrlResource(url);Properties properties = PropertiesLoaderUtils.loadProperties(resource);for (Map.Entry<?, ?> entry : properties.entrySet()) {String factoryClassName = ((String) entry.getKey()).trim();for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {result.add(factoryClassName, factoryName.trim());}}}cache.put(classLoader, result);return result;}catch (IOException ex) {throw new IllegalArgumentException("Unable to load factories from location [" +FACTORIES_RESOURCE_LOCATION + "]", ex);}
}

可以看得出,loadSpringFactorie方法,会从META-INF/spring.factories文件中读取配置,将其封装为Properties对象,将每个key作为返回的map的key,将key对应的配置集合作为该map的value。
而loadFactoryNames则是取出key为EnableAutoConfiguration.class的配置集合
我们查看META-INF/spring.factories的内容(完整路径:\org\springframework\boot\spring-boot\2.7.17\spring-boot-2.7.17.jar!\META-INF\spring.factories)
spring.factories.png
可以看到,EnableAutoConfiguration对应的value,则是我们在开发中经常用到的组件,比如Rabbit、Elasticsearch与Redis等中间件。
到这里,我们可以知道getCandidateConfigurations方法会从META-INF/spring.factories中获取各个组件的自动配置类的全限定名。
图解
selector.png
总结一下
总结一下.png

参考

SpringBoot的自动装配原理、自定义starter与spi机制,一网打尽
目前讲的最透彻的SpringBoot自动配置

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

怎么查询大数据信用评分?

相信在了解大数据信用评分的时候&#xff0c;不少人都因为大数据信用评分在申贷的时候遭受到过挫折&#xff0c;因为大数据信用已经被很多银行和金融机构作为风险控制的重要依据使用&#xff0c;其中的大数据信用评分&#xff0c;能直观的感知到用户的信用情况。那如何查询大数…

AI智能助手商业系统软件源码(IMYAI智能助手) AI换脸/智能体GPTs应用/AI视频生成/AI绘画/文档分析/GPT-4o模型支持

人工智能技术的发展日新月异&#xff0c;从深度学习到自然语言处理&#xff0c;再到计算机视觉等领域&#xff0c;不断推动着各行各业的变革。在应用层面&#xff0c;人工智能已深入到内容创作领域&#xff0c;为创作者提供了前所未有的便利和可能性。这些技术的发展潜力巨大&a…

C++ 80行 极简扫雷

一共5346个字符&#xff0c;MinGW编译通过&#xff08;强烈不建议写这种代码&#xff01;&#xff01;&#xff01;&#xff09; 压行规则&#xff1a;一行不超过80个字符 代码&#xff1a; #include<windows.h> #include<stdio.h> #include<time.h> #def…

关于Unity四种合批技术详解

文章目录 一.静态合批(StaticBatching)1.启用静态合批2.举例说明3.静态合批的限制4.静态合批的优点缺点5.动态指定物品合批 二.动态合批(Dynamic Batching)1.启用动态合批2.合批规则3.举例说明4.使用限制 三.GPU Instancing1.启用GPU Instancing2.启用限制3.举例说明 四.SRP Ba…

uniapp h5本地预览pdf教程 (含白屏|跨域解决方案)

第一步 下载pdf.js 很多pdf.js版本在真机ios环境都会白屏 经测试后2.5.207版本比较稳定&#xff0c;Android和IOS环境PDF文件都能加载成功 下载地址 https://github.com/mozilla/pdf.js/releases/tag/v2.5.207https://github.com/mozilla/pdf.js/releases/tag/v2.5.207第二步 解…

C++(week15): C++提高:(五)Redis数据库

文章目录 零、Redis的安装、API1.redis、hiredis、redis-plus-plus安装2.HiRedis的API 一、Redis数据库的基本概念1.关系型数据库与非关系型数据库的区别2.非关系型数据库的分离3.Redis的概念4.Redis的特性5.Redis的优点 二、Redis常用命令三、Redis的五大数据类型及其命令1.st…

c++初阶-----STL---list

作者前言 &#x1f382; ✨✨✨✨✨✨&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f382; ​&#x1f382; 作者介绍&#xff1a; &#x1f382;&#x1f382; &#x1f382; &#x1f389;&#x1f389;&#x1f389…

Datawhale AI夏令营第四期 魔搭-AIGC方向 task01笔记

目录 赛题内容 可图Kolors-LoRA风格故事挑战赛 baseline要点讲解(请配合Datawhale速通教程食用) Step1 设置算例及比赛账号的报名和授权 Step2 进行赛事报名并创建PAI实例 Step3 执行baseline Step4 进行赛题提交 微调结果上传魔搭 lora 调参参数介绍及 SD 的基础知识点…

ICETEK-DM6437-AICOM——CPU定时器及直流电机控制中断控制

一、设计目的&#xff1a; 1.1 CPU定时器程序设计&#xff1b; 1.2 2直流电机程序设计&#xff1b; 1.3 外中断。 二、设计原理&#xff1a; 2.1 定时器的控制&#xff1a; 在DM6437&#xff08;是一种数字信号处理器&#xff0c;DSP&#xff09;上使用其内部定时器和中断来…

JESD204B/C协议学习笔记

JESD204B基础概念 204B包含传输层&#xff0c;链路层&#xff0c;物理层。 应用层是对 JESD204B 进行配置的接口&#xff0c;在标准协议中是不含此层&#xff0c;只是为了便于理解&#xff0c;添加的一个层。 协议层指工程中生成的IP核JESD204B&#xff0c;负责处理输入的用户…

Java网络编程——简单的 API 调用

Get请求 - 无参数 上一章我们学习了网络的基本概念&#xff0c;我们知道 URL 能输入到浏览器地址栏中被打开&#xff0c; 那么能不能在程序中发送请求&#xff0c;获取结果呢&#xff1f; 例如“中国科学技术大学”的网站&#xff08;https://www.ustc.edu.cn/&#xff09;&a…

探索设计模式:观察者模式

探索设计模式&#xff1a;观察者模式 &#x1f9d0;观察者模式简介:gem:核心概念:rainbow:观察者模式的优点:truck:实现步骤1. 定义主题接口2. 实现观察者接口3. 具体主题实现4. 具体观察者实现5. 调用 :triangular_flag_on_post:总结 在实际开发过程中&#xff0c;设计模式的作…

【Hot100】LeetCode—124. 二叉树中的最大路径和

1- 思路 使用递归 dfs 实现① 递归思路&#xff1a;每次递归返回值为 &#xff0c; root.valMath.max(left,right) 从 左右孩子中挑选一个大的。② 递归公式&#xff1a;定义 sum&#xff0c;sum root.val left right 2- 实现 ⭐124. 二叉树中的最大路径和——题解思路 cl…

【矩阵对角线求和】求一个3*3矩阵对角线元素之和

求一个3*3矩阵对角线元素之和&#xff0c;使用C语言实现 具体代码&#xff1a; #include<stdio.h>int main(){float a[3][3],sum0;printf("请输入3x3矩阵的元素&#xff08;按行输入&#xff09;&#xff1a;\n");for(int i0;i<3;i){for(int j0;j<3;j)…

Java、PHP、Node 操作 MySQL 数据库常用方法

一、Java 操作 MySQL 数据库 1、Java 连接 MySQL 数据库 1. 使用 JDBC 驱动程序连接 使用这种方式&#xff0c;首先需要导入 MySQL 的 JDBC 驱动程序依赖&#xff0c;然后通过 Class.forName() 方法加载驱动程序类。其创建连接的过程相对直接&#xff0c;只需提供准确的数据库…

Tech Talk: SSD架构与功能模块详解

在之前的系列文章中&#xff0c;我们介绍了固态硬盘的系列知识&#xff0c;包括闪存的介质、原理&#xff0c;以及作为SSD大脑的控制器设计&#xff0c;本文将详细介绍SSD架构以及功能模块。 SSD架构简介 ◎SSD架构示意图 如上图所示&#xff0c;典型的SSD架构包括主机接口、SS…

点亮童梦思考之光,神秘伙伴震撼登场!

本文由 ChatMoney团队出品 介绍说明 咱们来聊聊“十万个为什么”机器人&#xff0c;这对小朋友来说&#xff0c;好处可多了去啦&#xff01; 小朋友们天生好奇&#xff0c;满脑子都是问号。 这个机器人就像个啥都懂的知识达人&#xff0c;不管他们问啥&#xff0c;都能给出答…

JVM知识总结(类加载器)

文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 类加载器 Bootstrap引导类加载器 引导类加载器也被称为启动类加载…

麦田物语第二十天

系列文章目录 麦田物语第二十天 文章目录 系列文章目录一、构建地图信息系统二、生成地图数据 一、构建地图信息系统 我们上一节课已经做好了鼠标的显示&#xff0c;这节课需要构建地图的一些信息&#xff0c;例如&#xff1a;可挖坑&#xff0c;可丢弃物品等地区。我们点击地…

c语言学习,isdigit()函数分析

1&#xff1a;isdigit() 函数说明&#xff1a; 检查参数c&#xff0c;是否为0到9阿拉伯数字 2&#xff1a;函数原型&#xff1a; int isdigit(int c) 3&#xff1a;函数参数&#xff1a; 参数c&#xff0c;为检测整数 4&#xff1a;返回值&#xff1a; 参数c是阿拉伯码字符&…