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,用户都是自己的包路径。

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

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

相关文章

WPF+MVVM案例实战与特效(二十四)- 粒子字体效果实现

文章目录 1、案例效果2、案例实现1、文件创建2.代码实现3、界面与功能代码3、总结1、案例效果 提示:这里可以添加本文要记录的大概内容: 2、案例实现 1、文件创建 打开 Wpf_Examples 项目,在 Views 文件夹下创建窗体界面 ParticleWindow.xaml,在 Models 文件夹下创建粒子…

js中怎么把excel和pdf文件转换成图片打包下载

index.html <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>文件转图片工具</title><!-- 本…

盘点 2024 十大免费/开源 WAF

WAF 是 Web Application Firewall 的缩写&#xff0c;也被称为 Web 应用防火墙。区别于传统防火墙&#xff0c;WAF 工作在应用层&#xff0c;对基于 HTTP/HTTPS 协议的 Web 系统有着更好的防护效果&#xff0c;使其免于受到黑客的攻击。 近几年经济增速开始放缓&#xff0c;科…

蓝牙资讯|苹果AirPods Pro 2推出听力测试、助听器和听力保护等功能

苹果推送iOS 18.1 系统版本更新&#xff0c;AirPods Pro 2 用户也在 iOS 18.1 中获得了强大的新功能。 运行固件 7B19 的 AirPods Pro 2 用户&#xff0c;搭配 iOS 18.1 系统的 iPhone&#xff0c;将获得三项强大的听力健康功能&#xff1a;听力测试、助听器和听力保护。 听力…

如何检查雷池社区版 WAF 是否安装成功?

容器运行状态检查&#xff1a; 使用命令行检查&#xff1a;打开终端&#xff0c;连接到安装雷池的服务器。运行 docker ps 命令&#xff0c;查看是否有与雷池相关的容器正在运行。 如果能看到类似 safeline-mgt、safeline-tengine 等相关容器&#xff0c;并且状态为 Up&#x…

【AI开源项目】Botpress - 开源智能聊天机器人平台及其部署方案

文章目录 Botpress 概述Botpress 的定位 Botpress 的主要特点1. OpenAI 集成2. 易于使用3. 定制和扩展性4. 多平台支持5. 集成和扩展 API6. 活跃的社区和详尽的文档 部署方案集成集成开发集成部署机器人示例开发工具代理本地开发先决条件从源代码构建 Botpress 如何解决常见问题…

Rust 力扣 - 1652. 拆炸弹

文章目录 题目描述题解思路题解代码题目链接 题目描述 题解思路 我们只需要遍历长度长度为k的窗口&#xff0c;然后把窗口内数字之和填充到结果数组中的对应位置即可 题解代码 impl Solution {pub fn decrypt(code: Vec<i32>, k: i32) -> Vec<i32> {let n c…

HTMLCSS:打造酷炫下载安装模拟按钮

效果演示 这段代码通过HTML和CSS创建了一个具有交互效果的下载按钮&#xff0c;当复选框被选中时&#xff0c;会触发一系列动画和样式变化&#xff0c;模拟了一个下载和安装的过程&#xff0c;包括圆形的动画、文本的显示和隐藏等。 HTML <div class"container&quo…

【C++、数据结构】哈希表——散列表(一)(概念/总结)

「前言」 &#x1f308;个人主页&#xff1a; 代码探秘者 &#x1f308;C语言专栏&#xff1a;C语言 &#x1f308;C专栏&#xff1a; C / STL使用以及模拟实现 &#x1f308;数据结构专栏&#xff1a; 数据结构 / 十大排序算法 &#x1f308;Linux专栏&#xff1a; Linux系统编…

WindowsDocker安装到D盘,C盘太占用空间了。

Windows安装 Docker Desktop的时候,默认位置是安装在C盘,使用Docker下载的镜像文件也是保存在C盘,如果对Docker使用评率比较高的小伙伴,可能C盘空间,会被耗尽,有没有一种办法可以将Docker安装到其它磁盘,同时Docker的数据文件也保存在其他磁盘呢? 答案是有的,我们可以…

mac|安装redis及RedisDesk可视化软件

一、安装 通过Homebrew安装 brew install redis 在安装过程可以得到以下信息&#xff1a; 1、启动redis或重新登陆redis brew services start redis 如果只想在前端运行&#xff0c;而不是在后端&#xff0c;则使用以下命令 /opt/homebrew/opt/redis/bin/redis-server /opt…

程序中怎样用最简单方法实现写excel文档

很多开发语言都能找到excel文档读写的库&#xff0c;但是在资源极其受限的环境下开发&#xff0c;引入这些库会带来兼容性问题。因为一个小功能引入一堆库&#xff0c;我始终觉得划不来。看到有项目引用的jar包有一百多个&#xff0c;看着头麻&#xff0c;根本搞不清谁依赖谁。…

重读《人月神话》(12)-未雨绸缪(Plan to Throw One Away)

对程序员而言&#xff0c;一个不容忽视的事实是&#xff1a;任何系统都将经历变更&#xff0c;最初精心设计的软件也可能因不断的修补而变得面目全非。无论设计多么完美&#xff0c;随着时间推移&#xff0c;系统难免陷入混乱&#xff0c;只是程度和速度有所不同。因此&#xf…

(附项目源码)python开发语言,基于python Web的高校毕业论文管理系统 51,计算机毕设程序开发+文案(LW+PPT)

摘 要 随着信息化技术的迅速发展&#xff0c;人类信息化文明的到来&#xff0c;为人类的日常生活以及日常生产活动提供了非常大的便利&#xff0c;有效地解决了很多曾经无法解决的问题。本次基于python Web的高校毕业论文管理系统的开发是针对我国传统的高校毕业论文管理模式沟…

计算机网络:网络层 —— 网络地址转换 NAT

文章目录 网络地址转换 NAT 概述最基本的 NAT 方法NAT 转换表的作用 网络地址与端口号转换 NAPTNAT 和 NAPT 的缺陷 网络地址转换 NAT 概述 尽管因特网采用了无分类编址方法来减缓 IPv4 地址空间耗尽的速度&#xff0c;但由于因特网用户数量的急剧增长&#xff0c;特别是大量小…

C++进阶:unordered_map和unordered_set的使用

目录 一.unordered_set系列 1.1unordered_set类的介绍 1.2unordered_set与set的差异 二.unordered_map的系列 三.unordered_multimap/unordered_multiset 一.unordered_set系列 1.1unordered_set类的介绍 • unordered_set的声明如下&#xff0c;Key就是unordered_set底层…

【6G 需求与定义】ITU(国际电联)对全球6G标准的愿景

博主未授权任何人或组织机构转载博主任何原创文章&#xff0c;感谢各位对原创的支持&#xff01; 博主链接 本人就职于国际知名终端厂商&#xff0c;负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作&#xff0c;目前牵头6G技术研究。 博客内容主要围绕…

java:题目:用Java实现简单的自取取款操作

import java.util.Scanner; public class ATM {public static void main(String[] args){//自主取款主类Scanner scnew Scanner(System.in);System.out.println("请输入账户号码&#xff1a;");String BankAccoutsrsc.nextLine();/BankAccout3 newBankAccoutnew Bank…

Windows 部署非安装版Redis

1.下载Redis https://github.com/microsoftarchive/redis/releases 选择下载zip包&#xff0c;如Redis-x64-3.0.504.zip&#xff0c;并解压 2.启动非安装版redis服务 进入到redis目录&#xff0c;打开cmd 执行命令 redis-server.exe redis.windows.conf 3.登录redis客户端…