【Spring Boot 源码学习】BootstrapRegistry 详解

举报
Huazie 发表于 2024/08/24 19:52:23 2024/08/24
【摘要】 本篇 Huazie 带大家通读了 BootstrapRegistry 的相关源码,这些内容对于后面的源码学习至关重要。

一、引言

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

二、主要内容

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

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

2.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 :实例供应者

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

2.3 registerIfAbsent 方法

registerIfAbsent 方法,包含两个参数:

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

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

2.4 isRegistered 方法

isRegistered 方法,只有一个参数:

  • Class<T> type :实例类型

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

2.5 getRegisteredInstanceSupplier 方法

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

  • Class<T> type :实例类型

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

2.6 addCloseListener 方法

addCloseListener 方法,只有一个参数:

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

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

2.7 InstanceSupplier 内部接口类

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

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

知识拓展:

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

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

2.7.1 get 方法

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

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

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

2.7.2 getScope 默认方法

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

2.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,实际上就是自己调自己,一直无限递归调用,最终导致栈溢出错误。

2.7.4 of 静态方法

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

return (registry) -> instance;

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

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

2.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(),它不接受任何参数,但返回一个通用类型的值。

2.8 Scope 内部枚举类

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

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

三、总结

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

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。