Simple RPC - 05 从零开始设计一个客户端(下)_ 依赖倒置和SPI

news2024/12/23 22:06:02

文章目录

  • Pre
  • 概述
  • 依赖倒置原则与解耦
    • 设计与实现
      • 1. 定义接口来隔离调用方与实现类
      • 2. 实现类`DynamicStubFactory`
      • 3. 调用方与实现类的解耦
  • 依赖注入与SPI的解耦
    • 依赖注入
    • SPI(Service Provider Interface)
  • 总结

在这里插入图片描述

Pre

Simple RPC - 01 框架原理及总体架构初探

Simple RPC - 02 通用高性能序列化和反序列化设计与实现

Simple RPC - 03 借助Netty实现异步网络通信

Simple RPC - 04 从零开始设计一个客户端(上)


概述

接 Simple RPC - 04 从零开始设计一个客户端(上) ,我们继续分析 依赖倒置和SPI是如何实现的。


依赖倒置原则与解耦

在软件设计中,依赖倒置原则(Dependence Inversion Principle, DIP) 是SOLID原则之一。它主张高层模块(调用者)不应依赖于低层模块(实现类),而是两者都应该依赖于抽象(接口或抽象类)。这意味着具体的实现细节应当与高层业务逻辑分离,通过接口来隔离依赖关系,从而提高代码的可维护性、可扩展性和可复用性。

设计模式 - 六大设计原则之ISP(接口隔离原则)


设计与实现

在这个RPC框架的设计中,通过定义接口来解耦调用方和具体实现,完全符合依赖倒置原则。

我们来看下是如何应用DIP来解耦的。

1. 定义接口来隔离调用方与实现类

public interface StubFactory {
    <T> T createStub(Transport transport, Class<T> serviceClass);
}

StubFactory接口定义了创建桩的方法,而具体的实现类DynamicStubFactory实现了该接口。

2. 实现类DynamicStubFactory

public class DynamicStubFactory implements StubFactory {
    // 实现 createStub 方法的逻辑
}

DynamicStubFactory实现了StubFactory接口,提供了实际的桩生成逻辑。

3. 调用方与实现类的解耦

在调用方NettyRpcAccessPoint中,我们并不直接依赖于具体的DynamicStubFactory,而是依赖于StubFactory接口。调用方通过接口与实现类进行交互,这样如果以后需要更换不同的StubFactory实现,只需更改实现类而无需修改调用方的代码。

public class NettyRpcAccessPoint {
    private final StubFactory stubFactory;

    public NettyRpcAccessPoint(StubFactory stubFactory) {
        this.stubFactory = stubFactory;
    }

    public <T> T createStub(Transport transport, Class<T> serviceClass) {
        return stubFactory.createStub(transport, serviceClass);
    }
}

依赖注入与SPI的解耦

依赖注入

通常情况下,依赖注入(如Spring框架)可以帮助我们实现这种解耦,通过配置或注解,框架会自动将具体的实现注入到调用方中。但在不使用Spring的情况下,我们可以使用Java内置的SPI机制来实现类似的解耦。

SPI(Service Provider Interface)

SPI机制通过在META-INF/services/目录下配置接口的实现类,在运行时动态加载这些实现类,实现依赖倒置。

  1. 配置文件

    • META-INF/services/目录下创建一个文件,文件名是接口的完全限定名(例如com.github.liyue2008.rpc.client.StubFactory)。
    • 文件内容是接口的实现类名(例如com.github.liyue2008.rpc.client.DynamicStubFactory)。
  2. SPI加载实现类


/**
 * 提供服务加载功能的支持类,特别是处理单例服务
 * @author artisan
 */
public class ServiceSupport {
    /**
     * 存储单例服务的映射,确保每个服务只有一个实例
     */
    private final static Map<String, Object> singletonServices = new HashMap<>();

    /**
     * 加载单例服务实例
     *
     * @param service 服务类的Class对象
     * @param <S> 服务类的类型参数
     * @return 单例服务实例
     * @throws ServiceLoadException 如果找不到服务实例
     */
    public synchronized static <S> S load(Class<S> service) {
        return StreamSupport.
                stream(ServiceLoader.load(service).spliterator(), false)
                .map(ServiceSupport::singletonFilter)
                .findFirst().orElseThrow(ServiceLoadException::new);
    }

    /**
     * 加载所有服务实例
     *
     * @param service 服务类的Class对象
     * @param <S> 服务类的类型参数
     * @return 所有服务实例的集合
     */
    public synchronized static <S> Collection<S> loadAll(Class<S> service) {
        return StreamSupport.
                stream(ServiceLoader.load(service).spliterator(), false)
                .map(ServiceSupport::singletonFilter).collect(Collectors.toList());
    }

    /**
     * 对服务实例进行单例过滤
     *
     * @param service 服务实例
     * @param <S> 服务类的类型参数
     * @return 单例过滤后的服务实例,如果该服务是单例的并且已有实例存在,则返回已存在的实例
     */
    @SuppressWarnings("unchecked")
    private static <S> S singletonFilter(S service) {

        if(service.getClass().isAnnotationPresent(Singleton.class)) {
            String className = service.getClass().getCanonicalName();
            Object singletonInstance = singletonServices.putIfAbsent(className, service);
            return singletonInstance == null ? service : (S) singletonInstance;
        } else {
            return service;
        }
    }
}

调用ServiceSupport.load(StubFactory.class)时,SPI机制会查找META-INF/services/目录下对应的配置文件,加载其中指定的实现类实例。


总结

通过依赖倒置原则(DIP)和SPI机制,我们有效地解耦了调用方与实现类。在这个RPC框架中,StubFactory接口及其实现类DynamicStubFactory之间的依赖关系被逆转,调用方只依赖接口,而不直接依赖具体实现。SPI机制进一步解耦了调用方与实现类的实例化,使得在运行时可以动态加载实现类,这为框架的扩展性和灵活性提供了强有力的支持。

通过这种设计,框架可以很容易地替换StubFactory的实现,而不影响调用方,保持了代码的高可维护性和在这里插入图片描述
扩展性。

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

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

相关文章

一个模型,多种作物:迁移学习如何提升设施农业AI模型效能

&#xff08; 于景鑫 国家农业信息化工程技术研究中心&#xff09;设施农业是现代农业的"压舱石",但传统的经验式管理模式已难以为继。在数字经济时代,设施农业亟需向数字化、网络化、智能化转型升级。以人工智能为代表的信息技术,正在为设施农业插上腾飞的翅膀。作为…

Kafka主题(Topic/文件夹)的操作

Kafka主题&#xff08;Topic/文件夹&#xff09;的操作 1、Kafka主题&#xff08;Topic/文件夹&#xff09;2、Kafka主题&#xff08;Topic/文件夹&#xff09;的一些操作2.1、创建主题&#xff08;Topic/文件夹&#xff09;2.2、列出所有主题&#xff08;Topic/文件夹&#xf…

8路VBO转HDMI2.0支持4K60频率ITE6265芯片方案心得分享

在此之前&#xff0c;有人找到我这边询问能不能将智能电视主板改成机顶盒&#xff0c;将VBO信号转换输出位HDMI进行投屏&#xff0c;具体应用奇奇怪怪&#xff01;但是奈何是甲方大佬。认命照做。从网上也有搜索了解过这类芯片&#xff0c;发现资料很少&#xff0c;所以有了这篇…

基于免疫算法的最优物流仓储点选址方案MATLAB仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 基于免疫算法的最优物流仓储点选址方案MATLAB仿真。 2.测试软件版本以及运行结果展示 MATLAB2022A版本运行 &#xff08;完整程序运行后无水印&#xff09; 3…

STM32标准库学习笔记-1.基础知识

STM32介绍&#xff1a; STM32是ST公司基于ARM Cortex-M内核开发的32位微控制器。 ARM的含义&#xff1a; 公司名称&#xff1a;ARM公司成立于1990年&#xff0c;全称是Advanced RISC Machines&#xff08;RISC:Reduced Instruction Set Computer 精简指令集计算机 相对应有C…

C++票据查验、票据ocr、文字识别

现在&#xff0c;80、90后的人们逐渐过渡为职场上的主力人员&#xff0c;在工作中当然也会碰到各种各样的问题。比如&#xff0c;当你的老板给你一个艰难的任务时&#xff0c;肯定是不能直接拒绝的。那么我们该怎么做呢&#xff1f;翔云建议您先认真考虑老板说的任务的难度&…

C语言日常练习 Day17

目录 一、找出一个二维数组的鞍点 二、有一篇文章&#xff0c;共有3行文字&#xff0c;每行有80个字符。要求分别统计出其中的英文大写字母、小写字母、数字、空格以及其他字符的个数 三、有一行电文&#xff0c;已按下面规律编译成密码&#xff1a;A->Z,a->z,B->Y,…

mp3格式转换器推荐哪个?音质无损转换新选择

暑假在家&#xff0c;想要自己学着制作独一无二的彩铃吗&#xff1f;首先得解决音乐格式的难题。 面对众多mp3格式转换app&#xff0c;你是否也在犹豫“mp3格式转换app常用哪个&#xff1f;” 别担心&#xff0c;热门mp3格式转换工具大盘点来袭&#xff0c;帮你轻松搞定格式转…

《深入浅出WPF》读书笔记.5控件与布局(上)

《深入浅出WPF》读书笔记.5控件与布局(上) 背景 深入浅出WPF书籍学习笔记附代码。WPF中数据是核心是主动的,UI是数据的表达是被动的。 程序的本质是数据算法&#xff1b;控件的本质是数据行为&#xff1b; 5.控件与布局 一、6类控件派生关系 1.布局控件:可以容纳多个控件…

为什么神经网络常常是linear+relu的堆叠

特征提取&#xff1a;每一层的线性变换可以看作是在提取输入数据的不同特征。通过堆叠多个这样的层&#xff0c;网络能够学习从原始数据中提取越来越复杂的特征表示非线性关系&#xff1a;单个神经元的线性变换是线性的&#xff0c;但通过引入非线性激活函数&#xff08;例如Re…

重生奇迹MU 百变职业 打造属于你的专属职业

重生奇迹MU让每个玩家都能够在这里打破常规、展示个性&#xff0c;以自己的方式创造专属的游戏体验。其中的加点玩法是最为重要的部分之一&#xff0c;它不仅在角色成长过程中发挥着重要作用&#xff0c;还能够激发玩家的想象力&#xff0c;让他们自由设计全新的玩法。同样一个…

美股收涨,半导体板块领涨;苹果iPhone出货预测上调

市场概况 在昨夜的交易中&#xff0c;美股三大股指全线收涨。道琼斯工业平均指数上涨1.39%&#xff0c;纳斯达克综合指数上涨2.34%&#xff0c;标准普尔500指数上涨1.61%。值得注意的是&#xff0c;英伟达股票涨幅近4%&#xff0c;推动了科技股的整体表现。美国十年期国债收益…

【W1】记录文档

标题 导出数据结构关系结构YOLOVOCLabelMeCOCOJSON可视化 导出数据结构 关系结构 任务 -> 批次 -> 条目 -> 帧YOLO 一张图片对应一个文本文件 .txt<object-class> <x> <y> <width> <height>归一化处理找到物体在图像中的中心点的 x&a…

018、钩子函数 mounted和beforeDestroy、父组件向子组件传递参数 props 的使用

文章目录 1、mounted 和 beforeDestroy1.1、mounted1.2、beforeDestroy 2、父组件向子组件传递参数 props2.1、子组件定义2.2、父组件调用子组件并传参 3、完整例子3.1、父组件 Tags.vue3.2、子组件 TagsMenu.vue3.3、效果图 1、mounted 和 beforeDestroy 1.1、mounted mount…

人类是怎样提取特征并学习和表达复杂的模式的?

人类提取特征、学习和表达复杂模式的过程涉及多方面的认知和神经机制。一些关键步骤和机制涉及&#xff1a; 感知是人类获取外部信息的过程。通过感官&#xff08;视觉、听觉、触觉等&#xff09;&#xff0c;人类可以接触到各种数据。注意力机制帮助我们从大量信息中筛选出重要…

Trying to Insert an input function using Python in OpenAI

题意&#xff1a;尝试在 OpenAI 中使用 Python 插入一个输入函数 问题背景&#xff1a; import os import openai openai.api_key API_Key Question "\\n\\nQ: input("Enter Question")?\\nA:", response openai.Completion.create( model"text-…

C++ 容器 deque(双端队列)

我们在学习之前的数据结构链表和顺序表的时候&#xff0c;发现顺序表插入删除慢&#xff0c;但是下标查找等速度快&#xff0c;链表下面查找等速度慢&#xff0c;但是插入删除快。它们互补。 那么我们是否可以将两个数据结构结合起来&#xff0c;产生一个完美的数据结构呢&…

LangChain与Elasticsearch向量数据库的完美结合

在过去的一年中&#xff0c;生成式 AI (Generative AI) 领域取得了显著的进展。许多新的服务和工具应运而生。其中&#xff0c;LangChain 已成为构建大语言模型 (LLM) 应用程序&#xff08;例如检索增强生成 (RAG) 系统&#xff09;最受欢迎的框架之一。该框架极大地简化了原型…

【网络安全】通过设备ID绕过双因素身份验证

未经许可,不得转载。 文章目录 正文正文 某站点登陆界面如下所示:https://redacted.com/login 输入正确的用户名密码并拦截登录请求,请求包如下: POST /api/authentication/login-2fa HTTP/1.1 Host: redacted.com Content-Length: 100 Sec-Ch-Ua: "Not)A;Brand&quo…

(南京观海微电子)——投影仪原理及使用介绍

投影仪 是一种用来放大显示图像的投影装置。目前已经应用于会议室演示以及在家庭中通过连接DVD影碟机等设备在大屏幕上观看电影。在电影院&#xff0c;也同样已开始取代老电影胶片的数码影院放映机&#xff0c;被用作面向硬盘数字数据的银幕。 投影仪的成像原理 首先&#xff…