Java SPI 与 Dubbo SPI 有什么区别?

分手后的思念是犯贱 2022-10-07 05:53 269阅读 0赞

作者:废物大师兄
来源:www.cnblogs.com/cjsblog/p/14346766.html

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

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

1、Java SPI

核心类:java.util.ServiceLoader

20210616144545434.png

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

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

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

首先,定义一个接口Car

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

两个实现类

ToyotaCar.java

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

HondaCar.java

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

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

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

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

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

20210616144545822.png

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

2021061614454643.png

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

20210616144546340.png

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

20210616144546606.png

20210616144546995.png

20210616144547433.png

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. import org.apache.dubbo.common.extension.SPI;
  3. @SPI
  4. public interface Car {
  5. void run();
  6. }

两个实现类不变

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

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

编写测试类:

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

下面跟一下代码

20210616144547704.png

2021061614454872.png

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

20210616144548357.png

20210616144548747.png

2021061614454977.png

20210616144549520.png

2021061614455011.png

20210616144550791.png

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

2021061614455169.png

20210616144551380.png

20210616144551697.png

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

202106161445529.png

20210616144552214.png

20210616144552495.png

20210616144552801.png

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

20210616144553212.png

跟之前的ServiceLoader如出一辙

20210616144553578.png

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

20210616144553969.png

最后,大家关注公众号Java技术栈,在后台回复:面试,可以获取我整理的 Java、Dubbo 系列面试题和答案,非常齐全。

近期热文推荐:

1.1,000+ 道 Java面试题及答案整理(2021最新版)

2.终于靠开源项目弄到 IntelliJ IDEA 激活码了,真香!

3.阿里 Mock 工具正式开源,干掉市面上所有 Mock 工具!

4.Spring Cloud 2020.0.0 正式发布,全新颠覆性版本!

5.《Java开发手册(嵩山版)》最新发布,速速下载!

觉得不错,别忘了随手点赞+转发哦!

发表评论

表情:
评论列表 (有 0 条评论,269人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Dubbo SPI

    前言 前面已经讲过[JDK SPI][], dubbo SPI主要基于JDK SPI机制,进行了增强,实现可扩展 为什么不直接用JDK的SPI呢,主要有哪些改变