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. IntelliJ IDEA竟然出了可以在云端编码的功能?

    前言 自从我用了正版的IntelliJ IDEA后,基本上都是与时俱进,出一个新版本就立马更新,这也能能让我体验到最新最快的功能. 最近在闲逛Jetbrains的官网时,看到了最新的2021.3EAP ...

  2. Java 中控制执行流程

    if-else 非常常用的流程控制非 if-else 莫属了,其中 else 是可选的,if 有两种使用方式 其一: if (Boolean-expression) { statement; } 其二 ...

  3. c语言1左移32位(1<<32)是多少,左移-1位呢

    C语言中 << 是逻辑移位,不是循环移位.1 左移 32 位后为 0,左移 -1 位实际是左移 255 位(互补),当然也是0.这种问题可以写一段小程序,单步执行,看一下每一步的结果.先说 ...

  4. [cf1340D]Nastya and Time Machine

    记$deg_{i}$为$i$的度数,简单分类讨论可得答案下限为$\max_{i=1}^{n}deg_{i}$ 另一方面,此下限是可以取到的,构造方法较多,这里给一个巧妙一些的做法-- 对其以dfs(儿 ...

  5. [loj6518]序列

    参考ExtremeSpanningTrees,考虑优化整体二分时求$g_{i}\in \{w_{mid},w_{mid+1}\}$的最优解 首先题目有一个条件似乎没有写出来,是保证$l\le k\le ...

  6. [bzoj4652]循环之美

    对于一个分数x/y(x和y互素),在k进制下为纯循环当且仅当y和k互素证明:任意一个分数都可以写成0.abbbbbbbb的形式(不妨假设a尽量短),设a的位数为l1,b的位数为l2,那么原分数即$\f ...

  7. 【JQuery】(1)JQuery基础

    JQuery基础 2019-11-02  21:11:17  by冲冲 1.jQuery简介 jQuery:轻量级."写的少,做的多".JavaScript函数库. 2.jQuer ...

  8. redis可以设置过期key回调实现延时队列

    <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring- ...

  9. CF1474E What Is It?

    考虑我们一定是每次构造最长的交换对. 那么就是\((1,n),(1,n - 1),...(1,\frac{n}{2} + 1)(\frac{n}{2},n)....(1,n)\)形式.

  10. HDU 6984 - Tree Planting(数据分治+状压 dp)

    题面传送门 傻逼卡常屑题/bs/bs,大概现场过得人比较少的原因就是它比较卡常罢(Fog 首先对于这样的题我们很难直接维护,不过注意到这个 \(n=300\) 给得很灵性,\(k\) 比较小和 \(k ...