服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Java教程 - Java 中经常被提到的 SPI 到底是什么?

Java 中经常被提到的 SPI 到底是什么?

2022-10-10 19:43Java极客技术鸭血粉丝Tang Java教程

Java​ 程序员在日常工作中经常会听到 SPI​,而且很多框架都使用了 SPI​ 的技术,那么问题来了,到底什么是 SPI 呢?今天阿粉就带大家好好了解一下 SPI。

Java 程序员在日常工作中经常会听到 SPI,而且很多框架都使用了 SPI 的技术,那么问题来了,到底什么是 SPI 呢?今天阿粉就带大家好好了解一下 SPI。

SPI 概念

SPI 全称是 Service Provider Interface,是一种 JDK 内置的动态加载实现扩展点的机制,通过 SPI 技术我们可以动态获取接口的实现类,不用自己来创建。

这里提到了接口和实现类,那么 SPI 技术上具体有哪些技术细节呢?

  • 接口:需要有一个功能接口;
  • 实现类:接口只是规范,具体的执行需要有实现类才行,所以不可缺少的需要有实现类;
  • 配置文件:要实现SPI 机制,必须有一个与接口同名的文件存放于类路径下面的 META-INF/services 文件夹中,并且文件中的每一行的内容都是一个实现类的全路径;
  • 类加载器ServiceLoader:JDK 内置的一个类加载器,用于加载配置文件中的实现类;

举个栗子

上面说了 SPI 的几个概念,接下来阿粉就通过一个栗子来带大家感受一下具体的用法。

第一步

创建一个接口,这里我们创建一个解压缩的接口,其中定义了压缩和解压的两个方法。

package com.example.demo.spi; public interface Compresser { byte[] compress(byte[] bytes); byte[] decompress(byte[] bytes); }

第二步

再写两个对应的实现类,分别是 GzipCompresser.java 和 WinRarCompresser.java 代码如下

package com.example.demo.spi.impl; import com.example.demo.spi.Compresser; import java.nio.charset.StandardCharsets; public class GzipCompresser implements Compresser { @Override
  public byte[] compress(byte[] bytes) { return"compress by Gzip".getBytes(StandardCharsets.UTF_8); } @Override
  public byte[] decompress(byte[] bytes) { return "decompress by Gzip".getBytes(StandardCharsets.UTF_8); } }
package com.example.demo.spi.impl; import com.example.demo.spi.Compresser; import java.nio.charset.StandardCharsets; public class WinRarCompresser implements Compresser { @Override
  public byte[] compress(byte[] bytes) { return "compress by WinRar".getBytes(StandardCharsets.UTF_8); } @Override
  public byte[] decompress(byte[] bytes) { return "decompress by WinRar".getBytes(StandardCharsets.UTF_8); } }

第三步

创建配置文件,我们接着在 resources 目录下创建一个名为 META-INF/services 的文件夹,在其中创建一个名为 com.example.demo.spi.Compresser 的文件,其中的内容如下:

com.example.demo.spi.impl.WinRarCompresser com.example.demo.spi.impl.GzipCompresser

注意该文件的名称必须是接口的全路径,文件里面的内容每一行都是一个实现类的全路径,多个实现类就写在多行里面,效果如下。

Java 中经常被提到的 SPI 到底是什么?

第四步

有了上面的接口,实现类和配置文件,接下来我们就可以使用 ServiceLoader 动态加载实现类,来实现 SPI 技术了,如下所示:

package com.example.demo; import com.example.demo.spi.Compresser; import java.nio.charset.StandardCharsets; import java.util.ServiceLoader; public class TestSPI { public static void main(String[] args) { ServiceLoader<Compresser> compressers = ServiceLoader.load(Compresser.class); for (Compresser compresser : compressers) { System.out.println(compresser.getClass()); } } }

运行的结果如下

Java 中经常被提到的 SPI 到底是什么?

可以看到我们正常的获取到了接口的实现类,并且可以直接使用实现类的解压缩方法。

原理

知道了如何使用 SPI 接下来我们来研究一下是如何实现的,通过上面的测试我们可以看到,核心的逻辑是 ServiceLoader.load() 方法,这个方法有点类似于 Spring 中的根据接口获取所有实现类一样。

点开 ServiceLoader 我们可以看到有一个常量 PREFIX,如下所示,这也是为什么我们必须在这个路径下面创建配置文件,因为 JDK 代码里面会从这个路径里面去读取我们的文件。

Java 中经常被提到的 SPI 到底是什么?

同时又因为在读取文件的时候使用了 class 的路径名称,因为我们使用 load 方法的时候只会传递一个 class,所以我们的文件名也必须是接口的全路径。

Java 中经常被提到的 SPI 到底是什么?

通过 load 方法我们可以看到底层构造了一个 java.util.ServiceLoader.LazyIterator 迭代器。

Java 中经常被提到的 SPI 到底是什么?

在迭代器中的 parse 方法中,就获取了配置文件中的实现类名称集合,然后在通过反射创建出具体的实现类对象存放到 LinkedHashMap providers = new LinkedHashMap<>(); 中。

Java 中经常被提到的 SPI 到底是什么?

常用的框架

SPI 技术的使用非常广泛,比如在 Dubble,不过 Dubble 中的 SPI 有经过改造的,还有我们很常见的数据库的驱动中也使用了 SPI,感兴趣的小伙伴可以去翻翻看,还有 SLF4J 用来加载不同提供商的日志实现类以及 Spring 框架等。

优缺点

前面介绍了 SPI 的原理和使用,那 SPI 有什么优缺点呢?

优点

优点当然是解耦,服务方只要定义好接口规范就好了,具体的实现可以由不同的 Jar 进行实现,只要按照规范实现功能就可以被直接拿来使用,在某些场合会被进行热插拔使用,实现了解耦的功能。

缺点

一个很明显的缺点那就是做不到按需加载,通过源码我们看到了是会将所有的实现类都进行创建的,这种做法会降低性能,如果某些实现类实现很耗时了话将影响加载时间。同时实现类的命名也没有规范,让使用者不方便引用。

原文地址:https://mp.weixin.qq.com/s/t92OtlMD_dwk1ldCVNvHBA

延伸 · 阅读

精彩推荐
  • Java教程java基础二叉搜索树图文详解

    java基础二叉搜索树图文详解

    二叉树是一种非常重要的数据结构,它同时具有数组和链表各自的特点,下面这篇文章主要给大家介绍了关于java基础二叉搜索树的相关资料,文中通过实例代码...

    Dark And Grey6072022-09-08
  • Java教程Spring与bean有关的生命周期示例详解

    Spring与bean有关的生命周期示例详解

    这篇文章主要给大家介绍了关于Spring与bean有关的生命周期的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者使用Spring具有一定的参考学习...

    eaglelihh5112020-07-27
  • Java教程Spring Boot修改启动端口的方法

    Spring Boot修改启动端口的方法

    下面小编就为大家带来一篇Spring Boot修改启动端口的方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...

    Java教程网3812020-12-03
  • Java教程浅谈cookie 和session 的区别

    浅谈cookie 和session 的区别

    下面小编就为大家带来一篇浅谈cookie 和session 的区别。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧 ...

    java教程网4222020-05-14
  • Java教程java 中MyBatis注解映射的实例详解

    java 中MyBatis注解映射的实例详解

    这篇文章主要介绍了java 中MyBatis注解映射的实例详解的相关资料,这里提供实例帮助大家理解这部分内容,需要的朋友可以参考下...

    yunlian06216012020-12-28
  • Java教程Spring Security 表单登录功能的实现方法

    Spring Security 表单登录功能的实现方法

    这篇文章主要介绍了Spring Security 表单登录,本文将构建在之前简单的 Spring MVC示例 之上,因为这是设置Web应用程序和登录机制的必不可少的。需要的朋友可...

    锅外的大佬3082019-06-30
  • Java教程Spring IOC源码之bean的注册过程讲解

    Spring IOC源码之bean的注册过程讲解

    这篇文章主要介绍了Spring IOC源码之bean的注册过程讲解,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...

    hansmall11492021-12-29
  • Java教程Java中Random简介_动力节点Java学院整理

    Java中Random简介_动力节点Java学院整理

    本文详细给大家介绍了Java中Random简介相关知识,非常不错,具有参考借鉴价值,需要的朋友参考下吧...

    动力节点5242020-11-08