SPI机制

2023-10-27

1、概念

        spi全称为 (Service Provider Interface),是JDK内置的一种服务提供发现机制。SPI是一种动态替换发现的机制,一种解耦非常优秀的思想。它是jdk提供给“服务提供厂商”或者“插件开发者”使用的接口,是一种扩展机制。

2、用途

        在面向对象的设计中,模块之间我们一般会采取面向接口编程的方式,而在实际编程过程过程中,API的实现是封装在jar中,当我们想要换一种实现方法时,还要生成新的jar替换以前的实现类。而通过jdk的SPI机制就可以实现,首先不需要修改原来作为接口的jar的情况下,将原来实现的那个jar替换为另外一种实现的jar即可。

        总结一下SPI的思想:在系统的各个模块中,往往有不同的实现方案,例如日志模块的方案、xml解析的方案等,为了在装载模块的时候不具体指明实现类,我们需要一种服务发现机制,java spi就提供这样一种机制。有点类似于IoC的思想,将服务装配的控制权移到程序之外,在模块化设计时尤其重要。

示例:重写第三方jar中SPRING BEAN的方式_xixingzhe2的博客-CSDN博客

3、优缺点

3.1 优点

  • 为了解耦,将接口和具体实现分离开来。
  • 提高框架的扩展性。应用程序可以根据实际业务情况启用框架扩展或替换框架组件,避免接口和实现都写在一起,调用方无权选择使用具体的实现类。

3.2 缺点

  • 虽然ServiceLoader也算是使用的延迟加载,但是基本只能通过遍历全部获取,也就是接口的实现类全部加载并实例化一遍。如果你并不想用某些实现类,它也被加载并实例化了,这就造成了浪费。获取某个实现类的方式不够灵活,只能通过Iterator形式获取,不能根据某个参数来获取对应的实现类。
  • 多个并发多线程使用ServiceLoader类的实例是不安全的。

4、规范

        定义服务的通用接口,针对通用的服务接口,提供具体的实现类。

  1. 在jar包(服务提供者)的META-INF/services/目录中,新建一个文件,文件名为SPI接口的"全限定名"(如:com.ybw.spi.service.DemoService)。 文件内容为该接口的具体实现类的"全限定名"(如:com.ybw.spi.service.impl.DemoOneServiceImpl)。
  2. 将spi所在jar放在主程序的classpath中。
  3. 服务调用方使用java.util.ServiceLoader去动态加载具体的实现类到JVM中。

5、SPI示例

代码结构

模块依赖关系

 5.1 spi-service

定义接口

package com.ybw.spi.service;


/**
 * Demo service. Implements should be use SPI.
 *
 * @author ybw
 * @version V1.0
 * @className DemoService
 * @date 2022/6/29
 **/
public interface DemoService {

    String sayHello();

}

 5.2 spi-service-impl-one

实现一

package com.ybw.spi.service.impl;


import com.ybw.spi.service.DemoService;

/**
 * Implement for DemoService
 *
 * @author ybw
 * @version V1.0
 * @className DemoOneServiceImpl
 * @date 2022/6/29
 **/
public class DemoOneServiceImpl implements DemoService {

    @Override
    public String sayHello() {
        return "hello world one";
    }
}

META-INF/services/下创建文件com.ybw.spi.service.DemoService,内容为

com.ybw.spi.service.impl.DemoOneServiceImpl

 5.3 spi-service-impl-two

实现二

package com.ybw.spi.service.impl;


import com.ybw.spi.service.DemoService;

/**
 * Implement for DemoService
 *
 * @author ybw
 * @version V1.0
 * @className DemoOneServiceImpl
 * @date 2022/6/29
 **/
public class DemoTwoServiceImpl implements DemoService {

    @Override
    public String sayHello() {
        return "hello world two";
    }
}

META-INF/services/下创建文件com.ybw.spi.service.DemoService,内容为

com.ybw.spi.service.impl.DemoTwoServiceImpl

5.4 spi-execute

执行模块

package com.ybw.spi.test;

import com.ybw.spi.service.DemoService;
import com.ybw.spi.service.impl.DemoOneServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;

import java.util.ServiceLoader;

/**
 * 加载使用
 *
 * @author ybw
 * @version V1.0
 * @className SpiTest
 * @date 2022/6/29
 **/
@Slf4j
public class SpiTest {


    /**
     * @methodName: spiTest
     * @return: void
     * @author: ybw
     * @date: 2022/6/29
     **/
    @Test
    public void spiTest() {
        ServiceLoader<DemoService> demoServices = ServiceLoader.load(DemoService.class);
        demoServices.forEach(demoService -> {
            log.info(demoService.getClass().getName());
            log.info("ClassLoader:{}",demoService.getClass().getClassLoader());
            log.info(demoService.sayHello());
        });
    }
}

引入依赖spi-service-impl-one

 输入日志

[INFO ] 2022-06-29 16:12:58.135 [main] com.ybw.spi.test.SpiTest - com.ybw.spi.service.impl.DemoOneServiceImpl
[INFO ] 2022-06-29 16:12:58.146 [main] com.ybw.spi.test.SpiTest - ClassLoader:sun.misc.Launcher$AppClassLoader@18b4aac2
[INFO ] 2022-06-29 16:12:58.151 [main] com.ybw.spi.test.SpiTest - hello world one

引入的实现模块不同,打印的日志也会不同。

5.5 代码地址

https://gitee.com/xixingzhe2/share/tree/master/spi/spi-jdk-demo

6、spring spi

        在springboot的自动装配过程中,最终会加载META-INF/spring.factories文件,而加载的过程是由SpringFactoriesLoader加载的。

        从CLASSPATH下的每个Jar包中搜寻所有META-INF/spring.factories配置文件,然后将解析properties文件,找到指定名称的配置后返回。注意的是,其实这里不仅仅是会去ClassPath路径下查找,会扫描所有路径下的Jar包,只不过这个文件只会在Classpath下的jar包中。

可以看另一边文章:spring boot 自定义starter_xixingzhe2的博客-CSDN博客

源代码:https://gitee.com/xixingzhe2/share/tree/master/spring/spring-demo-starter-parent

7、jdk与spring spi区别

jdk spring
加载类不同 JDK使用的工具类是ServiceLoader Spring中使用的类是SpringFactoriesLoader,在 org.springframework.core.io.support包中
文件路径不同 jdk配置放在META-INF/services/目录中 spring配置放在 META-INF/spring.factories中

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

SPI机制 的相关文章

  • 无论线程如何,对象是否总是能看到其最新的内部状态?

    假设我有一个带有简单整数计数变量的可运行对象 每次可运行对象运行时该变量都会递增 该对象的一个 实例被提交以在计划的执行程序服务中定期运行 class Counter implements Runnable private int coun
  • 了解 netty 通道缓冲区和水印

    我正在尝试了解网络缓冲区和水印 作为一个测试用例 我有一个 netty 服务器 它向客户端写入数据 客户端被阻止 基本上每次读取之间有 10 秒的睡眠时间 在正常 I O 下 如果接收方被阻塞 TCP 发送方将受到限制 由于流量控制 发送速
  • Hashset - 创建 Set 后使对象相同

    如果我们在 HashSet 中添加两个不同的对象 可变的 然后通过调用 setter 更改对象的值 使它们相同 则大小仍然是 hashSet 的 2 我无法理解其原因 public static void main String args
  • Java 小程序在 Mac 上闪烁

    这个问题很奇怪 问题并非在每个平台上都会发生 我在使用 MacOSX 的 Google Chrome 中出现了这种情况 但在 Safari 中却没有出现这种情况 对于使用 Windows 的朋友来说 在 Google Chrome 上运行得
  • 如何准确判断 double 是否为整数? [复制]

    这个问题在这里已经有答案了 具体来说 在 Java 中 我如何确定double是一个整数 为了澄清 我想知道如何确定 double 实际上不包含任何分数或小数 我主要关心的是浮点数的性质 我想到的方法 以及我通过谷歌找到的方法 基本上遵循以
  • 在 Eclipse 3.5 上安装旧版 TestNG 插件时出现问题

    我正在尝试在 eclipse 3 5 上安装 TestNG 5 11 并获得以下信息 eclipse buildId unknown java version 1 6 0 19 java vendor Sun Microsystems In
  • 使用 kryo 注册课程的策略

    我最近发现了 kryonet 库 它非常棒并且非常适合我的需求 然而 我遇到的一个问题是制定一种好的策略来注册所有可以转移的类 我知道我可以在每个对象中编写一个静态方法 该方法将返回它使用的所有类的列表 但我真的不想这样做 为了我自己的时间
  • 如何避免Eclipse在将类名放在注释中时导入类,以便checkstyle稍后不会抱怨?

    有时我将类名放在方法或类的注释中只是为了引用 但是 Eclipse 会自动执行导入并在文件中留下导入语句 这会导致稍后出现 未使用的导入 检查样式错误 当我在注释中输入类名时 是否可以更改一些配置以避免 Eclipse 自动导入 人们不同意
  • Spring 5.0.3 依赖问题

    升级到 spring 5 0 3 后我遇到以下问题 09 25 29 141 ERROR org jboss msc service fail ServerService Thread Pool 175 MSC000001 Failed t
  • Spring HATEOAS 和 HAL:更改 _embedded 中的数组名称

    我正在尝试使用 Spring HATEOAS 构建符合 HAL 的 REST API 经过一番摆弄后我终于开始工作了mostly正如预期的那样 示例 输出现在看起来像这样 links self href http localhost 808
  • 但是创建静态实用方法不应该被过度使用吗?如何避免呢? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 随着时间的推移 java项目中引入了许多实用方法来完成更复杂和简单的任务 当使用静态方法时 我们在代码中引入了紧密耦合 这使得我们的代
  • for循环中更新JLabel的问题

    我的程序的想法是从之前在其他 JFrame 中保存的列表中选择一个名称 我想在标签中一个接一个地打印所有名称 它们之间有很小的延迟 然后停在其中一个名称上 问题是lbl setText String 如果有多个则不起作用setText co
  • 从 CLI 部署 Maven 项目?

    在 IDE 中构建并运行良好 cd home thufir NetBeansProjects HelloMaven JAVA HOME usr lib jvm java 8 openjdk amd64 home thufir local s
  • Time.valueOf 方法返回错误值

    我使用 Time valueOf 方法将字符串 09 00 00 转换为 Time 对象 如下所示 Time valueOf LocalTime parse 09 00 00 当我调用 getTime 来显示我得到的值时 28800000
  • 使用 Guava Ordering 对对象列表进行多条件排序

    我有一个类无法实现可比较 但需要根据 2 个字段进行排序 我怎样才能用番石榴实现这一目标 假设班级是 class X String stringValue java util Date dateValue 我有一个清单 List
  • 了解Kafka流groupBy和window

    我无法理解 kafka 流中的 groupBy groupById 和窗口的概念 我的目标是聚合一段时间内 例如 5 秒 的流数据 我的流数据看起来像 value 0 time 1533875665509 value 10 time 153
  • JPA Web 应用程序管理策略

    我们目前正在开发一个 J2EE Web 应用程序 使用 JPA 作为我们的数据访问层 我们目前正在研究几种不同的策略来在我们的应用程序中利用缓存 Create an EntityManager per request 在请求范围内获取缓存
  • Hibernate 标准接受 %% 值

    我正在使用下面的 Hibernate 代码来过滤workFlowName crt add Restrictions like workFlowName workFlow MatchMode ANYWHERE crt is the crite
  • 无法连接到docker中的elasticsearch容器

    我正在尝试使用 docker 的官方 elasticsearch 镜像 我遵循了本指南 https www elastic co guide en elasticsearch reference current docker html但是当
  • Android ClassNotFoundException:在路径上找不到类

    10 22 15 29 40 897 E AndroidRuntime 2561 FATAL EXCEPTION main 10 22 15 29 40 897 E AndroidRuntime 2561 java lang Runtime

随机推荐

  • nodejs 下使用js封装console.log方法输出自定义的彩色字体,遇到Octal escape sequences are not allowed in template strings

    阳光明媚 在家敲javascript的代码 控制台里清一色的黑底白字 我console log出来的文字也是黑底白字淹没在茫茫字母海中 所以我想封装一下log方法 能自己定义字体还有背景的颜色 然后就出现了这个报错 Octal escape
  • OpenCV 图像旋转

    1 RotateArbitrarilyAngle Mat src Mat dst float angle 2 3 float radian float angle 180 0 CV PI 4 5 填充图像 6 int maxBorder i
  • SQL 题目(一)

    url http www cnblogs com finejob articles 974900 html 其他题目 url 为管理岗位业务培训信息 建立3个表 S S SN SD SA S SN SD SA 分别代表学号 学员姓名 所属单
  • 【轩说AI】无监督特征学习——主成分分析、稀疏编码、自编码器

    文章目录 无监督学习 无监督特征学习 主成分分析 稀疏编码 自编码器 主成分分析PCA 理解什么是PCA 有什么用处 稀疏编码 编码 交替优化 自编码器 Auto Encoder 稀疏自编码器 无监督学习 无监督特征学习 主成分分析 稀疏编
  • FortiGate下用户访问特定网址不通

    1 现状 如图 上网行为管理和防火墙控制用户的数据访问 到外网的出口链路有2条 联通的和电信的 其中联通的优先级较高 2 现象 用户访问www xxxxbbs com不通 3 分析 在上网行为管理上做源和目前地址排除 发现仍不能访问排除了上
  • ERP为什么在生产制造环节应用效果不理想?

    ERP在生产制造环节应用效果不理想的根本原因是ERP负责的是宏观管控 而生产制造环节应该使用MES来进行精细化管理 下面就详细讨论下 生产制造环节发生在工厂 工厂可以没有ERP 但如果要用系统 必定是MES系统 ERP是为企业服务的 MES
  • 学习汇编语言MASM笔记1

    关于源程序书写格式 太基础的略去 只记录一些我还不熟悉的内容 参考https wenku baidu com view 7ca70e0ef342336c1eb91a37f111f18582d00c5d html 其他网页的参考以挂上相关连接
  • Flutter 踩坑TabBar之TabController.addListener

    一 问题 之前提到TabBar的第二种实现方式 TabController是有坑的 问题 这个监听在点击切换tab的时候会回调两次 左右滑动切换tab正常调用一次 二 原因 点击切换tab的时候执行了一个动画效果 滑动切换的时候是没有的 在
  • 用JAVA写的一个自动化简单示例!

    public class lianxi public static void main String args throws InterruptedException System getProperty webDriver firefox
  • 2022-03-24 RK3566 MIPI屏 调试记录,panel-init-sequence 命令格式介绍

    一 Single channel mipi屏的硬件连接如下 二 根据屏厂给的初始化代码配置dts了 屏厂给的初始化代码分几种形式 这个文章里面RK PX30 android8 1的mipi屏调试 要不要买菜 的博客 CSDN博客 有比较详细
  • Ubuntu20.04(linux)离线安装nacos

    一 解压 1 首先local目录得有nacos server 2 0 4 tar gz包 切换目录cd usr local执行以下命令 tar zxvf nacos server 2 0 4 tar gz 二 创建nacos service
  • postgresql安装及常见错误处理

    postgresql安装及常见错误处理 一 安装说明 最近 由于业务需要 我抛弃了一直使用的mysql数据库 开始转向postgresql数据库 至于为什么选择postgresql 肯定是postgresql比mysql更强大 对于post
  • DECORATOR 装饰模式

    DECORATOR 装饰模式 对象结构性模式 1 意图 动态地给一个对象增加一些额外的职责 就增加功能来说 Decorator模式相比生成子类更为灵活 2 别名 包装器Wrapper 3 动机 有时我们希望给某对象而不是整个类添加一些功能
  • UnityVR--机械臂场景9-简单流水线应用1

    目录 一 整体介绍 二 准备工作 三 建立流水线 1 流水线包含的功能 2 流水线的代码结构 3 场景实现 4 测试运行 一 整体介绍 流水线是在空间和时间上合理安排和组织工艺线路的一种形式 它涉及到一种或多种生产设备 传感器 物料传输设备
  • 字符串 最长公共前缀

    LC 最长公共前缀 编写一个函数来查找字符串数组中的最长公共前缀 如果不存在公共前缀 返回空字符串 输入 strs flower flow flight 输出 fl swift 实现 class ViewController UIViewC
  • Google Chrome浏览器的Cookies文件找不到问题

    一 问题 有个每天执行的Python代码 会自动获取Google Chrome浏览器的Cookie来访问网站数据 具体相关实现思路 获取chrome80谷歌浏览器存储的指定网站Cookie数据方法详解 报错信息如下 Exception Co
  • Spring中的IOC容器必须要了解的一些概念

    文章目录 Spring中的IOC容器必须要了解的一些概念 a SpringIOC容器意义 b 怎样往Spring的IOC容器里面存储对象 c 怎样从SpringIOC容器中取对象 d 如果对象的属性是对象类型 那么要怎么给对象的属性赋值呢
  • macOS通过钥匙串访问找回WiFi密码

    如果您忘记了Mac电脑上的WiFi密码 可以通过钥匙串访问来找回它 具体步骤如下 1 打开Mac电脑的 启动台 然后在其他文件中找到 钥匙串访问 2 运行 钥匙串访问 应用程序 点击左侧的 系统 然后在右侧找到要查看的WiFi网络名称 如果
  • 蓝桥杯 试题 算法训练 拿金币 C++ 详解

    题目 有一个N x N的方格 每一个格子都有一些金币 只要站在格子里就能拿到里面的金币 你站在最左上角的格子里 每次可以从一个格子走到它右边或下边的格子里 请问如何走才能拿到最多的金币 前言 请先阅读以下两篇文章 https blog cs
  • SPI机制

    1 概念 spi全称为 Service Provider Interface 是JDK内置的一种服务提供发现机制 SPI是一种动态替换发现的机制 一种解耦非常优秀的思想 它是jdk提供给 服务提供厂商 或者 插件开发者 使用的接口 是一种扩