前面的博文《BootstrapRegistryInitializer 详解》,Huazie 带大家一起详细分析了 Spring Boot 启动时加载并初始化 BootstrapRegistryInitializer
及其相关的类的逻辑。其中有个 BootstrapRegistry
接口只是简单提及,本篇就详细分析一下 BootstrapRegistry
接口,这对于我们后续理解 《BootstrapRegistry
初始化器实现》的内容至关重要。
注意: 以下涉及 Spring Boot 源码 均来自版本 2.7.9,其他版本有所出入,可自行查看源码。
在 《BootstrapRegistryInitializer 详解》 的 2.1 小节,我们对 BootstrapRegistry
进行了初步的介绍:它是一个用于存储和共享对象的注册表,这些对象在 ApplicationContext
准备好之前就可能已经被创建并需要被共享。
首先让我们来看看 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个内部枚举类,下面我们一一来介绍下:
register
方法,包含两个参数:
Class<T> type
:实例类型InstanceSupplier<T> instanceSupplier
:实例供应者该方法用于将特定类型注册到注册表中。如果指定的类型已经被注册,并且尚未作为单例获取,那么它将被替换。
registerIfAbsent
方法,包含两个参数:
Class<T> type
:实例类型InstanceSupplier<T> instanceSupplier
:实例供应者如果尚未存在特定类型的注册,则向注册表中注册该类型。
isRegistered
方法,只有一个参数:
Class<T> type
:实例类型该方法用于返回给定类型是否已注册。如果给定类型已经注册,则返回 true
,否则,返回 false
。
getRegisteredInstanceSupplier
方法,也只有一个参数:
Class<T> type
:实例类型该方法返回给定类型的任何现有的 BootstrapRegistry.InstanceSupplier
。
addCloseListener
方法,只有一个参数:
ApplicationListener<BootstrapContextClosedEvent> listener
:待添加的监听器该方法用于添加一个 ApplicationListener
,当 BootstrapContext
关闭并且 ApplicationContext
已经准备就绪时,该监听器将与 BootstrapContextClosedEvent
一起被调用。
InstanceSupplier
内部接口类是用于提供实际实例的供应者。
它定义了一个 1 个普通方法,2 个默认方法,2 个静态方法。
知识拓展:
从 Java 8 开始,支持在接口中定义默认方法和静态方法。
get
方法,只包含一个参数:
BootstrapContext context
:BootstrapContext 是一个用于获取其他引导实例的上下文该方法是工厂方法,用于在需要时创建实例,后续我们在讲解 DefaultBootstrapContext
时也会涉及。
getScope
默认方法,用于返回提供的实例的作用域;如果该方法没有被重写,则默认返回 Scope.SINGLETON
。
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
的匿名对象,该匿名对象重写了 get
和 getScope
方法。这里使用匿名对象的好处就是可以在不定义新类的情况下快速地创建一个具有特定行为的对象。
细心的读者们,可能发现了匿名对象的 get
方法中,使用了 withScope
方法中定义的变量 parent
,它被用来存储当前对象的引用 this
。
那么这里的 parent
,能不能直接替换成 this
呢?
显然是不可以的,this
关键字用在匿名内部类中,指代的是该匿名内部类本身,而不是外部对象。而匿名对象这里的重写的 get
方法,实际上需要调用 withScope
方法所在的对象的 get
方法来实现功能。如果这里用 this
,实际上就是自己调自己,一直无限递归调用,最终导致栈溢出错误。
该静态方法是一个工厂方法,用于为给定实例创建一个BootstrapRegistry.InstanceSupplier
。
return (registry) -> instance;
这里采用了 Java 8 的 Lambda
表达式,也相当于如下的写法:
return new InstanceSupplier<T>() {
@Override
public T get(BootstrapContext registry) {
return instance;
}
};
该静态方法也是一个工厂方法,用于通过一个 Supplier
创建BootstrapRegistry.InstanceSupplier
。
return (registry) -> (supplier != null) ? supplier.get() : null;
这里也是用了 Java 8 的 Lambda
表达式,相当于如下的写法:
return new InstanceSupplier<T>() {
@Override
public T get(BootstrapContext registry) {
return (supplier != null) ? supplier.get() : null;
}
};
知识点:
Supplier
是 Java 8 开始引入,作为java.util.function
包的一部分,它与Lambda
表达式一起被引入,以支持函数式编程范式。该接口是为了简化无参数方法的表示,特别是在需要延迟执行或创建对象时。Supplier
接口只有一个抽象方法get
(),它不接受任何参数,但返回一个通用类型的值。
Scope 表示一个实例的作用域,它只包含两个枚举变量,分别是:
SINGLETON
:单例实例。InstanceSupplier
只会被调用一次,并且每次调用都会返回相同的实例。PROTOTYPE
:原型实例。InstanceSupplier
将在需要实例时被调用。本篇 Huazie 带大家通读了 BootstrapRegistry
的相关源码,这些内容对于后面的源码学习至关重要。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。