【Spring】深究SpringBoot自动装配原理
文章目录
- 前言
- 1、main入口
- 2、@SpringBootApplication
- 3、@EnableAutoConfiguration
- 4、AutoConfigurationImportSelector
- 4.1、selectImports()
- 4.2、getAutoConfigurationEntry()
- 4.3、getCandidateConfigurations()
- 4.4、loadFactoryNames()
- 5、META-INF/spring.factories
- 6、总结
前言
早期的Spring
项目需要添加需要配置繁琐的xml
,比如MVC
、事务、数据库连接等繁琐的配置。Spring Boot
的出现就无需这些繁琐的配置,因为Spring Boot
基于约定大于配置的理念,在项目启动时候,将约定的配置类自动装配到IOC
容器里。
这些都因为Spring Boot
有自动装配的特性。
接下来将会逐步从源码跟踪进去,一步步掀开自动装配的面纱。
1、main入口
在SpringBoot项目的启动类中,注解SpringBootApplication
是必须需要添加的,既然是从这里启动的,那么自动装配的操作应该也是在启动的时候去执行的吧,我们一步步挖进去一探究竟。
@SpringBootApplication
public class SpringbootInitApplication {public static void main(String[] args) {SpringApplication.run(SpringbootInitApplication.class, args);}
}
2、@SpringBootApplication
点进来@SpringBootApplication
注解之后会发现,这玩意头顶怎么挂着这么多注解的。不要着急,与Bean注入也只有三个相关,而自动装配的核心注解也是只有一个:
@SpringBootConfiguration
:继承自Spring的@Configuration
注解,作用也大致相同,支持在入口处通过@Bean
等注解手动配置一下 Bean 加入到容器中;@EnableAutoConfiguration
:顾名思义,这玩意就是用来自动装配的,都写在人家脸上了,接下来主要的介绍核心也是该注解;@ComponentScan
:告诉Spring需要扫描哪些包或类,如果不设值的话默认扫描@ComponentScan注解所在类的同级类和同级目录下的所有类,这也是为什么放置启动类位置有要求的原因。
@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 {// 省略该注解内属性和方法// ......
}
3、@EnableAutoConfiguration
点进来@EnableAutoConfiguration
注解,去掉那些有的没的注解,可以初步发现与自动装配有关的应该是有两个,分别是注解@AutoConfigurationPackage
和导入的类AutoConfigurationImportSelector
。
点进去注解@AutoConfigurationPackage
发现里面没有什么有用的信息,其作用是将添加该注解的类所在的package 作为自动装配 package 进行管理,感兴趣的小伙伴可以自行点进去查看。
AutoConfigurationImportSelector
作为被导入的类,也是实现自动装配的核心类之一,接下来将会点进去查看其细节。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {// 省略该注解内属性和方法// ......
}
4、AutoConfigurationImportSelector
4.1、selectImports()
在AutoConfigurationImportSelector
类中,自动装配核心方法为selectImports()
,在其文档中是这么介绍该方法的:
Select and return the names of which class(es) should be imported based on the AnnotationMetadata of the importing @Configuration class.
Returns: the class names, or an empty array if none
根据
@Configuration
中的AnnotationMetadata
,选择并返回应该被导入的类的名称。返回:类名,如果不存在则返回空数组
可以看到从这里便已经获取被设置需要自动装配的类的信息了,从源码中可以看到在selectImports()
中的核心方法应该是getAutoConfigurationEntry()
。
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {// 省略类中成员属性...@Overridepublic String[] selectImports(AnnotationMetadata annotationMetadata) {if (!isEnabled(annotationMetadata)) {return NO_IMPORTS;}AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());}// 省略类中其余方法...
}
4.2、getAutoConfigurationEntry()
getAutoConfigurationEntry()
方法同样处于AutoConfigurationImportSelector
类中,在刚方法中主要返回的是已经配置好的配置数组,该配置数组被包装在类中,其中AutoConfigurationImportSelector.AutoConfigurationEntry
的介绍如下:
Create an entry with the configurations that were contributed and their exclusions.
Params: configurations – the configurations that should be imported exclusions – the exclusions that were applied to the original list
使用所提供的配置及其排除项创建一个条目。
参数:configurations ——应该导入的配置 exclusions – 应用于原始列表的排除项
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);
}
4.3、getCandidateConfigurations()
方法getCandidateConfigurations()
同样处于AutoConfigurationImportSelector
类中,该方法主要用于获取应考虑的自动装配类名称,而获取候选项的方法为其中的loadFactoryNames()
方法。
Return the auto-configuration class names that should be considered. By default this method will load candidates using ImportCandidates with getSpringFactoriesLoaderFactoryClass().
返回应考虑的自动配置类名称。默认情况下,此方法将使用ImportCandidates和getSpringFactoriesLoaderFactoryClass()加载候选项。
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;
}
4.4、loadFactoryNames()
挖到这里其实就差不多到头了,再往下就不太礼貌了。
在这里干的活在官方给出的文档中都说的很简单明了了:
Load the fully qualified class names of factory implementations of the given type from “META-INF/spring.factories”, using the given class loader.
As of Spring Framework 5.3, if a particular implementation class name is discovered more than once for the given factory type, duplicates will be ignored.
使用给定的类加载器,从"META-INF/spring.factories"加载给定类型的工厂实现的完全限定类名。
从 Spring Framework 5.3 开始,如果针对给定工厂类型多次发现特定实现类名称,则将忽略重复项。
从介绍中就可以看到所谓的自动装配,就是将META-INF/spring.factories
中写明白的内容给加载出来,同时连上面提及到的排除项都写明白在这里了,而源码也是对官方文档进行直接翻译了,猜测是出于封装性和代码简洁度考虑,开发人员将核心逻辑封装成了私有方法loadSpringFactories()
:
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {ClassLoader classLoaderToUse = classLoader;if (classLoaderToUse == null) {classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();}String factoryTypeName = factoryType.getName();// 核心流程return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {Map<String, List<String>> result = cache.get(classLoader);if (result != null) {return result;}result = new HashMap<>();try {// 获取资源,常量FACTORIES_RESOURCE_LOCATION的值为META-INF/spring.factoriesEnumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);// 逐一处理加载到的资源while (urls.hasMoreElements()) {URL url = urls.nextElement();UrlResource resource = new UrlResource(url);// 装配属性Properties properties = PropertiesLoaderUtils.loadProperties(resource);for (Map.Entry<?, ?> entry : properties.entrySet()) {String factoryTypeName = ((String) entry.getKey()).trim();String[] factoryImplementationNames =StringUtils.commaDelimitedListToStringArray((String) entry.getValue());for (String factoryImplementationName : factoryImplementationNames) {result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>()).add(factoryImplementationName.trim());}}}// Replace all lists with unmodifiable lists containing unique elementsresult.replaceAll((factoryType, implementations) -> implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));cache.put(classLoader, result);}catch (IOException ex) {throw new IllegalArgumentException("Unable to load factories from location [" +FACTORIES_RESOURCE_LOCATION + "]", ex);}return result;
}
5、META-INF/spring.factories
spring-boot-autoconfigure.jar 包中的 META-INF/spring.factories 里面默认配置了很多aoto-configuration,如下:
以WebMvcAutoConfiguration
为例:
package org.springframework.boot.autoconfigure.web.servlet;@Configuration
@ConditionalOnWebApplication
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurerAdapter.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {@Bean@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {return new OrderedHiddenHttpMethodFilter();}@Bean@ConditionalOnMissingBean(HttpPutFormContentFilter.class)@ConditionalOnProperty(prefix = "spring.mvc.formcontent.putfilter", name = "enabled", matchIfMissing = true)public OrderedHttpPutFormContentFilter httpPutFormContentFilter() {return new OrderedHttpPutFormContentFilter();}......etc
}
这里有一个地方不知道大家有没有注意到,有大部分的注解前面都有Conditional
字样的,这其实也是SpringBoot
的强大之处之一,其使用了 Spring 4 框架的新特性:@Conditional
注释,此注解使得只有在特定条件满足时才启用一些配置。这也 Spring Boot “智能” 的关键注解。Conditional大家族如下:
注解 | 描述 |
---|---|
@ConditionalOnBean | 当容器中存在指定的Bean时生效 |
@ConditionalOnClass | 当类路径中存在指定的类时生效 |
@ConditionalOnExpression | 通过SpEL表达式指定条件,满足条件时生效 |
@ConditionalOnMissingBean | 当容器中不存在指定的Bean时生效 |
@ConditionalOnMissingClass | 当类路径中不存在指定的类时生效 |
@ConditionalOnNotWebApplication | 当应用程序不是Web应用时生效 |
@ConditionalOnResource | 当指定的资源存在于类路径中时生效 |
@ConditionalOnWebApplication | 当应用程序是Web应用时生效 |
6、总结
SpringBoot自动配置原理如下:
@EnableAutoConfiguration
注解导入AutoConfigurationImportSelector
类;- 执行
selectImports()
方法调用SpringFactoriesLoader.loadFactoryNames()
扫描所有jar
下面的对应的META-INF/spring.factories
文件; - 限定为
@EnableAutoConfiguration
对应的value
,将这些装配条件的装配到IOC
容器中。
自动装配简单来说就是自动将第三方的组件的 bean
装载到 IOC
容器内,不需要再去写 bean
相关的配置,符合约定大于配置理念。SpringBoot 基于约定大于配置的理念,配置如果没有额外的配置的话,就给按照默认的配置使用约定的默认值,按照约定配置到 IOC
容器中,无需开发人员手动添加配置,加快开发效率。
同时,对于自己开发SDK时,也可利用 SpringBoot 自动装配原理,编写自己的 META-INF/spring.factories
文件,从而将某些特定需求的类生成 Bean
放入容器中。
相关文章:

【Spring】深究SpringBoot自动装配原理
文章目录 前言1、main入口2、SpringBootApplication3、EnableAutoConfiguration4、AutoConfigurationImportSelector4.1、selectImports()4.2、getAutoConfigurationEntry()4.3、getCandidateConfigurations()4.4、loadFactoryNames() 5、META-INF/spring.factories6、总结 前言…...

阿里云负载均衡SLB网络型NLB负载均衡架构性能详解
阿里云网络型负载均衡NLB是阿里云推出的新一代四层负载均衡,支持超高性能和自动弹性能力,单实例可以达到1亿并发连接,帮您轻松应对高并发业务。网络型负载均衡NLB具有超强性能、自动弹性伸缩、高可用、TCPSSL卸载、多场景流量分发和丰富的高级…...
JavaScript学习 -- SM4算法应用实例
SM4算法,也被称为国密算法,是中国公布的一种高效且安全的对称加密算法。在JavaScript中,我们可以通过使用CryptoJS库来实现SM4算法的加密和解密。本篇博客将为您介绍如何在JavaScript中使用SM4算法,并提供一个实际的案例。 首先&…...

【JVM】什么是双亲委派机制
文章目录 1、类加载机制2、双亲委派模型2.1、介绍2.2、为什么需要双亲委派2.3、源码解析 3、破坏双亲委派3.1、介绍3.2、破坏实现3.3、破坏双亲委派的例子 4、线程上下文类加载器 1、类加载机制 类加载阶段分为加载、连接、初始化三个阶段,而加载阶段需要通过类的全…...

网络安全 Day24-select高级用法和多表连接
select高级用法和多表连接 1. select 多子句单表高级实践1.1 select 多子句高级语法1.2 聚合函数1.3 group by 实践1.4 having 筛选1.5 order by 排序1.6 limit 2. 多表连接 1. select 多子句单表高级实践 1.1 select 多子句高级语法 where 和 having 区别是后者是分组后进行…...

JUC并发编程之volatile详解
目录 1. volatile 1.1 volatile关键字的作用 1.1.1 变量可见性 1.1.2 禁止指令重排序 1.2 volatile可见性案例 1.3 volatile非原子性案例 1.4 volatile 禁止重排序 1.5 volatile 日常使用场景 送书活动 1. volatile 在并发编程中,多线程操作共享的变量时&a…...

swing布局详解
1. 布局管理器接口 (1)说明 布局管理器接口为LayoutManager和LayoutManager2,LayoutManager2是LayoutManager的子类。 (2)常用方法 方法描述LayoutManageraddLayoutComponent(String name, Component comp) removeL…...
el-table某一列嵌套使用el-popover,使用click触发,导致页面下拉框组件无法触发弹框关闭(解决办法)
在弹框触发的方法里加上document.body.click() 即可 尝试了很多其他的方法都没用,只有这个解决了 完整代码: <el-select change"sourceChange" clearable ><el-optionv-for"option in list1":key"option.code":…...

正泰电力携手图扑:VR 变电站事故追忆反演
VR(Virtual Reality,虚拟现实)技术作为近年来快速发展的一项新技术,具有广泛的应用前景,支持融合人工智能、机器学习、大数据等技术,实现更加智能化、个性化的应用。在电力能源领域,VR 技术在高性能计算机和专有设备支…...
报错 -bash: wget: command not found
1、报错 -bash: wget: command not found 可以重装 wget 工具: 卸载 wget 工具 yum remove wget下载 wget 工具 yum -y install wget最后尝试 wget “url” 又OK了,一般是原来的wget初始化有文件损坏造成的。...
HashMap扩容和Redis中Dict 扩容
扩容时机: Hash Map:要在某个临界点进行扩容处理,该临界点就是HashMap中元素的数量在数值上等于threshold(table数组长度*加载因子) Dict: 当每次新增键值对的时 , 会检测 负载因子(LoadFactor) , 判断以…...

【Redis】内存数据库Redis进阶(Redis持久化)
目录 分布式缓存 Redis 四大问题Redis 持久化RDB (Redis DataBase)RDB执行时机RDB启动方式——save指令save指令相关配置save指令工作原理save配置自动执行 RDB启动方式——bgsave指令bgsave指令相关配置bgsave指令工作原理 RDB三种启动方式对比RDB特殊启动形式RDB优点与缺点 A…...

在PHP8中检测数据类型-PHP8知识详解
在PHP 8中,可以使用多种方法来检测数据类型。以下是常用的四种方法:使用 gettype() 函数、使用 is_* 系列函数、使用 get_debug_type() 函数、使用 get_class() 函数。 一、使用 gettype() 函数 gettype() 函数返回给定变量的数据类型。例如:…...

amoeba实现MySQL读写分离
amoeba实现MySQL读写分离 准备环境:主机A和主机B作主从配置,IP地址为192.168.131.129和192.168.131.130,主机C作为中间件,也就是作为代理服务器,IP地址为192.168.131.136。三台服务器操作系统为RHEL6.4 x86_64,为…...

angr学习-入门篇
前言: 资源链接:GitHub - jakespringer/angr_ctf(题库仓库,里面有个讲解angr的PPT,里面有官方的题解很详细) GitHub - Hustcw/Angr_Tutorial_For_CTF: angr tutorial for ctf 安装: 关于angr…...

基于java SpringBoot和HTML的博客系统
随着网络技术渗透到社会生活的各个方面,传统的交流方式也面临着变化。互联网是一个非常重要的方向。基于Web技术的网络考试系统可以在全球范围内使用互联网,可以在本地或异地进行通信,大大提高了通信和交换的灵活性。在当今高速发展的互联网时…...

动态sql以及常用的标签
什么是动态sql: 指根据不同的条件生成不同的sql 搭建环境: 建表: create table blog( id varchar(50) not null comment 博客id, title varchar(100) not null comment 博客标题, author varchar(30) not null comment 博客作者, create_ti…...

DID以及社交网络中的ZKP
1. 引言 本文关键术语为: Decentralized Identity (DID,去中心化身份) or self-sovereign identity (SSI,自治身份) :是一个基于开放标准的框架,使用自主、独立的标识符和可验证证书,实现可信的数据交换。…...

基于SWAT-MODFLOW地表水与地下水耦合
耦合模型被应用到很多科学和工程领域来改善模型的性能、效率和结果,SWAT作为一个地表水模型可以较好的模拟主要的水文过程,包括地表径流、降水、蒸发、风速、温度、渗流、侧向径流等,但是对于地下水部分的模拟相对粗糙,考虑到SWAT…...

2023拒绝内卷!两年转行网络安全真实看法!
我目前转行网络安全两年,没啥天分,全靠努力,基本能够得上中级的水平了。看到大家对转行网络安全挺感兴趣,也有挺多争议,想把我的建议和经验告诉大家。 有很多人觉得网络安全已经饱和了,现在选择这个工作&a…...

业务系统对接大模型的基础方案:架构设计与关键步骤
业务系统对接大模型:架构设计与关键步骤 在当今数字化转型的浪潮中,大语言模型(LLM)已成为企业提升业务效率和创新能力的关键技术之一。将大模型集成到业务系统中,不仅可以优化用户体验,还能为业务决策提供…...
<6>-MySQL表的增删查改
目录 一,create(创建表) 二,retrieve(查询表) 1,select列 2,where条件 三,update(更新表) 四,delete(删除表…...
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...

【入坑系列】TiDB 强制索引在不同库下不生效问题
文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...
LLM基础1_语言模型如何处理文本
基于GitHub项目:https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken:OpenAI开发的专业"分词器" torch:Facebook开发的强力计算引擎,相当于超级计算器 理解词嵌入:给词语画"…...
是否存在路径(FIFOBB算法)
题目描述 一个具有 n 个顶点e条边的无向图,该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序,确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数,分别表示n 和 e 的值(1…...

均衡后的SNRSINR
本文主要摘自参考文献中的前两篇,相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程,其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt 根发送天线, n r n_r nr 根接收天线的 MIMO 系…...

HashMap中的put方法执行流程(流程图)
1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中,其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下: 初始判断与哈希计算: 首先,putVal 方法会检查当前的 table(也就…...

【7色560页】职场可视化逻辑图高级数据分析PPT模版
7种色调职场工作汇报PPT,橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版:职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...
C++.OpenGL (20/64)混合(Blending)
混合(Blending) 透明效果核心原理 #mermaid-svg-SWG0UzVfJms7Sm3e {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-icon{fill:#552222;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-text{fill…...