深入理解java和dubbo的SPI机制
1 SPI简介
1.1 SPI(Service Provider Interface)
本质:将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。这样可以在运行时,动态为接口替换实现类。
java SPI:用来设计给服务提供商做插件使用的。基于策略模式来实现动态加载的机制。我们在程序只定义一个接口,具体的实现交个不同的服务提供者;在程序启动的时候,读取配置文件,由配置确定要调用哪一个实现。
dubbo SPI:在dubbo中也有SPI机制,虽然都需要将接口全限定名配置在文件中,但是dubbo并没有使用java的spi机制,而是重新实现了一套功能更强的 SPI 机制, 支持了AOP与依赖注入,并且 利用缓存提高加载实现类的性能,同时 支持实现类的灵活获取。基于 SPI,我们可以很容易的对 Dubbo 进行拓展。例如dubbo当中的protocol,LoadBalance等都是通过SPI机制扩展。
2 java SPI
2.1实现过程
1)需要在 classpath 下创建一个目录,该目录命名必须是:META-INF/service
2)在该目录下创建一个 文本文件,该文件需要满足以下几个条件
- 文件名必须是扩展的接口的全路径名称
- 文件内部描述的是该扩展接口的所有实现类
- 文件的编码格式是 UTF-8
3)通过 java.util.ServiceLoader 的加载机制来加载服务


2.2 工作原理
1)当调用 ServiceLoader.load(Class clz) 方法时,会到jar中中的目录 “META-INF/services/“ + clz.getName 进行文件读取,
2)当在调用ServiceLoader.forEach()方法时,实际走的是LazyIterator,当在调用LazyIterator.hasNext() 时,在文件中读取到实际的服务实现类并把它们通过调用 Class.forName(String name, boolean initialize,ClassLoader loader)。

2.3 实际应用
javaSPI我们最熟悉的应用就是数据库驱动了,mysql和oracle驱动针对JDBC分别有自己的实现,这就有赖于java的SPI机制。

3 dubbo SPI
3.1 实现过程
1)需要在 classpath 下创建一个目录,该目录命名可以是:META-INF/service/、META-INF/dubbo/、META-INF/dubbo/internal/
2)在该目录下创建一个 文本文件,该文件需要满足以下几个条件
- 文件名必须是扩展的接口的全路径名称
- 文件内部描述的是该扩展接口的所有实现类,将服务实现类写成KV键值对的形式,Key是拓展类的name,Value是扩展的全限定名实现类。
3)通过 org.apache.dubbo.common.extension.ExtensionLoader 的加载机制来加载服务


3.2 工作原理
1)我们首先通过 ExtensionLoader的 getExtensionLoader 方法获取一个接口的 ExtensionLoader 实例,然后再通过 ExtensionLoader 的 getExtension 方法获取拓展类对象,源码如下,首先是 getExtensionLoader 方法:

new ExtensionLoader(type)源码如下:

注意这里创建 ExtensionLoader对象的构造方法如下:ExtensionLoader.getExtensionLoader获取ExtensionFactory接口的拓展类,再通过 getAdaptiveExtension从拓展类中获取目标拓展类。
2)通过 ExtensionLoader.getExtensionLoader取到接口的加载器Loader之后,再通过 getExtension方法获取需要拓展类对象。

以上代码首先检查holder中的实例缓存,缓存未命中则创建拓展对象。dubbo中包含了大量的扩展点缓存。这个就是典型的使用空间换时间的做法。

创建拓展类对象步骤分别为:
- 通过 getExtensionClasses 从配置文件中加载所有的拓展类,再通过名称获取目标拓展类
- 通过反射创建拓展对象
- 向拓展对象中注入依赖
- 将拓展对象包裹在相应的 Wrapper 对象中
我们接下来重点看下getExtensionClasses方法:

先从缓存中获取class,缓存未命中则调用loadExtensionClasses方法加载,我们再看下loadExtensionClasses这个方法:

我们看到这里遍历调用了多个策略去加载class的,跟到这里我们发现非常有意思的是:dubbo在加载META-INF目录下的class键值对的时候采用了javaSPI的方式


这里dubbo使用javaSPI的方式加载到3中类加载策略:
org.apache.dubbo.common.extension.DubboInternalLoadingStrategy 用于加载META-INF/dubbo/internal/中的class
org.apache.dubbo.common.extension.DubboLoadingStrategy 用于加载META-INF/dubbo/中的class
org.apache.dubbo.common.extension.ServicesLoadingStrategy 用于加载META-INF/service/中的class
dubbo的SPI还提供了自适应(Adaptive)、自动注入的功能就不在这里过多展开了,有兴趣可以自行了解。
3.3 实际应用
dubbo中大量使用了SPI机制:

例如dubbo的多协议的实现:

4 javaSPI和dubboSPI对比
- Java SPI在加载扩展点的时候,会一次性加载所有可用的扩展点,很多是不需要的,会浪费系统资源。dubboSPI有选择性地加载所需要的SPI接口。
- javaSPI配置文件中只是简单的列出了所有的扩展实现,而没有给他们命名。导致在程序中很难去准确的引用它们。而dubboSPI配置文件中以键值对的形式有别名,易于区分。
- SPI扩展如果依赖其他的扩展,javaspi做不到自动注入和装配,dubbo可以实现自动注入。
- javaSPI不提供类似于Spring的IOC和AOP功能,dubboSPI是支持的
作者:京东物流 龚航林
来源:京东云开发者社区 自猿其说Tech 转载请注明来源
深入理解java和dubbo的SPI机制的更多相关文章
- 某大厂面试题:说一说Java、Spring、Dubbo三者SPI机制的原理和区别
大家好,我是三友~~ 今天来跟大家聊一聊Java.Spring.Dubbo三者SPI机制的原理和区别. 其实我之前写过一篇类似的文章,但是这篇文章主要是剖析dubbo的SPI机制的源码,中间只是简单地 ...
- 面试常问的dubbo的spi机制到底是什么?
前言 dubbo是一款微服务开发框架,它提供了 RPC通信 与 微服务治理 两大关键能力.作为spring cloud alibaba体系中重要的一部分,随着spring cloud alibaba在 ...
- dubbo的spi机制
SPI SPI是一种扩展机制,在java中SPI机制被广泛应用,比如Spring中的SpringServletContainerInitializer 使得容器启动的时候SpringServletCo ...
- jdk和dubbo的SPI机制
前言:开闭原则一直是软件开发领域中所追求的,开闭原则中的"开"是指对于组件功能的扩展是开放的,是允许对其进行功能扩展的,“闭”,是指对于原有代码的修改是封闭的,即不应该修改原有的代 ...
- Dubbo的SPI机制与JDK机制的不同及原理分析
从今天开始,将会逐步介绍关于DUbbo的有关知识.首先先简单介绍一下DUbbo的整体概述. 概述 Dubbo是SOA(面向服务架构)服务治理方案的核心框架.用于分布式调用,其重点在于分布式的治理. 简 ...
- 《深入理解 Java 虚拟机》学习 -- 类加载机制
<深入理解 Java 虚拟机>学习 -- 类加载机制 1. 概述 虚拟机把描述类的数据从 Class 文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的 J ...
- Dubbo剖析-SPI机制
文章要点: 1.什么是SPi 2.Dubbo为什么要实现自己的SPi 3.Dubbo的IOC和AOP 4.Dubbo的Adaptive机制 5.Dubbo动态编译机制 6.Dubbo与Spring的融 ...
- 深入理解Java虚拟机:虚拟机类加载机制
目录 7.1 概述 7.2 类加载的时机 类的生命周期 5种情况需要"初始化" 7.3 类加载的过程 1.加载 2.验证 3.准备 4.解析 5.初始化 7.4 类加载器 类与类加 ...
- 《深入理解Java虚拟机》虚拟机类加载机制
上节学习回顾 上一节,我们深入到类文件去了解其结构细节,也大概对类文件的编写规则略知一二了,解析来我们就得学习这个类文件是如何被加载到Java虚拟机的,看看有什么引人入胜的奥秘. 本节学习重点 大部分 ...
- 深入理解Java中方法的参数传递机制
形参和实参 我们知道,在Java中定义方法时,是可以定义参数的,比如: public static void main(String[] args){ } 这里的args就是一个字符串数组类型的参数. ...
随机推荐
- 前端vue uni-app多图片上传组件,支持单个文件,多个文件上传 步骤条step使用
快速实现多图片上传组件,支持单个文件,多个文件上传 步骤条step使用; 下载完整代码请访问uni-app插件市场地址:https://ext.dcloud.net.cn/plugin?id=1274 ...
- MySQL 存储引擎 InnoDB 内存结构之缓冲池
缓冲池是主存储器中的一个区域,在访问 table 和索引数据时InnoDB会对其进行缓存.缓冲池允许直接从内存中访问频繁使用的数据,从而加快处理速度.在专用服务器上,通常将高达 80% 的物理内存分配 ...
- Unity中的RegisterPlugins:深入解析与实用案例
Unity中的RegisterPlugins:深入解析与实用案例 在Unity游戏开发中,我们经常需要使用第三方插件来实现一些特定的功能.为了让这些插件能够在Unity中正常工作,我们需要对它们进行注 ...
- 基于python+django的宠物商店-宠物管理系统设计与实现
该系统是基于python+django开发的宠物商店-宠物管理系统.是给师妹开发的课程作业.现将源码开放给大家.大家学习过程中,如遇问题可以在github咨询作者. 演示地址 前台地址: http:/ ...
- 一张图告诉你如何提高 API 性能
API 性能是指一个 API 在执行其功能时的效率和性能表现,通常用于衡量 API 的响应时间.吞吐量.可伸缩性和稳定性等方面的表现. API 性能的指标包括: 响应时间: API 的响应时间是指从发 ...
- HTML前端js
ajax请求方法书写 $.ajax({ type:"POST", url:CONTEXT_PATH+"/appAudit/insertSnDocCountAdmin&qu ...
- 2021-11-17 WPF初识
StackPanel容器:默认竖直排列,Orientation="Horizontal"横向排列,超过就不会显示 wrapPanel:超过会自动换行 设置样式: <Windo ...
- 何为DDD
从这一刻开始,请大家忘记自己是一名技术人员,用业务的角度来思考问题. 1.什么是DDD DDD(Domain-driven design,领域驱动设计),是一个很好的应用于微服务架构的方法论 DDD要 ...
- 解密Prompt系列12. LLM Agent零微调范式 ReAct & Self Ask
前三章我们分别介绍了思维链的使用,原理和在小模型上的使用.这一章我们正式进入应用层面,聊聊如何把思维链和工具使用结合得到人工智能代理. 要回答我们为什么需要AI代理?代理可以解决哪些问题?可以有以下两 ...
- 【HCDG城市行东莞站】松山湖开发者村助力企业释放数字新动能
本文分享自华为云社区<[HCDG城市行东莞站]从"数据治理"洞察"数字化运营之道",松山湖开发者村助力企业释放数字新动能!>,作者:华为云社区精选. ...