iis服务器助手广告
返回顶部
首页 > 资讯 > 精选 >Dubbo系列JDK SPI原理是什么
  • 566
分享到

Dubbo系列JDK SPI原理是什么

2023-07-05 05:07:40 566人浏览 泡泡鱼
摘要

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

这篇文章主要介绍“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原理是什么”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!

--结束END--

本文标题: Dubbo系列JDK SPI原理是什么

本文链接: https://www.lsjlt.com/news/349960.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

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

下载Word文档
猜你喜欢
  • Dubbo系列JDK SPI原理是什么
    这篇文章主要介绍“Dubbo系列JDK SPI原理是什么”,在日常操作中,相信很多人在Dubbo系列JDK SPI原理是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Dubbo系列JD...
    99+
    2023-07-05
  • Dubbo 系列JDK SPI 原理解析
    目录正文为什么要使用SPI?什么是 SPIJDK SPI 机制JDK SPI原理为什么不直接使用 JDK SPI正文 上一篇文章讲到了如何去找到 Dubbo 源码的调试入口,如果你...
    99+
    2023-02-24
    Dubbo JDK SPI原理 Dubbo JDK SPI
  • dubbo的SPI应用与原理是什么
    dubbo的SPI应用与原理是什么,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。dubboSPI(Service Provider Interface)本质是将接口实现类的全限...
    99+
    2023-06-05
  • 什么是Dubbo SPI机制
    本篇内容主要讲解“什么是Dubbo SPI机制”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“什么是Dubbo SPI机制”吧!SPI是什么SPI是一种简称,全名叫 Service Provide...
    99+
    2023-06-15
  • dubbo异步调用的原理是什么
    Dubbo异步调用的原理是通过使用线程池来实现异步调用。首先,服务消费者发送请求给服务提供者,然后服务提供者将请求放入线程池中进行处...
    99+
    2023-10-23
    dubbo
  • dubbo负载均衡轮询原理是什么
    Dubbo的负载均衡轮询原理是指当多个服务提供者同时存在时,将请求按照顺序依次分发给每个服务提供者,每个提供者处理完一个请求后再依次...
    99+
    2023-10-09
    dubbo
  • java中JDK动态代理的原理是什么
    这篇文章将为大家详细讲解有关java中JDK动态代理的原理是什么,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。Java可以用来干什么Java主要应用于:1. web开发;2. Android...
    99+
    2023-06-14
  • dubbo负载均衡的实现原理是什么
    Dubbo负载均衡的实现原理是通过在服务提供方和消费方之间选择合适的调用方式和节点来实现负载均衡。具体原理如下: 服务提供方:在...
    99+
    2024-04-09
    dubbo
  • Zookeeper原理及在Dubbo中使用的方法是什么
    这篇文章主要介绍了Zookeeper原理及在Dubbo中使用的方法是什么的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Zookeeper原理及在Dubbo中使用的方法是什么文章都会有所收获,下面我们一起来看看吧...
    99+
    2023-07-05
  • Redis序列化的原理是什么
    Redis序列化的原理是将数据结构转换为字节序列的过程,以便存储在内存中或通过网络传输。Redis支持多种序列化方式,包括字符串(S...
    99+
    2024-04-29
    Redis
  • 在Java中实现JDK动态代理的原理是什么
    在Java中实现JDK动态代理的原理是什么?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。一、什么是代理?代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对...
    99+
    2023-05-31
    java jdk 动态代理
  • JDK,JRE,JVM的关系是什么
    今天小编给大家分享一下JDK,JRE,JVM的关系是什么的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。JDK是SUN公司发布...
    99+
    2023-06-17
  • log4j反序列化的原理是什么
    log4j反序列化的原理是利用Java的反序列化机制,通过恶意构造的序列化数据,触发目标系统中的反序列化操作,从而导致可控的代码执行...
    99+
    2023-08-15
    log4j
  • python双端队列的原理是什么
    这篇文章主要介绍python双端队列的原理是什么,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!python的数据类型有哪些python的数据类型:1. 数字类型,包括int(整型)、long(长整型)和float(浮...
    99+
    2023-06-14
  • dubbo将异常转换成RuntimeException的原因是什么
    本篇内容主要讲解“dubbo将异常转换成RuntimeException的原因是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“dubbo将异常转换成RuntimeException的原因是什...
    99+
    2023-07-05
  • php反序列化的工作原理是什么
    PHP反序列化是指将经过序列化后的数据重新转换为原来的数据结构的过程。在PHP中,可以使用serialize()函数将数据序列化为字...
    99+
    2024-04-02
  • java延迟队列实现的原理是什么
    Java延迟队列(DelayQueue)是一种特殊的优先级队列,它允许元素在特定的延迟时间之后才能被获取。延迟队列的实现原理主要依赖...
    99+
    2024-03-01
    java
  • Java阻塞队列的实现原理是什么
    本篇文章给大家分享的是有关Java阻塞队列的实现原理是什么,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。BlockingQueue接口提供了3个添加元素方法:add:添加元素到...
    99+
    2023-06-17
  • 经典算法系列之KMP算法的原理及功能是什么
    KMP算法是一种字符串匹配算法,它的功能是在一个文本串中查找一个模式串的出现位置。KMP算法的原理是利用模式串内部的信息,即前缀和后...
    99+
    2023-09-22
    KMP算法
  • Cacti系统的工作原理是什么
    Cacti 是一个网络图形化监控工具,其工作原理主要包括以下几个步骤: 数据采集:Cacti 通过 SNMP(Simple Ne...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作