SpringBoot源码解析(一)

SpringBoot自动装配原理

@SpringBootApplication注解

我们在使用SpringBoot时,通常使用的是@SpringBootApplication这个注解,比如:

而这个注解的定义为下图,可以发现这个注解上有另外三个注解:@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan,所以我们可以认为@SpringBootApplication是一个三合一注解;

所以我们也可以这么用,如果我们这么用就能自己控制要不要用@EnableAutoConfiguration这个注解,如果用就表示开启自动配置,如果不用就表示不开启自动配置,那开启和不开启自动配置到底该怎么理解呢?

SpringBoot的自动配置就是SpringBoot自动配置一些Bean,从而让开发人员在用SpringBoot时可以少去配置很多Bean,所以如果我们开启了自动配置,那最终Spring容器中就有SpringBoot帮我们配置的Bean,如果没有开启自动配置,那Spring容器中就没有这些Bean,就需要我们自己去配置。

@EnableAutoConfiguration

那我们来看看@EnableAutoConfiguration这个注解是如何工作的?先看注解源码定义:

其中非常核心的就是AutoConfigurationImportSelector,而AutoConfigurationImportSelector实现了DeferredImportSelector这个接口,Spring容器在启动时,会在解析完其他所有程序员定义的配置类之后,来调用AutoConfigurationImportSelector中的selectImports方法,然后把该方法返回的类名对应的类作为配置类进行解析。

该方法会利用SpringFactoriesLoader找到所有的META-INF/spring.factories文件中key为
EnableAutoConfiguration.class的value值,也就是众多自动配置类的类名。

拿到这些类名后会进行去重,去重完之后,就会看是否存在某些自动配置类需要排除,我们可以通过@EnableAutoConfiguration注解的exclude属性,或者spring.autoconfigure.exclude配置来指定一些自动配置类的名字,然后把它们从自动配置类集合中排除掉。

然后会继续利用ConfigurationClassFilter对自动配置类进行进一步筛选,ConfigurationClassFilter会利用AutoConfigurationMetadata进行筛选,而AutoConfigurationMetadata对象对应的是"METAINF/ spring-autoconfigure-metadata.properties"文件中的内容,这是一种加快SpringBoot启动速度的机制,默认是开启了的。不过要通过maven或gradle的方式引入springboot的依赖来使用才能看 到效果,因为这个文件的内容是在SpringBoot源码工程编译的时候自动生成出来的,当然我们也可以手动创建这个文件,以及这个文件的内容,自动生成的这个文件内容样例如下

内容格式为:自动配置类名.条件注解=条件

有了这个文件的内容,SpringBoot会在通过spring.facotries文件找到所有的自动配置类后,会把这个文件中的内容读出来,然后利用AutoConfigurationImportFilter对所有的自动配置类进行条件匹配,这里的条件判断,只会判断所需要的类是否存在。如果需要的类或者需要的Bean对应的类都不存在,那么肯定不符合条件了,对于像@ConditionalOnMissingBean这样的条件,在这一步是不会去判断的,最后条件匹配成功的自动配置类就会记录下来,并最终返回给Spring容器,继续进行其他条件的匹配。所以通过这个机制,使得Spring并不需要解析所有的自动配置类,从而提高了效率。

当然在这个过滤的过程中,如果日志级别等于trace级别,那么会把所有条件不匹配的自动配置类记录到日志中,如果日志框架配置了打印到控制台,那就会打印到控制台,比如:

在SpringBoot中,还有一个更加强大的统计自动配置类匹配结果的功能,就是可以配置
debug=true,只要开启了这个配置,那么Spring在解析每一个自动配置类时,就会将是否匹配的结
果进行记录,比如开启了debug=true,我们可以在控制台看到

可以看到这个匹配结果分别记录了:

1. 哪些自动配置类的条件是匹配的

2. 哪些自动配置类的条件是不匹配的,并且具体原因也会打印出来,比如是哪个类不存在

3. 哪些自动配置类是无条件的

这个功能的实现,是Spring解析具体的自动配置类上的各种条件注解的时候统计的,每解析一个条件注解,就会把结果记录在ConditionEvaluationReport对象中,当Spring容器启动完成后,会发布一个ContextRefreshedEvent事件,而SpringBoot提供了一个ConditionEvaluationReportLoggingListener会处理这个事件,接收到这个事件后就会把统计结果进
行打印。

自动配置类解析的大体流程为:

1. 读取spring.factories中的所有自动配置类

2. 看是否配置了需要排除的自动配置类,进行排除

3. 然后利用spring-autoconfigure-metadata.properties文件来过滤掉一些自动配置类(条件中指定的类不存在的自动配置类)

4. 解析过滤后自动配置类,判断自动配置类所有的条件注解,条件全部符合才会真正去解析自动配置类上的其他内 容,比如@Bean(也会进行条件判断)。

@SpringBootConfiguration

可以看到@SpringBootConfiguration就是对@Configuration类的简单包装,这个注解在之前的spring源码解析中已经解释的很清楚了。

@ComponentScan

componetScan注解想必也不陌生,在之前的spring源码解析中也已经解释的很清楚了。

条件注解原理

springboot中的条件注解

1. ConditionalOnBean:是否存在某个某类或某个名字的Bean
2. ConditionalOnMissingBean:是否缺失某个某类或某个名字的Bean
3. ConditionalOnSingleCandidate:是否符合指定类型的Bean只有一个
4. ConditionalOnClass:是否存在某个类
5. ConditionalOnMissingClass:是否缺失某个类
6. ConditionalOnExpression:指定的表达式返回的是true还是false
7. ConditionalOnWebApplication:当前应用是一个Web应用
8. ConditionalOnNotWebApplication:当前应用不是一个Web应用
9. ConditionalOnProperty:Environment中是否存在某个属性
10. ConditionalOnResource:指定的资源是否存在

当然我们也可以利用@Conditional来自定义条件注解。条件注解是可以写在类上和方法上的,如果某个条件注解写在了自动配置类上,那该自动配置类会不会生效就要看当前条件能不能符合,或者条件注解写在某个@Bean修饰的方法上,那这个Bean生不生效就看当前条件符不符合。

@Condional的原理和源码

从condition 的使用需求我们知道,这个是单条件满足的时候才实例化bean 和加入到spring 容器,而在spring中一个类的实例化必须要变成beanDefinition对象,而ConfigurationClassPostProcessor 是所有beanDefinition 对象的集散地,所有的beanDefinition 都会在这个类里面处理。那么我们要完成Condition 功能也必定在这个类里面,ConfigurationClassPostProcessor 类中的shouldSkip 方法就是做bean 过滤的。
 

我们可以发现,SpringBoot的自动配置,实际上就是SpringBoot的源码中预先写好了一些配置类,预先定义好了一些Bean,我们在用SpringBoot时,这些配置类就已经在我们项目的依赖中了,而这些自动配置类或自动配置Bean到底生不生效,就看具体所指定的条件了。

这个getMatchOutcome 是一个钩子方法,不同的注解调用的实现类不一样,这里看两个注解的实现

1、conditionalOnBean

Bean 存在时才掉用方法,这个其实很好理解,判断bean 是否存在其实就只要从BeanFactory 中找就行了,源码里面就是从BeanFactory 中找。

从BeanFactory 中获取对应的实例,如果有则匹配。

2、conditionalOnClass

当工程上下文中存在该类时才调用方法,实现原理就是通过Class.forName反射的方式,如果反射有异常则返回false,如果反射没异常返回true

Starter机制

Starter原理

那SpringBoot中的Starter和自动配置又有什么关系呢?

其实首先要明白一个Starter,就是一个Maven依赖,当我们在项目的pom.xml文件中添加某个Starter依赖时,其实就是简单的添加了很多其他的依赖,比如:

1、spring-boot-starter-web:引入了spring-boot-starter、spring-boot-starter-json、spring-boot-starter-tomcat等和Web开发相关的依赖包
2、spring-boot-starter-tomcat:引入了tomcat-embed-core、tomcat-embed-el、tomcat-embed-websocket等和Tomcat相关的依赖包

如果硬要把Starter机制和自动配置联系起来,那就是通过@ConditionalOnClass这个条件注解,因为这个条件注解的作用就是用来判断当前应用的依赖中是否存在某个类或某些类,比如:

上面代码中就用到了@ConditionalOnClass,用来判断项目中是否存在Servlet.class、
Tomcat.class、UpgradeProtocol.class这三个类,如果存在就满足当前条件,如果项目中引入了
spring-boot-starter-tomcat,那就有这三个类,然后机会将TomcateServletWebServerFactory假如到spring容器中,最终会调用getWebServer方法获取到web容器。

这个代码中就用到了@ConditionalOnMissingBean,意思是如果当前不存在 ServletWebServerFactory类型的Bean,那就符合条件,结合整体代码意思就是:如果用户自己没有定义ServletWebServerFactory类型的Bean,那代码中所定义的Bean就会生效;如果用户自己定义了ServletWebServerFactory类型的Bean,那代码中定义的Bean就不生效;所以这个注解是非常重要的,SpringBoot利用这个注解来决定到底用用户自己的Bean,还是用 SpringBoot自动配置的。

如果没有spring-boot-starter-tomcat那就可能没有这三个类(除非你自己单独引入了Tomcat相关的依赖)。所以这就做到了,如果我们在项目中要用Tomcat,那就依赖spring-boot-starter-web就够了,因为它默认依赖了spring-boot-starter-tomcat,从而依赖了Tomcat,从而Tomcat相关的Bean能生效。

而如果不想用Tomcat,那就得这么写:得把spring-boot-starter-tomcat给排除掉,再添加上spring-boot-starter-jetty的依赖,这样Tomcat的Bean就不会生效,Jetty的Bean就能生效,从而项目中用的就是Jetty。

自定义Starter

当公司里面需要把一些共用的api 封装成jar 包的时候,就可以尝试自定义启动器来做。自定义启动器用到的就是springboot中的SPI 原理,springboot 会去加载META-INF/spring.factories 配置文件,并加载EnableAutoConfiguration 为key的所有类。

利用这一点,我们定义一个工程也会有这个文件。
1、定义启动器核心工程,工程结构如下

spring.factories 配置内容

被springboot SPI 加载的类

这个RedisTemplate 实例就是我们封装的通用API,其他工程可以直接导入jar使用的。

2、自定义starter
我们还会定义一个没代码的工程,在这个工程里面没有任何代码,只有一个pom文件,Pom 里面就是对前面核心工程jar 包的导入,工程名定义为spring-boot-study-starter

3、自定义启动器使用
其实就只要在另外的springboot 工程pom 文件里面导入依赖就可以了,这个依赖就是自定义starter 那个工程的maven 坐标。

总结

SpringBoot启动时,最核心的也就是创建一个Spring容器,而创建Spring容器的过程中会注解做几件事情:

一、把SpringApplication.run(SpringBootExample.class)传入进来的MyApplication类做为配置类进行解析。
二、由于MyApplication类上定义了@SpringBootApplication,相当于定义了@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan注解。
三、所以SpringBoot会进一步解析这些注解@EnableAutoConfiguration,通过@import注解导入AutoConfigurationImportSelector这个配置类,因为它实现了DeferredImportSelector接口,所以Spring会在把其他配置类都解析完之后,在最后才解析AutoConfigurationImportSelector这个配置类(Spring Framework中的知识);

四、而AutoConfigurationImportSelector这个类的作用就是用来解析SpringBoot的自动配置类,那既然无法扫描到SpringBoot中的自动配置类,那怎么知道SpringBoot中有哪些自动配置类呢?默认情况下SpringBoot会提供一个spring.factories文件,并把所有自动配置类的名字记录在这个文件中,SPI 在springboot 中是去读取META-INF/spring.factories 目录的配置文件内容,把配置文件中的类加载到spring 容器中。如果你想把一个类加载到spring 容器中,也可以采用这种方式来做。把类配置到spring.factories 配置文件中即可。并且这件事也是发生在解析完用户的配置类之后的,那么我们总结一下,如果你想把一个类加载到spring 容器中管理有几种方式:

1、通过xml 的bean 标签;

2、通过加@Component 注解被@ComponentScan 扫描;

3、通过在spring.factories 配置该类前两者是加载本工程的bean,扫描本工程的bean,第三点可以加载第三方定义的jar 包中的bean,毕竟第三方jar 包的包名跟本工程包名可能不一样,所以前两个方式扫描不到。
四、@ComponentScan:扫描,扫描时会扫描到用户所定义的配置类,并解析用户的配置类,注意:扫描是扫描不到SpringBoot的自动配置的类,因为扫描的包路径不匹配,SpringBoot的包都是
org.springframework.boot.xxxx,用户都是自己的包路径。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

BES2600WM---HiLink RM56 EVK

0 Preface/Foreword 0.1 路径 OpenHarmony/device_soc_bestechnic - 码云 - 开源中国 https://github.com/Hi-LinkDuino/RM56 1 环境搭建 1.1 安装依赖工具 sudo apt-get install build-essential gcc g make zlib* libffi-dev e2fsprogs pkg-config flex bison perl bc ope…

C# 编程语言学习教程

C# 编程语言学习教程 目录 C# 简介 1.1 什么是 C#1.2 C# 的特点1.3 C# 的应用领域 环境搭建 2.1 安装 Visual Studio2.2 创建第一个 C# 项目 基础语法 3.1 数据类型3.2 控制结构3.3 数组与字符串 面向对象编程 4.1 类与对象4.2 继承与多态4.3 接口与抽象类 常用库与框架 5.1 .…

PAT甲级-1092 To Buy or Not to Buy

题目 题目大意 Eva想要买珠子,但是只能按串买。如果串上有她想要买的所有珠子,那么输出“Yes”,再输出需要额外买几个珠子。如果串上缺少她想要的珠子,那么输出“No”,并输出缺少的珠子个数。其中,s1是商店…

使用WebAssembly优化Web应用性能

💓 博客主页:瑕疵的CSDN主页 📝 Gitee主页:瑕疵的gitee主页 ⏩ 文章专栏:《热点资讯》 使用WebAssembly优化Web应用性能 引言 WebAssembly 简介 编译 WebAssembly 模块 使用 Emscripten 编译 C/C 代码 使用 Rust 编译…

【C语言】自定义类型(结构体、枚举、联合的详解)下

写在前面 书接上回:【C语言】自定义类型(结构体、枚举、联合的详解)上 在上中,不才独篇撰写了结构体的具体细节,本篇笔记主要是把剩下的自定义类型全部展示。 文章目录 写在前面二、位段2.1、位段的定义2.2、位段占用…

初始JavaEE篇 —— 文件操作与IO

找往期文章包括但不限于本期文章中不懂的知识点: 个人主页:我要学编程程(ಥ_ಥ)-CSDN博客 所属专栏:JavaEE 目录 文件介绍 Java标准库中提供操作文件的类 文件系统操作 File类的介绍 File类的使用 文件内容操作 二进制文件的读写操作…

CSP 2024 入门级第二轮 CSP-J 2024 复赛 第一题 扑克牌

一、题目阅读 [CSP-J 2024] 扑克牌 - 洛谷 二、题目解析 输入 去重 三、题目代码 #include <bits/stdc.h> using namespace std;int main() {map<string, bool> flag;int n, res 52;cin >> n;while (n--) {string s;cin >> s;if (!flag[s]) { // …

面试题:JVM(二)

1. 面试题 简述 Java 类加载机制?&#xff08;百度&#xff09; JVM类加载机制 &#xff08;滴滴&#xff09; JVM中类加载机制&#xff0c;类加载过程&#xff0c;什么是双亲委派模型&#xff1f; &#xff08;腾讯&#xff09; JVM的类加载机制是什么&#xff1f; &#x…

AUTOSAR CP MCAL微控制器抽象层介绍

AUTOSAR(Automotive Open System Architecture)即汽车开放系统架构,它将汽车电子控制单元(ECU)的软件底层做了一个标准的封装,使得开发者能够共用一套底层软件,并通过修改参数来匹配不同的硬件和应用层软件。AUTOSAR CP(Classic Platform)是AUTOSAR架构中的一个重要组…

ElastricSearch 原理以及简单实用(超级通俗)

提到非结构化数据的检索&#xff0c;常常用到 ElasticSearch&#xff0c;他是什么呢&#xff1f; ElasticSearch 是一个基于 Apache Lucene 的分布式搜索引擎&#xff0c;可以作为实时文档存储系统&#xff0c;且文档的每一个内容都可以被检索&#xff0c;能够处理 PB 级别的结…

在线培训知识库:企业培训的新篇章

在当今快节奏的商业环境中&#xff0c;员工培训已成为企业保持竞争力的关键。在线培训知识库作为一种新兴的培训工具&#xff0c;正逐渐成为企业培训体系的核心。它不仅能够提供灵活的学习方式&#xff0c;还能确保培训内容的及时更新和高效传播。本文将探讨在线培训知识库的重…

简单的kafkaredis学习之kafka

简单的kafka&redis学习整理之kafka 1. kafka 1.1 什么是消息队列 在学习Kafka之前我们先来看一下什么是消息队列&#xff0c;消息队列(Message Queue)&#xff1a;可以简称为MQ 例如&#xff1a;Java中的Queue队列&#xff0c;也可以认为是一个消息队列 消息队列&#x…

【连续多届检索,ACM出版】第四届大数据、人工智能与风险管理国际学术会议 (ICBAR 2024,11月15-17)--冬季主会场

第四届大数据、人工智能与风险管理国际学术会议 (ICBAR 2024)--冬季主会场 2024 4th International Conference on Big Data, Artificial Intelligence and Risk Management 会议官网&#xff1a;www.icbar.net 2024 4th International Conference on Big Data, Artificial I…

集成框架 -- 自定义二方包 starter

自定义starter 二方包 My-thread-pool-startermy-thread-pool-starter 整体架构 测试 MyTestAppApplication测试工程 my-test-app 结构测试项目的 pom.xml 二方包 My-thread-pool-starter POM 文件 <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi&…

如何看待AI技术的应用前景?

文章目录 如何看待AI技术的应用前景引言AI技术的现状1. AI的定义与分类2. 当前AI技术的应用领域 AI技术的应用前景1. 经济效益2. 社会影响3. 技术进步 AI技术应用面临的挑战1. 数据隐私与安全2. 可解释性与信任3. 技能短缺与就业影响 AI技术的未来发展方向1. 人工智能的伦理与法…

华为擎云(银河麒麟V10+麒麟9000C CPU)电脑总是弹出“选择新密钥环的密码”(20241030)

症状如下图&#xff1a; 网络上一般的解决方法都是安装 seahorse &#xff0c;例如这篇文章:银河麒麟系统清除桌面密钥环 不知道为什么&#xff0c;这对我的这台电脑并不可行。 麒麟系统用的是 Gnome 桌面。这个“密钥环”的弹出框是由 gnome-keyring-daemon 弹出的。这是一个…

[SICTF Round4] PWN

这PWN题似乎是给我出的&#xff0c;4个一血1个2血。密码又过于简单。逆向太难了又不大会。 Stack fengshui main可以溢出覆盖rbpret所以它每一步都需要移栈。 可用的ROP里没有pop rdi,在4004c0里有错位的01 5d c3 &#xff1a;add DWORD PTR [rbp-0x3d], ebx 并且有对应的p…

yaml文件编写

Kubernetes 支持YAML和JSON格式管理资源 JSON 格式:主要用于 api 接口之间消息的传递 YAML 格式;用于配置和管理,YAML是一种简洁的非标记性语言,内容格式人性化容易读懂 一&#xff0c;yaml语法格式 1.1 基本语法规则 使用空格进行缩进&#xff08;不使用制表符&#xff0…

2025浙江省考报名流程详细教程

2025年浙江省考报名马上就要开始了&#xff0c;有想要参加浙江省考的同学&#xff0c;可以提前看一下报名流程&#xff0c;和报名照要求。 报名时间&#xff1a;11月6日9时一11月11日17时 南核时间&#xff1a;11月6日9时一11月13日17时 缴费时间&#xff1a;11月14日9时一11月…

江协科技STM32学习- P30 FlyMCU串口下载STLink Utility

&#x1f680;write in front&#x1f680; &#x1f50e;大家好&#xff0c;我是黄桃罐头&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流 &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd;​…