【Spring Boot 源码学习】BootstrapRegistry 详解

《Spring Boot 源码学习系列》

在这里插入图片描述

BootstrapRegistry 详解

  • 一、引言
  • 二、往期内容
  • 三、主要内容
    • 3.1 源码初识
    • 3.2 register 方法
    • 3.3 registerIfAbsent 方法
    • 3.4 isRegistered 方法
    • 3.5 getRegisteredInstanceSupplier 方法
    • 3.6 addCloseListener 方法
    • 3.7 InstanceSupplier 内部接口类
      • 3.7.1 get 方法
      • 3.7.2 getScope 默认方法
      • 3.7.3 withScope 默认方法
      • 3.7.4 of 静态方法
      • 3.7.5 from 静态方法
    • 3.8 Scope 内部枚举类
  • 四、总结

一、引言

前面的博文《BootstrapRegistryInitializer 详解》,Huazie 带大家一起详细分析了 Spring Boot 启动时加载并初始化 BootstrapRegistryInitializer 及其相关的类的逻辑。其中有个 BootstrapRegistry 接口只是简单提及,本篇就详细分析一下 BootstrapRegistry 接口,这对于我们后续理解 《BootstrapRegistry 初始化器实现》的内容至关重要。

在这里插入图片描述

二、往期内容

在开始本篇的内容介绍之前,我们先来看看往期的系列文章【有需要的朋友,欢迎关注系列专栏】:

Spring Boot 源码学习
Spring Boot 项目介绍
Spring Boot 核心运行原理介绍
【Spring Boot 源码学习】@EnableAutoConfiguration 注解
【Spring Boot 源码学习】@SpringBootApplication 注解
【Spring Boot 源码学习】走近 AutoConfigurationImportSelector
【Spring Boot 源码学习】自动装配流程源码解析(上)
【Spring Boot 源码学习】自动装配流程源码解析(下)
【Spring Boot 源码学习】深入 FilteringSpringBootCondition
【Spring Boot 源码学习】OnClassCondition 详解
【Spring Boot 源码学习】OnBeanCondition 详解
【Spring Boot 源码学习】OnWebApplicationCondition 详解
【Spring Boot 源码学习】@Conditional 条件注解
【Spring Boot 源码学习】HttpEncodingAutoConfiguration 详解
【Spring Boot 源码学习】RedisAutoConfiguration 详解
【Spring Boot 源码学习】JedisConnectionConfiguration 详解
【Spring Boot 源码学习】初识 SpringApplication
【Spring Boot 源码学习】Banner 信息打印流程
【Spring Boot 源码学习】自定义 Banner 信息打印
【Spring Boot 源码学习】BootstrapRegistryInitializer 详解
【Spring Boot 源码学习】ApplicationContextInitializer 详解
【Spring Boot 源码学习】ApplicationListener 详解
【Spring Boot 源码学习】SpringApplication 的定制化介绍

三、主要内容

注意: 以下涉及 Spring Boot 源码 均来自版本 2.7.9,其他版本有所出入,可自行查看源码。

在 《BootstrapRegistryInitializer 详解》 的 3.1 小节,我们对 BootstrapRegistry 进行了初步的介绍:它是一个用于存储和共享对象的注册表,这些对象在 ApplicationContext 准备好之前就可能已经被创建并需要被共享。

3.1 源码初识

首先让我们来看看 BootstrapRegistry 的源码:

public interface BootstrapRegistry {<T> void register(Class<T> type, InstanceSupplier<T> instanceSupplier);<T> void registerIfAbsent(Class<T> type, InstanceSupplier<T> instanceSupplier);<T> boolean isRegistered(Class<T> type);<T> InstanceSupplier<T> getRegisteredInstanceSupplier(Class<T> type);void addCloseListener(ApplicationListener<BootstrapContextClosedEvent> listener);@FunctionalInterfaceinterface InstanceSupplier<T> {T get(BootstrapContext context);default Scope getScope() {return Scope.SINGLETON;}default InstanceSupplier<T> withScope(Scope scope) {// 。。。}static <T> InstanceSupplier<T> of(T instance) {return (registry) -> instance;}static <T> InstanceSupplier<T> from(Supplier<T> supplier) {return (registry) -> (supplier != null) ? supplier.get() : null;}}enum Scope {SINGLETON,PROTOTYPE}}

它包含了 5 个方法,1个内部接口类,1个内部枚举类,下面我们一一来介绍下:

3.2 register 方法

register 方法,包含两个参数:

  • Class<T> type :实例类型
  • InstanceSupplier<T> instanceSupplier :实例供应者

该方法用于将特定类型注册到注册表中。如果指定的类型已经被注册,并且尚未作为单例获取,那么它将被替换。

3.3 registerIfAbsent 方法

registerIfAbsent 方法,包含两个参数:

  • Class<T> type :实例类型
  • InstanceSupplier<T> instanceSupplier :实例供应者

如果尚未存在特定类型的注册,则向注册表中注册该类型。

3.4 isRegistered 方法

isRegistered 方法,只有一个参数:

  • Class<T> type :实例类型

该方法用于返回给定类型是否已注册。如果给定类型已经注册,则返回 true,否则,返回 false

3.5 getRegisteredInstanceSupplier 方法

getRegisteredInstanceSupplier 方法,也只有一个参数:

  • Class<T> type :实例类型

该方法返回给定类型的任何现有的 BootstrapRegistry.InstanceSupplier

3.6 addCloseListener 方法

addCloseListener 方法,只有一个参数:

  • ApplicationListener<BootstrapContextClosedEvent> listener :待添加的监听器

该方法用于添加一个 ApplicationListener,当 BootstrapContext 关闭并且 ApplicationContext 已经准备就绪时,该监听器将与 BootstrapContextClosedEvent 一起被调用。

3.7 InstanceSupplier 内部接口类

InstanceSupplier 内部接口类是用于提供实际实例的供应者。

它定义了一个 1 个普通方法,2 个默认方法,2 个静态方法。

知识拓展:
Java 8 开始,支持在接口中定义默认方法和静态方法。

  • 默认方法(Default Method)允许你在接口中添加一个有默认实现的非抽象方法。这使得接口可以更加灵活地扩展,而不需要破坏与现有代码的兼容性。默认方法使用关键字 default 进行声明,并提供了具体的实现。
  • 静态方法(Static Method)允许你在接口中定义一个静态方法,该方法可以在不创建接口实例的情况下调用。静态方法使用关键字 static 进行声明,并可以直接通过接口名来调用。

3.7.1 get 方法

get 方法,只包含一个参数:

  • BootstrapContext context :BootstrapContext 是一个用于获取其他引导实例的上下文

该方法是工厂方法,用于在需要时创建实例,后续我们在讲解 DefaultBootstrapContext 时也会涉及。

3.7.2 getScope 默认方法

getScope 默认方法,用于返回提供的实例的作用域;如果该方法没有被重写,则默认返回 Scope.SINGLETON

3.7.3 withScope 默认方法

default InstanceSupplier<T> withScope(Scope scope) {Assert.notNull(scope, "Scope must not be null");InstanceSupplier<T> parent = this;return new InstanceSupplier<T>() {@Overridepublic T get(BootstrapContext context) {return parent.get(context);}@Overridepublic Scope getScope() {return scope;}};
}

通过阅读上述代码可知,该方法根据其参数 scope ,返回一个指定作用域的新的 BootstrapRegistry.InstanceSupplier 的匿名对象,该匿名对象重写了 getgetScope 方法。这里使用匿名对象的好处就是可以在不定义新类的情况下快速地创建一个具有特定行为的对象。

细心的读者们,可能发现了匿名对象的 get 方法中,使用了 withScope 方法中定义的变量 parent,它被用来存储当前对象的引用 this

那么这里的 parent ,能不能直接替换成 this 呢?

显然是不可以的,this 关键字用在匿名内部类中,指代的是该匿名内部类本身,而不是外部对象。而匿名对象这里的重写的 get 方法,实际上需要调用 withScope 方法所在的对象的 get 方法来实现功能。如果这里用 this,实际上就是自己调自己,一直无限递归调用,最终导致栈溢出错误。

3.7.4 of 静态方法

该静态方法是一个工厂方法,用于为给定实例创建一个BootstrapRegistry.InstanceSupplier

return (registry) -> instance;

这里采用了 Java 8Lambda 表达式,也相当于如下的写法:

return new InstanceSupplier<T>() {@Overridepublic T get(BootstrapContext registry) {return instance;}
};

3.7.5 from 静态方法

该静态方法也是一个工厂方法,用于通过一个 Supplier 创建BootstrapRegistry.InstanceSupplier

return (registry) -> (supplier != null) ? supplier.get() : null;

这里也是用了 Java 8Lambda 表达式,相当于如下的写法:

return new InstanceSupplier<T>() {@Overridepublic T get(BootstrapContext registry) {return (supplier != null) ? supplier.get() : null;}
};

知识点:SupplierJava 8 开始引入,作为 java.util.function 包的一部分,它与 Lambda 表达式一起被引入,以支持函数式编程范式。该接口是为了简化无参数方法的表示,特别是在需要延迟执行或创建对象时。Supplier 接口只有一个抽象方法 get(),它不接受任何参数,但返回一个通用类型的值。

3.8 Scope 内部枚举类

Scope 表示一个实例的作用域,它只包含两个枚举变量,分别是:

  • SINGLETON :单例实例。InstanceSupplier 只会被调用一次,并且每次调用都会返回相同的实例。
  • PROTOTYPE :原型实例。InstanceSupplier 将在需要实例时被调用。

四、总结

本篇 Huazie 带大家通读了 BootstrapRegistry 的相关源码,这些内容对于后面的源码学习至关重要。

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

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

相关文章

使用Go的并发模型

Golang提供了强大的并发支持&#xff0c;可以轻松编写高效的并发程序。以下是一些使用Go并发模型的技巧&#xff1a; Goroutine&#xff1a;通过使用goroutine&#xff0c;您可以并发地执行函数或方法&#xff0c;从而充分利用多核处理器的能力。Channel&#xff1a;使用chann…

故障诊断 | 一文解决,GRU门控循环单元故障诊断(Matlab)

文章目录 效果一览文章概述专栏介绍模型描述源码设计参考资料效果一览 文章概述 故障诊断 | 一文解决,GRU门控循环单元故障诊断(Matlab) 专栏介绍 订阅【故障诊断】专栏,不定期更新机器学习和深度学习在故障诊断中的应用;订阅

基于spring boot实现邮箱发送和邮箱验证

目录 一、邮箱发送实现1. 开通邮箱服务2. 添加邮箱依赖3.添加配置4.添加邮箱通用类5. 测试类 二、邮箱验证实现1.添加依赖2. 添加配置3.添加controller4. 测试 项目地址: https://gitee.com/nssnail/springboot-email 一、邮箱发送实现 1. 开通邮箱服务 使用qq邮箱、163邮箱都…

1月无代码资讯 | 两项低代码无代码行业报告相继重磅发布;GitHub Copilot Chat全面开放使用

栏目导读&#xff1a;无代码资讯栏目从全球视角出发&#xff0c;带您了解无代码相关最新资讯。 TOP3 大事件 1、ResearchAndMarkets.com "低代码无代码开发平台市场—— 2018-2028 年全球行业规模、份额、趋势、机遇及预测"报告发布 据雅虎财经近日资讯显示&#xf…

网络层 IP协议(1)

前置知识 主机:配有IP地址,但是不进行路由控制的设备 路由器:既配置了IP地址,又能进行路由控制的设备 节点:主机和路由器的总称 IP协议主要完成的任务就是 地址管理和路由选择 地址管理:使用一套地址体系,将网络设备的地址描述出来 路由选择:一个数据报如何从源地址到目的地址 …

【LVGL源码移植】

LVGL源码移植 ■ LVGL源码移植一&#xff1a;下载LVGL源码二&#xff1a;修改LVGL文件夹1: 将这5个文件&#xff0c;复制到一个新的文件夹2: 简化文件&#xff0c;减少内存消耗&#xff08;去除不必要的文件&#xff09;3: 为了规范化&#xff0c;我们将下列文件进行重命名 三&…

git使用方法(简易版)

一、git使用过程 1.注册git账号&#xff0c;并新建一个仓库&#xff1b; http://t.csdnimg.cn/ePcsx可以参考链接 2.在电脑文件夹中&#xff0c;右键选择 Git Bash Here,输入git init&#xff08;初始化仓库&#xff09;&#xff1b; git init - 初始化仓库。 Git 使用 git …

【Java万花筒】驶入未来:利用Java库构建智能自动驾驶与车联网系统

加速汽车智能化&#xff1a;解析自动驾驶与车联网的Java库应用技巧 前言 随着自动驾驶和车联网技术的发展&#xff0c;开发人员需要强大的工具和资源来构建智能交通系统。本文将介绍几个重要的Java库&#xff0c;包括Autoware、Apollo、OpenXC、CarSync和V2XLib&#xff0c;它…

webassembly003 MINISIT mnist/convert-h5-to-ggml.py

数据结构 # Convert MNIS h5 transformer model to ggml format # # Load the (state_dict) saved model using PyTorch # Iterate over all variables and write them to a binary file. # # For each variable, write the following: # - Number of dimensions (int) # …

张维迎《博弈与社会》多重均衡与制度和文化(1)多重均衡问题

什么是多重均衡 我曾经在课堂上做过这样一个实验&#xff1a;随机选择男女两位同学参加一个选数字的游戏。游戏的基本规则为&#xff1a;每一个同学随机地从1到10十个数字中任意选择5个。如果两人选择的数字没有任何重复的话&#xff0c;则每人可以得到50元&#xff1b;如果两人…

npm install 安装依赖,为什么有时候会修改项目 package-lock.json,怎么解决?

前端开发时经常会接手一个别人创建的项目已经上线了&#xff0c;后期让我来修复缺陷&#xff0c;或者子新增功能。 上来就使用npm install 安装项目依赖&#xff0c;一看package-lock.json文件被自动修改了&#xff0c;可是我也没有修改package.json文件内容啊&#xff0c;不管…

深入了解协议栈内核源码

三次握手内核源码 深入理解 Linux 的 TCP 三次握手_tcp_v4_conn_request-CSDN博客 socket.c 内核态函数入口 三次状态变化 创建socket入口 ping Breakpoint 7, SyS_socket (family2, type3, protocol1) at net/socket.c:1325 1325 SYSCALL_DEFINE3(socket, int, famil…

Google Gemini Pro 国内版

Google Gemini Pro 国内版&#xff1a;【直达链接】 Google Gemini Pro 国内版 能力分类基准测试描述更高分数更好Gemini UltraGPT-4通用MMLU57个主题&#xff08;包括STEM、人文等&#xff09;的问题表示是90.0%86.4%&#xff08;5-shot, 报告&#xff09;推理Big-Bench Hard…

微信小程序(二十九)交互提示-界面加载框和提示框

注释很详细&#xff0c;直接上代码 上一篇 新增内容&#xff1a; 1.showLoading加载框示范 2.showToast提示框示范 源码&#xff1a; index.wxml <!-- 列表渲染基础写法&#xff0c;不明白的看上一篇 --> <view class"students"><view class"it…

音视频数字化(音乐CD)

上篇文章【音视频数字化(音频数字化)】我们聊了音频数字化原理,其中谈到了音乐CD,结尾也提到了一个小问题:“CD音质是最高吗?为什么?”不知道大家是怎么理解的。 其实CD质量只是“无损”存储,但是数字化标准只是“44.1kHz,16bit”,因此相对于现在,音质不能说最高。 …

故障诊断 | 一文解决,BP神经网络的故障诊断(Matlab)

文章目录 效果一览文章概述专栏介绍模型描述源码设计参考资料效果一览 文章概述 故障诊断 | 一文解决,BP神经网络的故障诊断(Matlab) 专栏介绍 订阅【故障诊断】专栏,不定期更新机器学习和深度学习在故障诊断中的应用;订阅

策略模式+SpringBoot接口,一个接口实现接收的数据自动分流处理

策略模式 定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。策略模式的精髓就在于将经常变化的一点提取出来,单独变成一类,并且各个类别可以相互替换和组合。 1、策略接口 CalculationStrategy //算数 public interface…

dubbo rpc序列化

序列化配置 provider <dubbo:service interface"com.example.DemoService" serialization"hessian2" ref"demoService"/>consumer <dubbo:reference id"demoService" interface"com.example.DemoService" seria…

PyTorch 最新安装教程

以下教程适用于 Windows、Linux 和 macOS 操作系统 1、确保您的计算机上已经安装了 Python。建议使用 Python 3.7 或更高版本。 2、打开终端&#xff08;对于 Windows 用户&#xff0c;请使用命令提示符&#xff09;。 3、在终端中运行以下命令&#xff0c;以安装 PyTorch 的…

解决:IDEA无法下载源码,Cannot download sources, sources not found for: xxxx

原因 Maven版本太高&#xff0c;遇到http协议的镜像网站会阻塞&#xff0c;要改为使用https协议的镜像网站 解决方案 1.打开设置 2. 拿到settings.xml路径 3. 将步骤2里箭头2的User settings file&#xff1a;settings.xml打开&#xff0c;作以下修改 保存即可。如果还不行…