【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);

	@FunctionalInterface
	interface 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>() {
		@Override
		public T get(BootstrapContext context) {
			return parent.get(context);
		}

		@Override
		public 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>() {
    @Override
    public 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>() {
    @Override
    public 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 的相关源码,这些内容对于后面的源码学习至关重要。

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

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

相关文章

Python基础小知识:format函数用法

format优点 format是python2.6新增的一个格式化字符串的方法&#xff0c;相对于老版的%格式方法&#xff0c;它有很多优点。 1.不需要理会数据类型的问题&#xff0c;在%方法中%s只能替代字符串类型 2.单个参数可以多次输出&#xff0c;参数顺序可以不相同 3.填充方式十分灵…

【深入浅出SpringCloud原理及实战】「Netflix系列之Hystrix」针对于限流熔断组件Hystrix的回退降级实现方案和机制

针对于限流熔断组件Hystrix的回退降级实现方案和机制 依赖隔离依赖隔离之线程&线程池高延迟请求的例子 线程池的优势线程池的弊端线程池的开销线程池开销 信号量 依赖隔离 Hystrix通过使用『舱壁模式』&#xff08;注&#xff1a;将船的底部划分成一个个的舱室&#xff0c;…

项目01《游戏-03-开发》Unity2D

基于 项目01《游戏-02-开发》Unity2D &#xff0c; 继续制作游戏&#xff1a; 首先要做的时切割人物Idle空闲状态下的动画&#xff0c; 在切割之前我们需要创建一个文件夹&#xff0c;用来存放动画控制器AnimatorContoller&#xff0c; 再创建一个人物控制器文件夹…

沁恒微WCH32v003驱动ST7735S硬件spi+DMA调试小坑

最近项目需要&#xff0c;要用wch32v003驱动ST7735S&#xff0c;用硬件spiDMA方式可以提高屏幕刷新率&#xff0c;但是使用过程遇到一下问题&#xff0c;分享出来&#xff0c;有清楚的大佬可以指点指点。 这篇文章并不是给着急移植程序使用的人看的&#xff0c;因为在赶进度的时…

如果我要访问一个网址,那么在网络中会有哪些过程

访问一个网址是我们日常网络使用中非常常见的操作&#xff0c;背后涉及到一系列精密而复杂的步骤。这个过程包括DNS解析、建立TCP连接、发起HTTP请求、服务器处理请求、服务器响应、浏览器渲染等环节。在这篇文章中&#xff0c;我们将深入探讨这些步骤&#xff0c;并解释它们在…

力扣hot100 跳跃游戏 II 贪心 思维

Problem: 45. 跳跃游戏 II 思路 &#x1f468;‍&#x1f3eb; 参考 每次在上次能跳到的范围&#xff08;end&#xff09;内选择一个能跳的最远的位置&#xff08;也就是能跳到max_far位置的点&#xff09;作为下次的起跳点 &#xff01; Code ⏰ 时间复杂度: O ( n ) O(n…

猫什么时候发腮?性价比高的发腮主食冻干推荐

猫什么时候发腮&#xff1f;发腮是猫咪成长过程中一个重要的体征&#xff0c;也是猫父母们非常关心的问题。想要让猫咪拥有可爱的肉嘟嘟脸型&#xff0c;主人需要在适龄的年龄段加强营养补给&#xff0c;不要错失最佳发腮期。猫什么时候发腮呢&#xff1f;应该怎样喂养才能让猫…

TypeScript实战教程(一):表单上传与后端处理

TypeScript实战教程&#xff08;一&#xff09;&#xff1a;表单上传与后端处理 文章目录 TypeScript实战教程&#xff08;一&#xff09;&#xff1a;表单上传与后端处理一、前言1、TypeScript介绍2、TypeScript的关键特性包括&#xff1a;3、使用场景4、编译过程 二、环境配置…

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

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

不同核函数高斯过程回归算法与不同因子输入情况下对长江流域蒸散发量应用研究_杨梓涵_2023

不同核函数高斯过程回归算法与不同因子输入情况下对长江流域蒸散发量应用研究_杨梓涵_2023 摘要关键词 0 引言1 材料与方法1.1 数据资料1.2 参考作物腾发量( ET0 ) 计算方法1.2.1 FAO&#xff0d;56 Penman&#xff0d;Monteith 模型1.2.2 Hargreaves&#xff0d;Samani 模型1.…

apisix多节点搭建

文章目录 前言一、介绍1. 端口介绍2. APISIX节点介绍3. apisix单机安装配置教程(选看) 二、准备1. 配置集群免密登录2. 搭建etcd集群 三、安装apisix节点1. 复制脚本2. 增加执行权限3. 分发脚本4. 执行脚本5. 配置apisix的etcd集群地址 四、安装apisix-dashboard1. 复制脚本2. …

【GEE】基于GEE批量下载Landsat8 L1C数据(整幅)

之前发过一篇使用GEE下载Landsat8的文章&#xff0c;然后有很多小伙伴私信我各种问题&#xff0c;如L1C、L2数据代码怎么修改&#xff0c;如何镶嵌&#xff0c;如何去云、 如何裁剪等一系列问题。正好快过年了&#xff0c;手头的事也没有多少了&#xff0c;所以这两天整理了一下…

蜂邮EDM邮件营销平台,低至0.0041元每封!

推荐的邮件营销平台有哪些&#xff1f;邮件营销平台如何使用&#xff1f; 你是否厌倦了传统的推广方式&#xff1f;是时候尝试一种全新的、高效的传播方式了&#xff01;蜂邮EDM邮件营销平台正在掀起一场数字化风潮&#xff0c;每封邮件仅需0.0041元&#xff0c;让你的推广成本…

5款超级好用的桌面端软件推荐

​ 今天我想分享一些自己比较喜欢的桌面端软件&#xff0c;还请大家包涵指正。如果你曾搜索过 Windows效率工具推荐&#xff0c;对下文的软件或许有所了解。不过为了凑字数&#xff0c;我还是会再介绍一遍。 1.电子书阅读——Starrea ​ Starrea是一款轻量、易用而又全功能的…

C#/.NET/.NET Core优秀项目和框架2024年1月简报

前言 公众号每月定期推广和分享的C#/.NET/.NET Core优秀项目和框架&#xff08;每周至少会推荐两个优秀的项目和框架当然节假日除外&#xff09;&#xff0c;公众号推文中有项目和框架的介绍、功能特点、使用方式以及部分功能截图等&#xff08;打不开或者打开GitHub很慢的同学…

[Python-闫式DP]

闫式DP分析法 闫老师是将DP问题归结为了有限集合中的最值问题。 动态规划有两个阶段&#xff0c;一是状态表示&#xff0c;二是状态计算。 状态表示 f(i,j) 状态表示是一个化零为整的过程&#xff0c;动态规划的做题思路不是暴力法的每一个物品都去枚举&#xff0c;而是将相…

异步解耦之RabbitMQ(二)__RabbitMQ架构及交换机

异步解耦之RabbitMQ(一) RabbitMQ架构 RabbitMQ是一个基于AMQP&#xff08;Advanced Message Queuing Protocol&#xff09;协议的消息代理中间件&#xff0c;它通过交换机和队列实现消息的路由和分发。以下是RabbitMQ的架构图&#xff1a; Producer&#xff08;生产者&#…

Java设计模式-组合模式(13)

大家好,我是馆长!今天开始我们讲的是结构型模式中的组合模式。老规矩,讲解之前再次熟悉下结构型模式包含:代理模式、适配器模式、桥接模式、装饰器模式、外观模式、享元模式、组合模式,共7种设计模式。 组合模式(Composite Pattern) 定义 组合(Composite)模式:又叫…

深度学习与神经网络Pytorch版 3.2 线性回归从零开始实现 1.生成数据集

3.2 线性回归从零开始实现 目录 3.2 线性回归从零开始实现 一 &#xff0c;简介 1. 原理 2. 步骤 3. 优缺点 4. 应用场景 二 &#xff0c;代码展现 1. 生成数据集(完整代码) 2. 各个函数解析 2.1 torch.normal()函数 2.2 torch.matmul()函数 2.3 d2l.plt.scatter(…

【教学类-44-54】20240201 德彪钢笔行书(实线字体)制作的数字描字帖

作品展示 背景需求&#xff1a; 找到了两款适合做数字描字贴的字体 【教学类-44-03】20240111阿拉伯数字字帖的字体&#xff08;三&#xff09;——德彪钢笔行书&#xff08;实线字体&#xff09;和print dashed&#xff08;虚线字体&#xff09;-CSDN博客文章浏览阅读1.1k次…