我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Dubbo系列JDK SPI原理是什么

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Dubbo系列JDK SPI原理是什么

这篇文章主要介绍“Dubbo系列JDK SPI原理是什么”,在日常操作中,相信很多人在Dubbo系列JDK SPI原理是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Dubbo系列JDK SPI原理是什么”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

正文

在阅读的过程中你会发现,有很多代码很相似,并且重复出现,比如这里:

private static final ProxyFactory PROXY_FACTORY = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();private static final Protocol PROTOCOL = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();……Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, url);DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);Exporter<?> exporter = PROTOCOL.export(wrapperInvoker);exporters.add(exporter);……

这段代码是 Dubbo 服务发布的关键流程,其中用到了两个类都是通过ExtensionLoader.getExtensionLoader()去获得的,这其实体现的就是Dubbo 的SPI机制,SPI 机制在 Dubbo 中被大量运用,也是 Dubbo 设计的亮点所在。

为什么要使用SPI?

这就要谈到Dubbo的架构设计了,之前提到 Dubbo 采用的是分层架构的方式,Dubbo 的设计体现了程序设计中的开闭原则,每一层都可以被另一种实现技术替换掉,而不影响上下层之间的依赖和整体逻辑的运转,这其实就是微内核架构(微内核+插件)。

微内核架构也被称为插件化架构(Plug-in Architecture),这是一种面向功能进行拆分的可扩展性架构。内核功能是比较稳定的,只负责管理插件的生命周期,不会因为系统功能的扩展而不断进行修改。功能上的扩展全部封装到插件之中,插件模块是独立存在的模块,包含特定的功能,全部可被替换,并且可以拓展内核系统的功能,而 Dubbo 最终决定采用 SPI 机制来加载插件。

开闭原则 OCP (Open-Closed Principle ):程序的设计应该是不约束扩展,即扩展开放,但又不能修改已有功能,即修改关闭。

Dubbo系列JDK SPI原理是什么

什么是 SPI

SPI ,全称为 Service Provider Interface,直接翻译过来是服务提供者接口,是一种服务发现机制,而我们通常指的是JDK的SPI。

JDK SPI,它是JDK内置的一种服务发现机制,可以动态的发现服务,即服务提供商,它通过在ClassPath路径下的META-INF/services文件夹查找文件,自动加载文件里所定义的类。

根据他的定义就知道他主要是被框架开发人员使用的,通过 SPI 可以加载服务本身以外的扩展。最常用的比如JDBC驱动连接时候选择不同的驱动,对java.sql.Driver的实现就利用了SPI机制;Spring框架中也使用了很多,比如在 Spring 中为了支持Servlet3.0规范不使用web.xml,对javax.servlet.ServletContainerInitializer的实现也利用了SPI;在Dubbo中更是大量运用了SPI机制,不但有JDK SPI的运用,更重要的是 Dubbo 自己还实现了一套SPI机制。

JDK SPI 机制

当服务提供者想利用SPI机制去扩展,需要遵循以下步骤。

  • 首先需要实现实现对应接口。

  • 然后需要在 Classpath 下的 META-INF/services/ 目录中创建一个以服务接口全路径命命名的文件。

  • 在该文件中记录服务接口的所有具体实现类,通常是在外部引入的扩展包中,比如引入JDBC的jar包。

  • 做好以上配置,就可以利用JDK SPI的查找机制在META-INF/services/文件夹下根据配置来对具体的实现类加载和实例化。

如果看完上述流程还不是很清楚,请看如下示例。比如这里有接口 MySPI 需要按照上述流程按照SPI机制加载。

Dubbo系列JDK SPI原理是什么

首先提供了两个实现类 GoodbyeMySPI 和 HelloMySPI,然后在 META-INF/services 文件夹下保存了文件org.daley.spi.demo.MySPI,文件的内容是两个实现类的全路径名。就绪之后就可以在main方法中启动demo,用ServiceLoader.load()加载 MySPI 的两个实现类,然后分别调用接口方法执行。代码如下:

public interface MySPI {    void say();}public class HelloMySPI implements MySPI{    @Override    public void say() {        System.out.println("HelloMySPI say:hello");    }}public class GoodbyeMySPI implements MySPI {    @Override    public void say() {        System.out.println("GoodbyeMySPI say:Goodbye");    }}public static void main(String[] args) {    ServiceLoader<MySPI> serviceLoader = ServiceLoader.load(MySPI.class);    Iterator<MySPI> iterator = serviceLoader.iterator();  // 开始迭代执行    while (iterator.hasNext()) {        MySPI spi = iterator.next();        spi.say();    }}//配置文件 org.daley.spi.demo.MySPIorg.daley.spi.demo.GoodbyeMySPIorg.daley.spi.demo.HelloMySPI//输出如下://GoodbyeMySPI say:Goodbye//HelloMySPI say:hello

JDK SPI原理

看完上述 demo,你有没有好奇 JDK SPI 的原理是什么?相信你已经猜的八九不离十了。我们从demo的main方法开始追踪起。

很明显关键的代码就一行ServiceLoader.load(MySPI.class),他是整个类加载的入口。

  • 调用ServiceLoader.load(MySPI.class)开始加载,并且会拿到当前线程的类加载器。

  • 开始创建一个ServiceLoader,最终可以追踪到调用reload()方法

public static <S> ServiceLoader<S> load(Class<S> service) {    ClassLoader cl = Thread.currentThread().getContextClassLoader();    return ServiceLoader.load(service, cl);}public static <S> ServiceLoader<S> load(Class<S> service,                                            ClassLoader loader){    return new ServiceLoader<>(service, loader);}private ServiceLoader(Class<S> svc, ClassLoader cl) {  // 绑定接口和类加载器    service = Objects.requireNonNull(svc, "Service interface cannot be null");    loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;    acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;  // 加载    reload();}

reload()方法中首先清空了providers,它里面存贮了所有服务接口的实现,key为实现类名,value为对象。然后便会new一个LazyIteratorLazyIteratorServiceLoader内部实现的一个迭代器,此时还没有真正开始加载,只是保存了接口和类加载器在迭代器中。

private LinkedHashMap<String,S> providers = new LinkedHashMap<>();public void reload() {    providers.clear();    lookupIterator = new LazyIterator(service, loader);}private class LazyIterator implements Iterator<S>{  private LazyIterator(Class<S> service, ClassLoader loader) {        this.service = service;        this.loader = loader;  }}

正如其名懒加载迭代器,在调用iterator.hasNext()时才真正发生加载。在hasNextService()方法中,第一次调用此方法会先拼接出配置SPI的文件名,在本demo中也就是META-INF/services/org.daley.spi.demo.MySPI,然后会使用类加载器加载配置文件并且将每行的内容设置到迭代器pending中,每次遍历都可以按行依次拿到实现类的名字。

Dubbo系列JDK SPI原理是什么

比如第一次迭代,返回第一行配置的实现类名org.daley.spi.demo.GoodbyeMySPI。到这里只是加载配置文件拿到类名,还未加载类。

public boolean hasNext() {    if (acc == null) {        return hasNextService();    } else {      ……    }}// pengding中保存了配置文件中的实现类名,按行迭代Iterator<String> pending = null;private boolean hasNextService() {      ……    if (configs == null) {        try {          // 设置配置文件路径 META-INF/services/org.daley.spi.demo.MySPI            String fullName = PREFIX + service.getName();            if (loader == null)                configs = ClassLoader.getSystemResources(fullName);            else                configs = loader.getResources(fullName);        } catch (IOException x) {            fail(service, "Error locating configuration files", x);        }    }  // 第一次遍历 pendind=null    while ((pending == null) || !pending.hasNext()) {        if (!configs.hasMoreElements()) {            return false;        }        pending = parse(service, configs.nextElement());    }  // 按行拿到实现类名    nextName = pending.next();    return true;}

当执行到iterator.next()的时候才会真正去加载class类。追踪此方法最终进入nextService()方法,在这里你会看到熟悉的Class.forName()以及newInstance()方法,读取类和实例化类的逻辑一目了然。到这里JDK SPI的核心逻辑就结束了。

public S next() {    if (acc == null) {        return nextService();    } else {        ……    }}private S nextService() {    if (!hasNextService())        throw new NoSuchElementException();        //当前正在迭代的实现类名    String cn = nextName;    nextName = null;    Class<?> c = null;    try {      // 根据类路径加载class        c = Class.forName(cn, false, loader);    ……      // 实例化实现类,并且保存在providers中        S p = service.cast(c.newInstance());        providers.put(cn, p);        return p;    } catch (Throwable x) {        fail(service,             "Provider " + cn + " could not be instantiated",             x);    }    throw new Error();          // This cannot happen}

简言之,通过将实现类名保存在以服务接口命名的配置文件中,放置在META-INF/servicesServiceLoader会在先读取配置文件中实现类的名字,然后根据调用newInstance()方法对其进行实例化。简化的原理图如下:

Dubbo系列JDK SPI原理是什么

为什么不直接使用 JDK SPI

既然已经有了 JDP SPI 为什么还需要 Dubbo SPI呢?

技术的出现通常都是为了解决现有问题,通过之前的 demo,不难发现 JDK SPI 机制就存在以下一些问题:

  • 实现类会被全部遍历并且实例化,假如我们只需要使用其中的一个实现,这在实现类很多的情况下无疑是对机器资源巨大的浪费,

  • 无法按需获取实现类,不够灵活,我们需要遍历一遍所有实现类才能找到指定实现。

到此,关于“Dubbo系列JDK SPI原理是什么”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Dubbo系列JDK SPI原理是什么

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Dubbo系列JDK SPI原理是什么

这篇文章主要介绍“Dubbo系列JDK SPI原理是什么”,在日常操作中,相信很多人在Dubbo系列JDK SPI原理是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Dubbo系列JDK SPI原理是什么
2023-07-05

Dubbo 系列JDK SPI 原理解析

这篇文章主要为大家介绍了Dubbo 系列JDK SPI 原理解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-02-24

dubbo的SPI应用与原理是什么

dubbo的SPI应用与原理是什么,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。dubboSPI(Service Provider Interface)本质是将接口实现类的全限
2023-06-05

dubbo异步调用的原理是什么

Dubbo异步调用的原理是通过使用线程池来实现异步调用。首先,服务消费者发送请求给服务提供者,然后服务提供者将请求放入线程池中进行处理。线程池中的线程会并发地执行请求,并且不会阻塞服务提供者的主线程。一旦线程处理完请求,就会返回给服务消费者
2023-10-23

dubbo负载均衡轮询原理是什么

Dubbo的负载均衡轮询原理是指当多个服务提供者同时存在时,将请求按照顺序依次分发给每个服务提供者,每个提供者处理完一个请求后再依次处理下一个请求,循环往复,直到所有请求都被处理完毕。具体的实现原理如下:1. Dubbo通过维护一个服务提供
2023-10-09

dubbo负载均衡的实现原理是什么

Dubbo负载均衡的实现原理是通过在服务提供方和消费方之间选择合适的调用方式和节点来实现负载均衡。具体原理如下:服务提供方:在Dubbo中,服务提供方会将自身的服务注册到注册中心,并提供给消费方可以调用的服务节点列表。Dubbo支持多种负载
dubbo负载均衡的实现原理是什么
2024-04-09

java中JDK动态代理的原理是什么

这篇文章将为大家详细讲解有关java中JDK动态代理的原理是什么,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。Java可以用来干什么Java主要应用于:1. web开发;2. Android
2023-06-14

Zookeeper原理及在Dubbo中使用的方法是什么

这篇文章主要介绍了Zookeeper原理及在Dubbo中使用的方法是什么的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Zookeeper原理及在Dubbo中使用的方法是什么文章都会有所收获,下面我们一起来看看吧
2023-07-05

在Java中实现JDK动态代理的原理是什么

在Java中实现JDK动态代理的原理是什么?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。一、什么是代理?代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对
2023-05-31

Redis序列化的原理是什么

Redis序列化的原理是将数据结构转换为字节序列的过程,以便存储在内存中或通过网络传输。Redis支持多种序列化方式,包括字符串(String)、整数(Integer)、列表(List)、哈希表(Hash)、集合(Set)等。在Redis中
Redis序列化的原理是什么
2024-04-29

python双端队列的原理是什么

这篇文章主要介绍python双端队列的原理是什么,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!python的数据类型有哪些?python的数据类型:1. 数字类型,包括int(整型)、long(长整型)和float(
2023-06-14

log4j反序列化的原理是什么

log4j反序列化的原理是利用Java的反序列化机制,通过恶意构造的序列化数据,触发目标系统中的反序列化操作,从而导致可控的代码执行。具体来说,log4j中的LogEvent对象在序列化过程中,会将日志内容写入到一个Base64编码的字符串
2023-08-15

Java 阻塞队列的实现原理究竟是什么?(java阻塞队列的实现原理是什么)

在Java编程中,阻塞队列(BlockingQueue)是一种用于多线程环境下的线程安全的数据结构,它提供了一组用于添加、移除和检查元素的方法,同时在某些情况下会阻塞线程,直到满足特定的条件。阻塞队列的实现原理基于线程的等待和通知机制,下面我们来详细探讨Java阻塞队列的实现原理。
Java 阻塞队列的实现原理究竟是什么?(java阻塞队列的实现原理是什么)
Java2024-12-22

Java阻塞队列的实现原理是什么

本篇文章给大家分享的是有关Java阻塞队列的实现原理是什么,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。BlockingQueue接口提供了3个添加元素方法:add:添加元素到
2023-06-17

java延迟队列实现的原理是什么

Java延迟队列(DelayQueue)是一种特殊的优先级队列,它允许元素在特定的延迟时间之后才能被获取。延迟队列的实现原理主要依赖于两个关键点:1.使用优先级队列来存储延迟元素。2.使用延迟元素自身的延迟时间来实现元素的排序。具体实现原
java延迟队列实现的原理是什么
2024-03-01

经典算法系列之KMP算法的原理及功能是什么

KMP算法是一种字符串匹配算法,它的功能是在一个文本串中查找一个模式串的出现位置。KMP算法的原理是利用模式串内部的信息,即前缀和后缀的最长公共部分,来避免不必要的字符比较。通过预先计算出模式串的最长公共前缀和最长公共后缀数组,可以加速匹配
2023-09-22

linux系统的工作原理是什么

Linux系统的工作原理是基于Unix操作系统的设计理念和原则,它是一个开放源代码的操作系统。Linux系统的工作原理可以简单概括为以下几个方面:1. 内核:Linux系统的核心是其内核,它负责管理计算机的硬件资源和提供系统调用接口。内核是
2023-09-27

Linux文件系统的原理是什么

这篇文章将为大家详细讲解有关Linux文件系统的原理是什么,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。一、硬盘简介在介绍文件系统前,我们先来了解一下 硬盘。众所周知,内存在断电后数据就会丢
2023-06-15

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录