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

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

文章目录

  • 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的实现,而不影响调用方,保持了代码的高可维护性和在这里插入图片描述
扩展性。

相关文章:

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

文章目录 Pre概述依赖倒置原则与解耦设计与实现1. 定义接口来隔离调用方与实现类2. 实现类DynamicStubFactory3. 调用方与实现类的解耦 依赖注入与SPI的解耦依赖注入SPI&#xff08;Service Provider Interface&#xff09; 总结 Pre Simple RPC - 01 框架原理及总体架构初探 …...

2024新型数字政府综合解决方案(三)

新型数字政府综合解决方案通过融合人工智能、大数据和云计算技术&#xff0c;建立了一个智能化、互联互通的政府服务平台&#xff0c;旨在提升政府服务效率与透明度。该方案通过全面数字化政务流程&#xff0c;实现数据的实时共享和自动化处理&#xff0c;使公众能够便捷地访问…...

计算机毕业设计hadoop+spark+hive知识图谱音乐推荐系统 音乐数据分析可视化大屏 音乐爬虫 LSTM情感分析 大数据毕设 深度学习 机器学习

流程&#xff1a; 1.Python采集网易云音乐歌手、歌词、音乐、评论等约10-20万海量数据&#xff0c;存入mysql数据库&#xff1b; 2.使用pandasnumpy/MapReduce对mysql中四类数据进行数据清洗&#xff0c;写入.csv文件并上传至hdfs(含评论NLP文本分类/lsm情感分析); 3.使用hive建…...

值类型与引用类型

值类型 在Swift中&#xff0c;如果一个对象是用struct实现的&#xff0c;则该对象为值类型&#xff0c;在被赋值给常量或者变量时或者作为参数传递给函数时&#xff0c;值类型总是被复制&#xff0c;复制后的对象与之前的对象指向不同的内存。 Swift的基本类型(Array、Dictio…...

C++STL初阶(12):stack和queue的初阶实现

1. stack的选型 对于栈的实现是我们非常熟悉的过程&#xff1a; C语言基础数据结构——栈和队列_栈和队列 插入取出数据-CSDN博客 _top表示下标&#xff0c;_capacity表示空间大小&#xff1a; 那么按照我们原来的思路&#xff0c;利用_top和_capacity T*来给stack构形。 temp…...

汽车IVI中控OS Linux driver开发实操(二十三):驱动的设备probe及匹配

第一个函数:probe linux驱动模型是分成三个部分的,设备(结构体device),驱动(结构体device_driver),总线(结构体bus_type)。在Linux内核中,设备驱动通常会实现一个probe函数,它是...

华为od(D卷)二叉树计算

文章目录 题目描述输入描述输出描述示例1思路代码 题目描述 给出一个二叉树如下图所示&#xff1a; 6/ \7 9\ / -2 6 请由该二叉树生成一个新的二叉树&#xff0c;它满足其树中的每个节点将包含原始树中的左子树和右子树的和。 20 (7-296)/ \-2 6\ / 0 0 左子树…...

技术爱好者完全用台式机部件定制游戏笔记本电脑

高端笔记本电脑的功能强大到令人难以置信的地步&#xff0c;但大多数笔记本电脑在至少几个关键性能方面仍然落后于台式机。一位 YouTuber 对这种情况感到厌倦&#xff0c;为了抹除这种差距&#xff0c;他开始了为期 14 个月的旅程&#xff0c;使用真正的台式机硬件打造自己的笔…...

100个练习学习Rust!if・Panic・演练

之前的文章 【0】准备 【1】构文・整数・变量 ← 上回 【2】 if・Panic・演练 ← 本次 这是“100 Exercise To Learn Rust”的第2次练习&#xff01;本次的主题包括 if 表达式、panic 机制&#xff0c;以及对前面内容的总结练习。 本次相关的页面如下&#xff1a; 2.3. Bran…...

MODELSIM仿真报错解决记录

目录 问题&#xff1a;Modelsim报错&#xff1a;Error (10228): Verilog HDL error at Line_Shift_RAM_1Bit.v(39): module “Line_Shift_RAM_1 原因&#xff1a;创建的IP核放到了别的位置 解决方法&#xff1a;删掉IP核以及QIP等文件&#xff0c;将IP核创建到工程目录下 问…...

day33-负载均衡实战

01.问题总结 1.rsync同步注意目录加/和不加/的区别 2.安装wordpress过程中禁止使用IP安装,解析成域名安装 比如安装过程 10.0.0.7--->填写数据库信息--->写入数据库中 如果安装完成后再使用www.wp.com访问&#xff0c;不能访问页面乱码的问题。 3.挂载wordpress挂载uplo…...

网络接口 eno1 未连接或未托管

网络接口 eno1 未连接或未托管&#xff0c;通常意味着该接口没有被识别或没有被配置为自动连接到网络。以下是一些可能的解决方案&#xff1a; 检查物理连接&#xff1a; 确保您的以太网电缆正确连接到 eno1 接口和调制解调器/路由器。 启用网络接口&#xff1a; 使用以下命令…...

Linux I/O 多路复用机制详解

文章目录 1 文件描述符&#xff08;File Descriptor&#xff09;1.1 什么是文件描述符&#xff1f;1.2 文件描述符与文件的关系 2 文件描述符集合&#xff08;File Descriptor Set&#xff09;2.1 什么是文件描述符集合&#xff1f;2.2 fd_set 结构体 3 select() 函数的工作原理…...

第43课 Scratch入门篇:雪花随风飘

雪花随风飘 故事背景: 雪花轻轻地从灰蒙蒙的天空中飘落下来,它们像是天空中飘洒下来的羽毛,又像是冬日的精灵在翩翩起舞。每一片雪花都独一无二,它们在空中旋转、飘荡,最终缓缓降落在屋顶、树枝、街道和行人的肩头。 程序原理: 众多的雪花肯定是克隆功能,降落过程是通过…...

VueUse 基于 Vue 3 Composition API 的高质量 Hooks 库

VueUse 是什么? VueUse 是基于 Vue 3 Composition API 的高质量 Hooks 库。例如获取滚动的距离 VueUse 官网:VueUse | VueUse VueUse 什么使用? 1、通过npm安装 VueUse npm i @vueuse/core 2、搜索需要使用的函数,例如搜索 useScroll 滚动 3、使用useScroll 滚动函数 …...

ARM CoreLink 系列 5.1.1 -- CI-700 System Address Map 】

文章目录 System Address MapRN SAMRN SAM memory regions and target typesSAM memory region size configurationRN SAM target ID selectionSystem Address Map 所有的CHI 命令都包含一个 Source ID 和 Target ID, 其中 Source ID 可以来自于 RN Node, Target ID 可以来自…...

【数据结构】二叉树(一)

目录 1. 树型结构 概念 树的表示形式 ​编辑 2. 二叉树&#xff08;重点&#xff09; 2.1 概念 2.2 二叉树的性质 2.3 二叉树的存储 2.4 二叉树的遍历 前中后序遍历 层序遍历&#xff1a; 2.5二叉树的基本操作 本篇主要理解树和二叉树相关概念&#xff0c;二叉树遍…...

使用duplicate搭建备库或者级联备库

使用duplicate搭建备库或者级联备库&#xff1a; 主库或者源端&#xff1a; 1. 创建pfile&#xff0c;更改&添加部分参数、传输到备库&#xff1b; 2. 主库&#xff08;或者源端&#xff09;的tnsnames.ora文件添加 备库的连接信息 备库&#xff1a; 1. 备库添加静态监听 2…...

【存储学习笔记】4:快照(Snapshot)技术的实现方式

1 快照 1.1 动机 在上一篇《备份》里提到&#xff0c;热备份就是在执行操作时&#xff0c;服务器需要正常处理来自用户或应用对数据的更新&#xff0c;这样能够保证数据7*24小时可用&#xff08;在很多服务里这是必要的&#xff09;。 而热备份的困难就是如何保证数据的一致…...

数根(字符串数根公式)

公式&#xff1a;a的数根(a-1)%91&#xff1b; #include <bits/stdc.h> using namespace std; string s; long long sum; int main(){cin>>s;for(int i0;i<s.size();i){sums[i]-0;}cout<<(sum-1)%91; }...

地震勘探——干扰波识别、井中地震时距曲线特点

目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波&#xff1a;可以用来解决所提出的地质任务的波&#xff1b;干扰波&#xff1a;所有妨碍辨认、追踪有效波的其他波。 地震勘探中&#xff0c;有效波和干扰波是相对的。例如&#xff0c;在反射波…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误

HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误&#xff0c;它们的含义、原因和解决方法都有显著区别。以下是详细对比&#xff1a; 1. HTTP 406 (Not Acceptable) 含义&#xff1a; 客户端请求的内容类型与服务器支持的内容类型不匹…...

(十)学生端搭建

本次旨在将之前的已完成的部分功能进行拼装到学生端&#xff0c;同时完善学生端的构建。本次工作主要包括&#xff1a; 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...

K8S认证|CKS题库+答案| 11. AppArmor

目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作&#xff1a; 1&#xff09;、切换集群 2&#xff09;、切换节点 3&#xff09;、切换到 apparmor 的目录 4&#xff09;、执行 apparmor 策略模块 5&#xff09;、修改 pod 文件 6&#xff09;、…...

VB.net复制Ntag213卡写入UID

本示例使用的发卡器&#xff1a;https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...

基于服务器使用 apt 安装、配置 Nginx

&#x1f9fe; 一、查看可安装的 Nginx 版本 首先&#xff0c;你可以运行以下命令查看可用版本&#xff1a; apt-cache madison nginx-core输出示例&#xff1a; nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...

【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)

要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况&#xff0c;可以通过以下几种方式模拟或触发&#xff1a; 1. 增加CPU负载 运行大量计算密集型任务&#xff0c;例如&#xff1a; 使用多线程循环执行复杂计算&#xff08;如数学运算、加密解密等&#xff09;。运行图…...

动态 Web 开发技术入门篇

一、HTTP 协议核心 1.1 HTTP 基础 协议全称 &#xff1a;HyperText Transfer Protocol&#xff08;超文本传输协议&#xff09; 默认端口 &#xff1a;HTTP 使用 80 端口&#xff0c;HTTPS 使用 443 端口。 请求方法 &#xff1a; GET &#xff1a;用于获取资源&#xff0c;…...

GitFlow 工作模式(详解)

今天再学项目的过程中遇到使用gitflow模式管理代码&#xff0c;因此进行学习并且发布关于gitflow的一些思考 Git与GitFlow模式 我们在写代码的时候通常会进行网上保存&#xff0c;无论是github还是gittee&#xff0c;都是一种基于git去保存代码的形式&#xff0c;这样保存代码…...

MFC 抛体运动模拟:常见问题解决与界面美化

在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...