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

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网络编程:OSI协议,TCP/IP协议,IP地址,UDP编程

目录 国际网络通信协议标准&#xff1a; 1.OSI协议&#xff1a; 2.TCP/IP协议模型&#xff1a; 应用层 &#xff1a; 传输层&#xff1a; 网络层&#xff1a; IPV4协议 IP地址 IP地址的划分&#xff1a; 公有地址 私有地址 MA…

dotnet常用命令详解

命令预览 基础命令 new&#xff1a;创建项目 restore&#xff1a;恢复依赖 build&#xff1a;编译项目 publish&#xff1a;生成项目需要的文件准备发布项目 run&#xff1a;运行项目 test&#xff1a;测试项目 vstest&#xff1a;从指定的程序集中运行测试 pack&#…

升级阿里云linux服务器上的php版本

查看已安装的php软件包 [rootiZbp13pl2v34qj0thwq9aiZ ~]# rpm -qa|grep php php74-php-common-7.4.26-1.el7.remi.x86_64 oniguruma5php-6.9.7.1-1.el7.remi.x86_64 php74-php-gd-7.4.26-1.el7.remi.x86_64 php74-php-opcache-7.4.26-1.el7.remi.x86_64 php74-php-json-7.4.2…

记录|Git工具——下载GitHub项目

目录 前言一、Step 1. 下载Git二、Step2. 用Git Bash 下载到本地更新时间 前言 参考文章&#xff1a; 1、如何使用Git将Github项目拉到本地 2、git 安装、创建仓库、上传项目、克隆下载、常用命令 – 一篇文章总结&#xff08;适用github / gitee&#xff09; 3、Git的使用【入…

政策驱动,科技引领,漫途信息化监测方案守护农村饮水安全!

近日&#xff0c;山西省人民政府正式发布《山西省农村供水高质量发展规划》&#xff0c;明确到2030年&#xff0c;全面实现农村24小时供水&#xff0c;县域农村饮水安全标准化建设达标率力争达到80%&#xff0c;基本实现农村供水城市化、城乡供水均等化。 现阶段部分地区受…

ArcGIS简单介绍

ArcGIS体系结构 &#xff08;1&#xff09;GIS Server 宿主各种GIS资源&#xff0c;将他们封装为服务提供给客户端应用。GIS Server分为两部分&#xff1a;Server Object Manager&#xff08;SOM&#xff09;和Server Object Containers&#xff08;SOCs&#xff09;。一个SO…

Java语言程序设计——篇十三(4)

&#x1f33f;&#x1f33f;&#x1f33f;跟随博主脚步&#xff0c;从这里开始→博主主页&#x1f33f;&#x1f33f;&#x1f33f; 欢迎大家&#xff1a;这里是我的学习笔记、总结知识的地方&#xff0c;喜欢的话请三连&#xff0c;有问题可以私信&#x1f333;&#x1f333;&…

仪表板展示|DataEase看中国:2024巴黎奥运会中国体育代表团战绩报告

背景介绍 北京时间2024年8月12日凌晨&#xff0c;巴黎奥运会闭幕。在本届奥运会中&#xff0c;我们不仅见证了许多新世界纪录的诞生&#xff0c;更看到了中国体育的强大实力与无限潜力。中国运动健儿们卓越的表现和顽强的拼搏精神&#xff0c;不但让国人为之自豪&#xff0c;也…

Windows 11 Build 27686 上手体验:2TB FAT32、更好的沙盒等功能

Windows 11 Build 27686 现已在 Insider Program 的 Canary 频道发布&#xff0c;其中包含一些有趣的内容。该版本确认了微软更改 FAT32 大小限制的计划&#xff0c;并将其大小限制从 32GB 提高到 2TB。与此同时&#xff0c;沙盒也得到了改进。让我们来详细了解一下此次更新。 …

【卡码网Python基础课 16.出现频率最高的字母】

目录 题目描述与分析描述2.分析 一、哈希表二、代码编写 题目描述与分析 描述 题目描述&#xff1a; 给定一个只包含小写字母的字符串&#xff0c;统计字符串中每个字母出现的频率&#xff0c;并找出出现频率最高的字母&#xff0c;如果最高频率的字母有多个&#xff0c;输出…

Nginx--虚拟机配置

前言&#xff1a;本博客仅作记录学习使用&#xff0c;部分图片出自网络&#xff0c;如有侵犯您的权益&#xff0c;请联系删除 1、什么是虚拟主机 虚拟主机是一种特殊的软硬件技术&#xff0c;它可以将网络上的每一台计算机分成多个虚拟主机&#xff0c;每个虚拟主机可以独立对…

字符串函数!!!(续)(C语言)

一. strtok函数的使用 继续上次的学习&#xff0c;今天我们来认识一个新的函数strtok&#xff0c;它的原型是char* strtok(char* str,const char* sep)&#xff0c;sep参数指向了一个字符串&#xff0c;定义了用作分隔符的字符合集&#xff0c;第一个参数指定⼀个字符串&#…

基于C# winform部署图像动漫化AnimeGANv2部署onnx模型

【界面截图】 【效果演示】 【部分实现代码】 using System; using System.Diagnostics; using System.Windows.Forms; using OpenCvSharp;namespace FIRC {public partial class Form1 : Form{Mat src null;public Form1(){InitializeComponent();}private void button1_Cli…

消息系统-WebSocket消息推送

消息系统-WebSocket消息推送 接口层使用消息通知 1.数据库设计: 1.消息通知表 2.消息记录表 3.用户表和角色表及用户角色记录表 2.设计: 未使用消息中间件 ,利用接口层调用消息通知接口工具类 3.前端:消息通知页面 1.消息通知列表 2.消息通知标签 3.消息通知未读抽屉列表 一.…

Ubuntu离线安装库并解决依赖关系

&#xff08;1&#xff09;起因 安装插件出现库未找到的错误 configure: error: curses library is required but not found.&#xff08;2&#xff09;解决方法 手动到Ubuntu的库发布网页下载 http://packages.ubuntu.com/ 选择系统对应架构的版本下载&#xff0c;然后上传…

django(REST_FRAMEWORK)+swagger+Apifox 集成

1.reset_framework 1.1安装rest_framework 1.2使用rest_framework 在django框架中setting文件中注册rest_framework INSTALLED_APPS [rest_framework, ]2.reset_frameworkswagger 2.1.安装drf_yasg 2.2.在django框架中setting文件中注册drf_yasg INSTALLED_APPS [drf_…

滴滴开源新项目Unify:聚焦Flutter与原生通信难题,助力跨端应用落地

引言 在移动开发领域&#xff0c;移动跨端技术因其提效收益&#xff0c;逐渐成为业界趋势之一。Flutter 作为近年来热门的跨端技术&#xff0c;以高性能、自渲染、泛跨端著称&#xff0c;得到广泛应用。在滴滴国际化业务中&#xff0c;我们大量应用 Flutter。目前已在滴滴国际化…

【大模型部署及其应用 】RAG检索技术和生成模型的应用程序架构:RAG 使用 Meta AI 的 Llama 3

目录 RAG检索技术和生成模型的应用程序架构1. **基本概念**2. **工作原理**3. **RAG的优势**4. **常见应用场景**5. **RAG的挑战**6. **技术实现**参考RAG 使用 Meta AI 的 Llama 3亲自尝试运行主笔记本与文档应用聊天关键架构组件1. 自定义知识库2. 分块3. 嵌入模型4. 矢量数据…

PHP多商家营销活动平台系统小程序源码

解锁营销新境界&#xff01;「多商家营销活动平台」让你的品牌火出圈✨ &#x1f680;【聚合力量&#xff0c;共创辉煌】&#x1f680; 在这个竞争激烈的市场中&#xff0c;单打独斗早已不是最佳选择&#xff01;「多商家营销活动平台」横空出世&#xff0c;它像一座桥梁&…

关于Python3项目中依赖包管理问题

背景&#xff1a;最近在使用Python3.11编写脚本来获取google play中app的用户评论&#xff0c;脚本中需要安装多个依赖包&#xff0c;在本地Pycharm调试通过以后&#xff0c;上传到github&#xff0c;然后在linux服务器拉取脚本来运行&#xff0c;发现存在几个问题。本文将面临…