SPI(Service Provider Interface)是JDK内置的一种服务提供发现机制。本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。这样可以在运行时,动态为接口替换实现类。

在Java中SPI是被用来设计给服务提供商做插件使用的。基于策略模式来实现动态加载的机制。我们在程序只定义一个接口,具体的实现交个不同的服务提供者;在程序启动的时候,读取配置文件,由配置确定要调用哪一个实现。有很多组件的实现,如日志、数据库访问等都是采用这样的方式,最常用的就是 JDBC 驱动。

1.  Java SPI

核心类:java.util.ServiceLoader

服务是一组众所周知的接口和(通常是抽象的)类。服务提供者是服务的特定实现。提供者中的类通常实现接口,并子类化服务本身中定义的类。服务提供者可以以扩展的形式安装在Java平台的实现中,即放置在任何常见扩展目录中的jar文件。提供程序也可以通过将它们添加到应用程序的类路径或其他特定于平台的方法来提供。

通过在资源目录META-INF/services中放置一个提供程序配置文件来识别服务提供程序。文件名是服务类型的完全限定二进制名称。该文件包含具体提供程序类的完全限定二进制名的列表,每行一个。每个名称周围的空格和制表符以及空白行将被忽略。注释字符是'#';在每一行中,第一个注释字符之后的所有字符都将被忽略。文件必须用UTF-8编码。

按照上面的方法,我们来写个例子试一下

首先,定义一个接口Car

  1. package org.example;
  2.  
  3. public interface Car {
  4. void run();
  5. }

两个实现类

ToyotaCar.java

  1. package org.example;
  2.  
  3. public class ToyotaCar implements Car {
  4. @Override
  5. public void run() {
  6. System.out.println("Toyota");
  7. }
  8. }

HondaCar.java

  1. package org.example;
  2.  
  3. public class HondaCar implements Car {
  4. @Override
  5. public void run() {
  6. System.out.println("Honda");
  7. }
  8. }

在META-INF/services下创建一个名为org.example.Car的文本文件

  1. org.example.ToyotaCar
  2. org.example.HondaCar

最后,写个测试类运行看一下效果

  1. package org.example;
  2.  
  3. import java.util.ServiceLoader;
  4.  
  5. public class App
  6. {
  7. public static void main( String[] args )
  8. {
  9. ServiceLoader<Car> serviceLoader = ServiceLoader.load(Car.class);
  10. serviceLoader.forEach(x->x.run());
  11. }
  12. }

跟一下ServiceLoader的代码,看看是怎么找到服务实现的

用当前线程的类加载器加载

接口和类加载器都有了,万事俱备只欠东风

Java SPI 不足之处:

  • 不能按需加载。Java SPI在加载扩展点的时候,会一次性加载所有可用的扩展点,很多是不需要的,会浪费系统资源
  • 获取某个实现类的方式不够灵活,只能通过 Iterator 形式获取,不能根据某个参数来获取对应的实现类
  • 不支持AOP与IOC
  • 如果扩展点加载失败,会导致调用方报错,导致追踪问题很困难

2.  Dubbo SPI

Dubbo重新实现了一套功能更强的SPI机制, 支持了AOP与依赖注入,并且利用缓存提高加载实现类的性能,同时支持实现类的灵活获取。

  1. <dependency>
  2. <groupId>org.apache.dubbo</groupId>
  3. <artifactId>dubbo</artifactId>
  4. <version>2.7.8</version>
  5. </dependency>

核心类:org.apache.dubbo.common.extension.ExtensionLoader

先来了解一下@SPI注解,@SPI是用来标记接口是一个可扩展的接口

改造一下前面的例子,在Car接口上加上@SPI注解

  1. package org.example;
  2.  
  3. import org.apache.dubbo.common.extension.SPI;
  4.  
  5. @SPI
  6. public interface Car {
  7. void run();
  8. }

两个实现类不变

在META-INF/dubbo目录下创建名为org.example.Car的文本文件,内容如下(键值对形式):

  1. toyota=org.example.ToyotaCar
  2. honda=org.example.HondaCar

编写测试类

  1. package org.example;
  2.  
  3. import org.apache.dubbo.common.extension.ExtensionLoader;
  4.  
  5. import java.util.ServiceLoader;
  6.  
  7. public class App
  8. {
  9. public static void main( String[] args )
  10. {
  11. // Java SPI
  12. ServiceLoader<Car> serviceLoader = ServiceLoader.load(Car.class);
  13. serviceLoader.forEach(x->x.run());
  14.  
  15. // Dubbo SPI
  16. ExtensionLoader<Car> extensionLoader = ExtensionLoader.getExtensionLoader(Car.class);
  17. Car car = extensionLoader.getExtension("honda");
  18. car.run();
  19. }
  20. }

下面跟一下代码

如果缓存Map中有,直接返回,没有则加载完以后放进去

加载策略到底是怎样的呢?

到这里就有点明白了,又看到了熟悉的ServiceLoad.load(),这不是刚才讲的Java SPI嘛

回到之前策略那个地方,将策略按顺序排列,依次遍历所有的策略来加载。就是在那三个目录下查找指定的文件,并读取其中的内容

跟之前的ServiceLoader如出一辙

遇到@Adaptive标注的就缓存起来

下课

Java SPI 与 Dubbo SPI的更多相关文章

  1. Dubbo 扩展点加载机制:从 Java SPI 到 Dubbo SPI

    SPI 全称为 Service Provider Interface,是一种服务发现机制.当程序运行调用接口时,会根据配置文件或默认规则信息加载对应的实现类.所以在程序中并没有直接指定使用接口的哪个实 ...

  2. dubbo源码分析--dubbo spi解析

    1. 什么叫SPI? 简单总结就是一种使用类名字符串来动态实例化java类的方式,也就是反射. 2. java SPI与Dubbo SPI有什么区别 (此图来自网上,我没有刻意去截图) 然后在这个文件 ...

  3. Dubbo SPI源码解析①

    目录 0.Java SPI示例 1.Dubbo SPI示例 2.Dubbo SPI源码分析 ​ SPI英文全称为Service Provider Interface.它的作用就是将接口实现类的全限定名 ...

  4. Dubbo SPI 和 Java SPI 区别?

    JDK SPI JDK 标准的 SPI 会一次性加载所有的扩展实现,如果有的扩展吃实话很耗时,但 也没用上,很浪费资源. 所以只希望加载某个的实现,就不现实了 DUBBO SPI 1,对 Dubbo ...

  5. 2. Dubbo原理解析-Dubbo内核实现之基于SPI思想Dubbo内核实现(转)

    转载自  斩秋的专栏  http://blog.csdn.net/quhongwei_zhanqiu/article/details/41577159 SPI接口定义 定义了@SPI注解 public ...

  6. dubbo SPI设计

    SPI 全称为 Service Provider Interface,是一种服务发现机制.SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类.这样可以在运行时, ...

  7. 【Dubbo源码阅读系列】之 Dubbo SPI 机制

    最近抽空开始了 Dubbo 源码的阅读之旅,希望可以通过写文章的方式记录和分享自己对 Dubbo 的理解.如果在本文出现一些纰漏或者错误之处,也希望大家不吝指出. Dubbo SPI 介绍 Java ...

  8. 理解 Dubbo SPI 扩展机制

    写在前面 最近接触了 gRPC 体会到虽然众多 RPC 框架各有各的特点但是他们提供的特性和功能有很多的相似之处 , 这就说明他们面对同样的分布式系统带来的问题.从 2016 年左右开始接触到 dub ...

  9. 【DUBBO】 Dubbo原理解析-Dubbo内核实现之基于SPI思想Dubbo内核实现

    转载:http://blog.csdn.net/quhongwei_zhanqiu/article/details/41577235 SPI接口定义 定义了@SPI注解 public @interfa ...

随机推荐

  1. 高端面试必备:一个Java对象占用多大内存

    这个问题一般会出现在稍微高端一点的 Java 面试环节.要求面试者不仅对 Java 基础知识熟悉,更重要的是要了解内存模型. Java 对象模型 HotSpot JVM 使用名为 oops (Ordi ...

  2. 转载--对batch normalization的理解

    转载的大神的: https://www.cnblogs.com/guoyaohua/p/8724433.html 上边这个应该是抄的下边这个原文,但是上边的有重点标记 https://blog.csd ...

  3. WebService的开发手段

    一.WebService的开发手段 目前有关webService的开发手段有2种 1.JDK开发(jdk必须是1.6及以上版本,因为jdk是在1.6版本中引入并支持webservice开发的); 2. ...

  4. 图解HTTP权威指南(四)| 代理

    作者简介 李先生(Lemon),高级运维工程师(自称),SRE专家(目标),梦想在35岁买一辆保时捷.喜欢钻研底层技术,认为底层基础才是王道.一切新技术都离不开操作系统(CPU.内存.磁盘).网络等. ...

  5. FFT原理及C++与MATLAB混合编程详细介绍

    一:FFT原理 1.1 DFT计算 在一个周期内的离散傅里叶级数(DFS)变换定义为离散傅里叶变换(DFT). \[\begin{cases} X(k) = \sum_{n=0}^{N-1}x(n)W ...

  6. LeetCode 124 二叉树中最大路径和

    题目: 给定一个非空二叉树,返回其最大路径和. 本题中,路径被定义为一条从树中任意节点出发,达到任意节点的序列.该路径至少包含一个节点,且不一定经过根节点. 思路:递归 分为三部分,根节点,左子树,右 ...

  7. [Skill]加速npm与yarn还原

    npm源 使用cnpm alias cnpm="npm --registry=https://registry.npm.taobao.org //或者 npm install -g cnpm ...

  8. 学习记录——使用PHP实现数据增删查改等基本功能(前后端分离)

    萌新初次学习服务器端语言,分享学习经验 实现功能:1.显示数据表    2.对数据进行分页    3.对数据进行增删查改 由于本萌新采用前后端完全分离方案,所以数据传输用的ajax,为了提高代码的复用 ...

  9. Nginx安装步骤及本地浏览器不通解决方案,Nginx在Linux发布项目,Tomcat 与本地浏览器不通解决方案

    Nginx安装步骤及本地浏览器不通解决方案 1.将安装包放到usr/local文件夹下 2..进入local目录,解压 tar -zxvf nginx-1.17.5.tar.gz 3.进入 nginx ...

  10. Python基础语法5-控制流语句