当前位置: 首页 > news >正文

Dubbo SPI 加载逻辑

SPI 配置的加载

Dubbo 通过 loadExtensionClasses 方法加载 SPI。

    /**
     * synchronized in getExtensionClasses
     */
    private Map<String, Class<?>> loadExtensionClasses() throws InterruptedException {
        checkDestroyed();
        cacheDefaultExtensionName();

        Map<String, Class<?>> extensionClasses = new HashMap<>();

        for (LoadingStrategy strategy : strategies) {
            loadDirectory(extensionClasses, strategy, type.getName());

            // compatible with old ExtensionFactory
            if (this.type == ExtensionInjector.class) {
                loadDirectory(extensionClasses, strategy, ExtensionFactory.class.getName());
            }
        }

        return extensionClasses;
    }

通过 strategies 加载扩展点集合,三个 LoadingStrategy 指定的目录分别为 ‘META-INF/dubbo/internal/, META-INF/dubbo/, META-INF/services/’。其中,META-INF/dubbo/internal/ 存放的是 Dubbo 内置的一些扩展点,META-INF/services/ 存放的是 Dubbo 自身的一些业务逻辑所需要的一些扩展点,而 META-INF/dubbo/ 存放的是上层业务系统自身的一些定制 Dubbo 的相关扩展点。

该方法返回给 getExtensionClasses, getExtensionClasses 将加载的 SPI 类对象缓存起来。

SPI 扩展类实例的获取

通过 ExtensionAccessor.getExtensionAccessor 方法获取 ExtensionAccessor,然后从 ExtensionAccessor 获取 SPI 扩展类的实例。

		// 获取 ExtensionAccessor 实例
        ExtensionAccessor extensionAccessor = ExtensionAccessor.getExtensionAccessor();
        // 通过 ExtensionAccessor 获取扩展加载器
        ExtensionLoader<HelloService> extensionLoader = extensionAccessor.getExtensionLoader(HelloService.class);

ExtensionAccessor 调用 ExtensionDirector 获取 ExtensionLoader:

public interface ExtensionAccessor {

    ExtensionDirector getExtensionDirector();

    default <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
        return getExtensionDirector().getExtensionLoader(type);
    }

	...
}

ExtensionDirector 用于管理和协调多个 ExtensionLoader,其 getExtensionLoader 方法实现如下:

    @Override
    @SuppressWarnings("unchecked")
    public <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
        checkDestroyed();
        if (type == null) {
            throw new IllegalArgumentException("Extension type == null");
        }
        if (!type.isInterface()) {
            throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!");
        }
        if (!withExtensionAnnotation(type)) {
            throw new IllegalArgumentException("Extension type (" + type
                    + ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
        }

        // 1. find in local cache
        ExtensionLoader<T> loader = (ExtensionLoader<T>) extensionLoadersMap.get(type);

        ExtensionScope scope = extensionScopeMap.get(type);
        if (scope == null) {
            SPI annotation = type.getAnnotation(SPI.class);
            scope = annotation.scope();
            extensionScopeMap.put(type, scope);
        }

        if (loader == null && scope == ExtensionScope.SELF) {
            // create an instance in self scope
            loader = createExtensionLoader0(type);
        }

        // 2. find in parent
        if (loader == null) {
            if (this.parent != null) {
                loader = this.parent.getExtensionLoader(type);
            }
        }

        // 3. create it
        if (loader == null) {
            loader = createExtensionLoader(type);
        }

        return loader;
    }

ExtensionDirector 首先在自身缓存查找 ExtensionLoader,然后在 parent 查找 ExtensionLoader,都找不到时则通过 type 创建。

一个 ExtensionLoader 对应一个 SPI 接口,而每个 SPI 接口可以有多个扩展实现类,所以 ExtensionLoader 和 Extension 的关系是一对多。以下示例提供了多个 LoadBalance 扩展实现类,通过 getExtension 和扩展类的名称获取 Extension。

import org.apache.dubbo.common.extension.ExtensionLoader;

// 定义SPI接口
interface LoadBalance {
    String select();
}

// 实现类1:随机负载均衡
class RandomLoadBalance implements LoadBalance {
    @Override
    public String select() {
        return "Random selection";
    }
}

// 实现类2:轮询负载均衡
class RoundRobinLoadBalance implements LoadBalance {
    @Override
    public String select() {
        return "Round-robin selection";
    }
}

public class Main {
    public static void main(String[] args) {
        // 获取LoadBalance接口对应的ExtensionLoader
        ExtensionLoader<LoadBalance> extensionLoader = ExtensionLoader.getExtensionLoader(LoadBalance.class);

        // 获取不同的扩展实现
        LoadBalance randomLoadBalance = extensionLoader.getExtension("random");
        LoadBalance roundRobinLoadBalance = extensionLoader.getExtension("roundrobin");
    }
}

在 ExtensionLoader 中加载 Extension 的逻辑如下:

getExtension -> createExtension -> createExtensionInstance -> InstantiationStrategy.instantiate

InstantiationStrategy.instantiate 将一个类的对象实例化:

  1. 找到合适的构造器
  2. 从 ScopeModel 拿到构造参数
  3. 通过类构造器实例化并返回

ExtensionLoader 与 Extension

为什么不一股脑加载出 Extension,为什么需要 ExtensionLoader?

1. 解耦与模块化设计

分离关注点:ExtensionLoader 承担了扩展加载的具体逻辑,将扩展加载的过程与使用扩展的代码分离开来。这样一来,使用扩展的代码不需要关心扩展是如何从配置文件中加载出来的,只需要通过 ExtensionLoader 获取所需的扩展实例即可,符合单一职责原则,提高了代码的可维护性和可扩展性。
便于扩展和维护:当需要修改扩展加载的规则或者支持新的配置文件格式时,只需要修改 ExtensionLoader 的实现,而不会影响到使用扩展的代码。例如,后续如果要支持从数据库中加载扩展信息,只需要在 ExtensionLoader 中添加相应的逻辑。

2. 配置解析与管理

配置文件解析:Dubbo 的扩展配置通常存储在 META - INF/dubbo 或 META - INF/services 目录下的文件中,ExtensionLoader 负责解析这些配置文件,提取出扩展接口与实现类的映射关系。例如,它会读取文件内容,将配置信息解析成 Map 结构,方便后续查找和使用。
配置缓存:ExtensionLoader 会对解析后的配置信息进行缓存,避免重复解析配置文件,提高扩展加载的性能。当多次获取同一个扩展接口的扩展实现时,直接从缓存中获取配置信息,而不需要再次读取和解析配置文件。

3. 实例化与生命周期管理

实例化扩展:ExtensionLoader 负责根据配置信息实例化扩展类。它会处理扩展类的构造函数注入、依赖注入等问题,确保扩展类能够正确地被创建。例如,如果扩展类的构造函数需要传入其他扩展实例,ExtensionLoader 会先获取这些依赖的扩展实例,然后再实例化该扩展类。
生命周期管理:ExtensionLoader 可以管理扩展实例的生命周期,包括初始化、销毁等操作。在扩展实例创建后,ExtensionLoader 可以调用扩展类的初始化方法;在扩展实例不再使用时,也可以调用相应的销毁方法,确保资源的正确释放。

4. 自适应扩展与包装器处理

自适应扩展:Dubbo 支持自适应扩展机制,ExtensionLoader 负责实现自适应扩展的逻辑。自适应扩展可以根据运行时的参数动态选择合适的扩展实现,而不需要在代码中硬编码扩展的名称。ExtensionLoader 会生成自适应扩展的代理类,根据实际情况调用不同的扩展实现。
包装器处理:当获取扩展实例时,ExtensionLoader 会自动将包装器应用到 Extension 实例上,实现对扩展功能的增强。

Extension 访问器 ExtensionAccessor

ExtensionAccessor 是 Extension 的统一访问器。

  • 用于获取扩展加载管理器ExtensionDirector对象
  • 获取扩展对象 ExtensionLoader
  • 根据扩展名字获取具体扩展对象
  • 获取自适应扩展对象
  • 获取默认扩展对象

模块领域模型 ScopeModel

ScopeModel

ScopeModel 是为了实现:

  1. 让 Dubbo 支持多应用的部署,这块一些大企业有诉求
  2. 从架构设计上,解决静态属性资源共享、清理的问题
  3. 分层模型将应用的管理和服务的管理分开

ScopeModel 类是模型对象的公共抽象父类型

  • 内部 id 用于表示模型树的层次结构
  • 公共模型名称,可以被用户设置
  • 描述信息
  • 类加载器管理
  • 父模型管理 parent
  • 当前模型的所属域 ExtensionScope 有: FRAMEWORK (框架),APPLICATION (应用),MODULE (模块),SELF (自给自足,为每个作用域创建一个实例,用于特殊的 SPI 扩展,如 ExtensionInjector)
  • 具体的扩展加载程序管理器对象的管理: ExtensionDirector
  • 域 Bean 工厂管理,一个内部共享的 Bean 工厂 ScopeBeanFactory

FrameworkModel

dubbo 框架模型,可与多个应用程序共享

  • FrameworkModel 实例对象集合,allInstances
  • 所有 ApplicationModel 实例对象集合,applicationModels
  • 发布的 ApplicationModel 实例对象集合 pubApplicationModels
  • 框架的服务存储库 FrameworkServiceRepository 类型对象(数据存储在内存中)
  • 内部的应用程序模型对象 internalApplicationModel

ApplicationModel

ApplicationModel 表示正在使用 Dubbo 的应用程序,并存储基本元数据信息,以便在 RPC 调用过程中使用。ApplicationModel 包括许多关于发布服务的 ProviderModel 和许多关于订阅服务的 Consumer Model。

  • ExtensionLoader、DubboBootstrap 和这个类目前被设计为单例或静态(本身完全静态或使用一些静态字段)。因此,从它们返回的实例属于流程范围。如果想在一个进程中支持多个 dubbo 服务器,可能需要重构这三个类。
  • 所有 ModuleModel 实例对象集合 moduleModels
  • 发布的 ModuleModel 实例对象集合 pubModuleModels
  • 环境信息 Environment 实例对象 environment
  • 配置管理 ConfigManager 实例对象 configManager
  • 服务存储库 ServiceRepository 实例对象 serviceRepository
  • 应用程序部署器 ApplicationDeployer 实例对象 deployer
  • 所属框架 FrameworkModel 实例对象 frameworkModel
  • 内部的模块模型 ModuleModel 实例对象 internalModule
  • 默认的模块模型 ModuleModel 实例对象 defaultModule

Module Model

ModuleModel 是服务模块的模型。

  • 所属应用程序模型 ApplicationModel 实例对象 applicationModel
  • 模块环境信息 ModuleEnvironment 实例对象 moduleEnvironment
  • 模块服务存储库 ModuleServiceRepository 实例对象 serviceRepository
  • 模块的服务配置管理 ModuleConfigManager 实例对象 moduleConfigManager
  • 模块部署器 ModuleDeployer 实例对象 deployer 用于导出和引用服务

相关文章:

  • GEO与AISEO的关系解析:核心差异与协同逻辑
  • WebLogic漏洞再现
  • 算法基础篇(1)(蓝桥杯常考点)
  • Java多线程与高并发专题——使用 Future 有哪些注意点?Future 产生新的线程了吗?
  • python解决多个矢量点图层合并为一个点图层
  • Jackson的核心类与API方法:ObjectMapper、JsonNode、ObjectNode、ArrayNode
  • Mongodb分片模式部署
  • 基于HTML的邮件发送状态查询界面设计示例
  • centos7 下haproxy+keepalived 搭建高可用服务器
  • 软件版本号制定方法
  • 蓝桥杯C++基础算法-多重背包
  • quartz.net条件执行
  • python基于spark的心脏病患分类及可视化(源码+lw+部署文档+讲解),源码可白嫖!
  • 【QA】Qt中有哪些命令模式的运用?
  • AI日报 - 2025年3月24日
  • Langchain4J框架相关面试题
  • 施磊老师高级c++(五)
  • 知识库已上线
  • 同步双写与删缓存在缓存一致性的实践对比
  • linux---------进程概念(上)
  • 一条铺过11年时光的科学红毯,丈量上海科创的“长宽高”
  • 梅花奖在上海|湘剧《夫人如见》竞梅,长沙文旅来沪推广
  • 博物馆日|为一个展奔赴一座城!上海171家博物馆等你来
  • 国寿资产获批参与第三批保险资金长期投资改革试点
  • 广西壮族自治区政府主席蓝天立任上被查
  • 俄乌官员即将在土耳其会谈,外交部:支持俄乌开启直接对话