Life moves pretty fast,if you don't stop and look around once in a while,you will miss it

为什么需要SPI?

思考一个场景,我们封装了一套服务,别人通过引入我们写好的包,就可以使用这些接口API,完成相应的操作,这本来没有什么问题,但是会存在使用该服务的实体有不相同的业务需求,需要进一步的扩展,但是由于api是写好的,想要扩展并非那么的简单,如果存在这样子的场景,我们该怎么办?

可以使用Java 提供的SPI机制

什么是SPI?SPI和API的区别

  • SPI

    SPI的全称是Service Provider Interface,是Java提供的可用于第三方实现和扩展的机制,通过该机制,我们可以实现解耦,SPI接口方负责定义和提供默认实现,SPI调用方可以按需扩展

  • API的全称是Application Programming Interface,广义上来看就是接口,负责程序与程序之间进行协作的通道,就好比上面给的例子,【我们封装好了一套服务,通过API的形式提供给他人使用,别人使用API就能得到想要的】

所以他们俩的区别就很明显了,API的调用方只能依赖使用提供方的实现,SPI就如同可定制化的API一样,调用方可以自定义实现替换API提供的默认实现

来人,上点对抗

首先,我们新建一个空的maven项目,里边有两个包

  • spi-provider 从名字就可以得知是SPI的提供方
  • spi-user SPI的使用方

spi-provider

我们简单定义一个SPI接口,就叫ISpiTest,里边就一个saySomething方法,再提供一个默认的实现

public interface ISpiTest {
void saySomething();
} public class DefaultSpiImplementation implements ISpiTest{
@Override
public void saySomething() {
System.out.println("[默认实现] -> 今天也是充满希望的一天");
}
}

然后,模拟走流程,注意步骤4是我们之后要自定义替换的

/**
* 模拟一套流程
* @author Amg
* @date 2021/12/9
*/
public class TestUtils { public static void workFlow(ISpiTest s) { System.out.println("1、步骤1.......");
System.out.println("2、步骤2.......");
System.out.println("3、步骤3.......");
System.out.print("4、步骤4:");
s.saySomething();
System.out.println("5、步骤5.......");
}
}

接着,重点来了,我们需要在resources目录下面创建/META-INF/services文件夹然后以SPI接口的全限定类名作为名称创建一个文件

往文件里面填写实现类的全限定类名,如下

com.amg.spi.DefaultSpiImplementation

到此,spi-provider这个模块就完成了,至于之后要怎么使用,到spi-user模块中进一步说明

spi-user

首先,我们在pom文件中,引入spi-provider坐标依赖

​ 然后定义main方法,在main方法中调用在spi-provider中定义的SPI接口,此时采用的是默认的配置

可以注意到我们使用ServiceLoader这个类的load方法,传入SPI接口的字节码进行构造,我们在spi-provider中resources中给出了一个默认实现,但是我们是在spi-user中去调用的,ServiceLoader会自动读取META-INF下的配置文件,就算是跨jar包也是可以的

然后现在我们在spi-user中定义一个实现类,以及把他配置到META-INF下(需要注意,这个配置的全限定类名仍然需要是spi-provider中定义SPI接口的路径),来看看效果

spi-user下META-INF里边内容如下

com.amg.spiuser.service.impl.WantHamburger

可以发现,我们并没有改变任何的客户端代码,只是把配置文件进行了简单的修改,即可完成自定义实现,这就是使用SPI的魅力

思考一下,我们之前的流程是怎么做的

  • 首先定义了一个接口,面向接口编程嘛
  • 定义配置文件
  • 各个自定义的实现类,只需要按照规则重写配置文件即可

总结

通过这个流程,我们可以归纳为一句话,SPI是策略模式的一种体现,配合面向接口编程的思想以及必要的配置文件,即可完成定义和具体实现的解耦,而且是可定制化的API

SPI的优点有以下

  • 定制化实现接口
  • 解耦

SPI的缺点有以下

  • 通过观察ServiceLoader,可以发现并没有额外的加锁机制,所以会存在并发问题
  • 获取对应的实现类不够灵活,从上面例子可以看出,需要使用迭代器的方式获取
  • 需要知道接口的所有具体实现类,所以每次都要加载和实例化所有的实现类

实际中,SPI的使用还是很常见的,例如Dubbo和Spring Boot都为我们提供了一套SPI机制,只不过此SPI是在Java提供的SPI机制基础上进行改造而来,有兴趣的同学也可以去查下资料,增长增长

好啦,本期的文章就到这里,限于本人水平的问题,如有写得不对的地方,欢迎指出更正,谢谢!

Java SPI机制,你了解过吗?的更多相关文章

  1. Java spi机制浅谈

    最近看到公司的一些框架和之前看到的开源的一些框架的一些服务发现和接入都采用了java的spi机制. 所以简单的总结下java spi机制的思想. 我们系统里抽象的各个模块,往往有很多不同的实现方案,比 ...

  2. JDK源码解析之Java SPI机制

    1. spi 是什么 SPI全称Service Provider Interface,是Java提供的一套用来被第三方实现或者扩展的API,它可以用来启用框架扩展和替换组件. 系统设计的各个抽象,往往 ...

  3. 聊聊Java SPI机制

    一.Java SPI机制 SPI(Service Provider Interface)是JDK内置的服务发现机制,用在不同模块间通过接口调用服务,避免对具体服务服务接口具体实现类的耦合.比如JDBC ...

  4. Java SPI机制实战详解及源码分析

    背景介绍 提起SPI机制,可能很多人不太熟悉,它是由JDK直接提供的,全称为:Service Provider Interface.而在平时的使用过程中也很少遇到,但如果你阅读一些框架的源码时,会发现 ...

  5. 组件化框架设计之Java SPI机制(三)

    阿里P7移动互联网架构师进阶视频(每日更新中)免费学习请点击:https://space.bilibili.com/474380680 本篇文章将从深入理解java SPI机制来介绍组件化框架设计: ...

  6. Java SPI 机制实现解耦与本地化

    SPI 是 Java 提供的一种服务加载方式,全名为 Service Provider Interface,可以避免在 Java 代码中写死服务的提供者,而是通过 SPI 服务加载机制进行服务的注册和 ...

  7. Java SPI机制详解

    Java SPI机制详解 1.什么是SPI? SPI 全称为 (Service Provider Interface) ,是JDK内置的一种服务提供发现机制.SPI是一种动态替换发现的机制, 比如有个 ...

  8. java SPI机制

    1. SPI是Service Provider Interfaces的简称.根据Java的SPI规范,我们可以定义一个服务接口,具体的实现由对应的实现者去提供,即Service Provider(服务 ...

  9. Java SPI机制学习笔记

    最近在阅读框架源代码时,常常看到 SPI 的子包, 忍不住查了下: Service Provider Interface : 服务提供接口. JavaSPI 实际上是“基于接口的编程+策略模式+配置文 ...

  10. Java SPI机制简介

    SPI 简介 SPI 全称为 (Service Provider Interface) ,是JDK内置的一种服务提供发现机制. 目前有不少框架用它来做服务的扩展发现, 简单来说,它就是一种动态替换发现 ...

随机推荐

  1. 开发规范 - UML图

    依赖关系是用一套带箭头的虚线表示,他通常描述一个对象在运行期间会用到另一个对象的关系.如图为例码农只有在工作的时候才会用到 Mac 电脑,所以这种依赖关系是依赖于运行状态的.通常情况下是在程序里面通过 ...

  2. 基于hadoop_yarn的资源隔离配置

    目录 yarn的基本概念 scheduler 集群整体的资源定义 fair scheduler简介 配置demo 队列的资源限制 基于具体资源限制 基于权重资源限制 队列运行状态限制 基于用户和分组限 ...

  3. Node常用内置模块

    node内置的fs模块就是文件系统模块,负责读写文件 fs同时提供了同步异步的方法 'use strict'; var fs = require('fs'); fs.readFile('test.tx ...

  4. [atARC077F]SS

    (以下字符串下标从0开始,并定义$2s=s+s$) 考虑$f(S)$,即令$l=\max_{2i<|S|且S[0,i)=S[|S|-i,|S|)]}i$,则$f(S)=S+S[l,|S|-l)$ ...

  5. 深度学习,机器学习神器,白嫖免费GPU

    深度学习,机器学习神器,白嫖免费GPU! 最近在学习计算机视觉,自己的小本本没有那么高的算力,层级尝试过Google的Colab,以及移动云的GPU算力,都不算理想.如果数据集比较小,可以试试Cola ...

  6. Java-ASM框架学习-修改类的字节码

    Tips: ASM使用访问者模式,学会访问者模式再看ASM更加清晰 ClassReader 用于读取字节码,父类是Object 主要作用: 分析字节码里各部分内容,如版本.字段等等 配合其他Visit ...

  7. could not extract ResultSet

    使用jpa进行代码更新:update的时候发现代码出现了异常:could not extract ResultSet 在数据库看数据并没有更新,后发现更新操作需要加一个注解 注释:当设置nativeQ ...

  8. java内部类的调用方式

    public class DotThis { public class Inner{ public DotThis outer(){ return DotThis.this; }; } /* 1.第一 ...

  9. 『学了就忘』Linux文件系统管理 — 57、Linux文件系统介绍

    目录 1.了解硬盘结构(了解即可) (1)硬盘的逻辑结构 (2)硬盘接口 2.Linux文件系统介绍 (1)Linux文件系统的特性 (2)Linux常见文件系统 3.整理一下对文件系统的认识 在了解 ...

  10. RNA_seq 热图绘制

    若已经拿到表达矩阵exprSet 若差异较大,进行log缩小不同样本的差距 1.热图全体 1 ##加载包 2 library(pheatmap) 3 4 ##缩小表达量差距 5 exprSet < ...