什么是 SPI,和API有什么区别?

面试回答

Java 中区分 API 和 SPI,通俗的讲:API 和 SPI 都是相对的概念,他们的差别只在语义上,API 直接被应用开发人员使用,SPI 被框架扩展人员使用。

API Application Programming Interface

大多数情况下,都是实现方来制定接口并完成对接口的不同实现,调用方仅仅依赖却无权选择不同实现。

SPI Service Provider Interface

而如果是调用方来制定接口,实现方来针对接口实现不同的实现。调用方来选择自己需要的实现方。

知识扩展

如何定义一个 SPI

步骤1、定义一组接口(假设是 com.chiyi.test.IShout),并写出接口的一个或多个实现,(假设是 com.chiyi.test.Dogcom.chiyi.test.Cat)。

public interface IShout {void shout();
}
public class Dog implements IShout{@Overridepublic void shout() {System.out.println("wang wang");}
}
public class Cat implements IShout{@Overridepublic void shout() {System.out.println("miao miao");}
}

步骤2、在 src/main/resources/ 下建立 /META-INF/services目录,新增一个以接口命名的文件(com.chiyi.test.IShout 文件),内容是要应用的实现类(这里是 com.chiyi.test.Dogcom.chiyi.test.Cat,每行一个类)。

com.chiyi.test.Dog
com.chiyi.test.Cat

步骤3、使用 ServiceLoader 来加载配置文件中指定的实现。

public class Main {public static void main(String[] args) {ServiceLoader<IShout> shouts=ServiceLoader.load(IShout.class);for(IShout s:shouts){s.shout();}}
}

代码输出:

wang wang

miao miao

SPI 的实现原理

看 ServiceLoader 类的签名类的成员变量:

public final class ServiceLoader<S>implements Iterable<S>
{private static final String PREFIX = "META-INF/services/";// 代表被加载的类或者接口private final Class<S> service;// 用于定位,加载和实例化 providers 的类加载器private final ClassLoader loader;// 创建 ServiceLoader 时采用的访问控制上下文private final AccessControlContext acc;// 缓存 providers,按实例化的顺序排列private LinkedHashMap<String,S> providers = new LinkedHashMap<>();// 懒查找迭代器private LazyIterator lookupIterator;······
}

参考具体源码,梳理了一下,实现的流程如下:

  1. 应用程序调用 ServiceLoader.load 方法,ServiceLoader.load方法内先创建一个新的 ServiceLoader,并实例化该类中的成员变量,包括:
    1. loader(ClassLoader 类型,类加载器)
    2. acc(AccessControlContext 类型,访问控制器)
    3. providers(LinkedHashMap 类型,用于缓存加载成功的类)
    4. lookupIterator(实现迭代器功能)
  1. 应用程序通过迭代器接口获取对象实例
    1. ServiceLoader 先判断成员变量 providers 对象中(LinkedHashMap 类型)是否有缓存实例对象,如果有缓存,直接返回。
    2. 如果没有缓存,执行类的装载:
      1. 读取 META-INF/services/ 下的配置文件,获得所有能被实例化的类的名称
      2. 通过反射方法 Class.forName() 加载类对象,并用 instance() 方法将类实例化
      3. 把实例化的类缓存到 providers 对象中(LinkedHashMap 类型)
      4. 然后返回实例对象

SPI 的应用场景

概括地说,适用于:调用者根据实际使用需要,启用、扩展、或者替换框架的实现策略。

比如常见的例子:

  1. 数据库驱动加载接口实现类的加载
  2. JDBC 加载不同类型数据库的驱动
  3. 日志门面接口实现类加载
  4. SLF4J 加载不同提供商的日志实现类

Spring

Spring 中大量使用了 SPI,比如:对 servlet3.0 规范对 ServletContainerInitializer 的实现、自动类型转换 Type Conversion SPI(Converter SPI、Formatter SPI)等

Dubbo

Dubbo 中也大量使用 SPI的方式实现框架的扩展,不过它对 java 提供的原生 SPI 做了封装,允许用户扩展实现 Filter 接口。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/42652.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

opencv 矩阵运算

1.矩阵乘&#xff08;*&#xff09; Mat mat1 Mat::ones(2,3,CV_32FC1);Mat mat2 Mat::ones(3,2,CV_32FC1);Mat mat3 mat1 * mat2; //矩阵乘 结果 2.元素乘法或者除法&#xff08;mul&#xff09; Mat m Mat::ones(2, 3, CV_32FC1);m.at<float>(0, 1) 3;m.at…

浏览器控制台调试实用方法

许多程序员仅知道控制台的console.log&#xff0c;其实控制台API还包含一些其他实用方法&#xff0c; 这些方法在前端调试时会很有帮助。 目录 console.dir 查看对象属性和方法 输出DOM元素 console.error console.time和console.timeEnd console.log console.clear 总结…

set NOCOUNT on

SET NOCOUNT ON 是一条 SQL 语句&#xff0c;用于禁止在执行查询时返回受影响的行数消息。通常&#xff0c;当执行 INSERT、UPDATE、DELETE 等操作时&#xff0c;数据库会返回一个消息&#xff0c;表示受影响的行数。但在某些情况下&#xff0c;你可能希望禁用这些消息&#xf…

(五)、深度学习框架源码编译

1、源码构建与预构建&#xff1a; 源码构建&#xff1a; 源码构建是通过获取软件的源代码&#xff0c;然后在本地编译生成可执行程序或库文件的过程。这种方法允许根据特定需求进行配置和优化&#xff0c;但可能需要较长的时间和较大的资源来编译源代码。 预构建&#xff1a; 预…

dubbo与zookeeper

ZooKeeper 在 Dubbo 应用中的作用 ZooKeeper 是一个开源的分布式协调服务&#xff0c;它在 Dubbo 中被广泛使用来实现服务注册、发现和配置管理等功能。在 Dubbo 架构中&#xff0c;ZooKeeper 扮演了一个重要的角色&#xff0c;可以提供以下功能&#xff1a; ZooKeeper 是一个开…

2023年05月 C/C++(二级)真题解析#中国电子学会#全国青少年软件编程等级考试

第1题:数字放大 给定一个整数序列以及放大倍数x,将序列中每个整数放大x倍后输出。 时间限制:1000 内存限制:65536 输入 包含三行: 第一行为N,表示整数序列的长度(N ≤ 100); 第二行为N个整数(不超过整型范围),整数之间以一个空格分开; 第三行包含一个整数(不超过整…

【RocketMQ】SpringBoot集成RocketMQ

SpringBoot集成RocketMQ 首先依旧是引入依赖 <dependency><groupId>org.apache.rocketmq</groupId><artifactId>rocketmq-spring-boot-starter</artifactId><version>2.2.2</version> </dependency>然后就可以编写发送不同类…

Vue2-全局事件总线、消息的订阅与发布、TodoList的编辑功能、$nextTick、动画与过渡

&#x1f954;&#xff1a;高度自律即自由 更多Vue知识请点击——Vue.js VUE2-Day9 全局事件总线1、安装全局事件总线2、使用事件总线&#xff08;1&#xff09;接收数据&#xff08;2&#xff09;提供数据&#xff08;3&#xff09;组件销毁前最好解绑 3、TodoList中的孙传父&…

【Git】Git中用到的一些命令

Git文件有四种状态&#xff1a; 未跟踪未修改&#xff08;已跟踪&#xff09;已修改&#xff08;已跟踪&#xff09;已暂存&#xff08;已跟踪&#xff09; 通常我们将项目clone下来就会处于已跟踪状态 1、git diff命令 git diff&#xff1a;查看没有暂存的文件更新哪些部分…

js判断手指的上滑,下滑,左滑,右滑,事件监听 和 判断鼠标滚轮向上滚动滑轮向下滚动

js判断手指的上滑&#xff0c;下滑&#xff0c;左滑&#xff0c;右滑&#xff0c;事件监听 和 判断鼠标滚轮向上滚动滑轮向下滚动 pc端 判断鼠标滚轮向上滚动滑轮向下滚动 const scrollFunc (e) > { e e || window.event; let wheelDelta e.wheelDelta ? e.wheelDelta…

Spring Clould 部署 - Docker

视频地址&#xff1a;微服务&#xff08;SpringCloudRabbitMQDockerRedis搜索分布式&#xff09; 初识Docker-什么是Docker&#xff08;P42&#xff0c;P43&#xff09; 微服务虽然具备各种各样的优势&#xff0c;但服务的拆分通用给部署带来了很大的麻烦。 分布式系统中&…

[强网杯 2019]随便注

输入1‘ 输入1“ 和输入1 一样说明是由‘闭合 然后我们尝试输入select 这里提示过滤了select&#xff0c;说明联合查询&#xff0c;报错注入&#xff0c;布尔,时间盲注就都不可以使用了。我们只剩下了 堆叠注入。 或者将select编码绕开也可以。 按sql注入测试1 or 11 # ​然…

Unity 物体的运动之跟随鼠标

你想让鼠标点击哪里&#xff0c;你的运动的对象就运动到哪里吗&#xff1f; Please follow me ! 首先&#xff0c;你要先添加一个Plane ,以及你的围墙&#xff0c;你的移动的物体 想要实现跟随鼠标移动&#xff0c;我们先创建一个脚本 using System.Collections; using Syst…

铜卡计混合法比热测试仪绝热量热计的高精度主动控制解决方案

摘要&#xff1a;在下落法比热容测试中绝热量热计的漏热是最主要误差源&#xff0c;为实现绝热量热计的低漏热要求&#xff0c;本文介绍了主动护热式等温绝热技术以及相应的解决方案。方案的核心一是采用循环水冷却金属圆筒给量热计和护热装置提供低温环境或恒定冷源&#xff0…

黑马点评-项目集成git及redis实现短信验证码登录

目录 IDEA集成git 传统session存在的问题 redis方案 业务流程 选用的数据结构 整体访问流程 发送短信验证码 获取校验验证码 配置登录拦截器 拦截器注册配置类 拦截器 用户状态刷新问题 刷新问题解决方案 IDEA集成git 远程仓库采用码云&#xff0c;创建好仓库&…

【O2O领域】Axure外卖订餐骑手端APP原型图,外卖配送原型设计图

作品概况 页面数量&#xff1a;共 110 页 兼容软件&#xff1a;Axure RP 9/10&#xff0c;不支持低版本 应用领域&#xff1a;外卖配送、生鲜配送 作品申明&#xff1a;页面内容仅用于功能演示&#xff0c;无实际功能 作品特色 本品为外卖订餐骑手端APP原型设计图&#x…

CentOS下MySQL的彻底卸载的几种方法

这里我为大家详细讲解下“CentOS下MySQL的彻底卸载的几种方法”的完整攻略。 一、关闭MySQL服务 在开始操作之前&#xff0c;需要先关闭MySQL服务。可以使用以下命令来关闭MySQL服务&#xff1a; systemctl stop mysqld 或者 service mysqld stop 二、使用yum命令卸载MySQL…

微前端 - qiankun

qiankun 是一个基于 single-spa 的微前端实现库&#xff0c;旨在帮助大家能更简单、无痛的构建一个生产可用微前端架构系统。 本文主要记录下如何接入 qiankun 微前端。主应用使用 vue2&#xff0c;子应用使用 vue3、react。 一、主应用 主应用不限技术栈&#xff0c;只需要提…

数据结构之线性表的类型运用Linear Lists: 数组,栈,队列,链表

线性表 定义 一个最简单&#xff0c;最基本的数据结构。一个线性表由多个相同类型的元素穿在一次&#xff0c;并且每一个元素都一个前驱&#xff08;前一个元素&#xff09;和后继&#xff08;后一个元素&#xff09;。 线性表的类型 常见的类型&#xff1a;数组、栈、队列…

mysql、redis面试题

mysql 相关 1、数据库优化查询方法 外键、索引、联合查询、选择特定字段等等2、简述mysql和redis区别 redis&#xff1a; 内存型非关系数据库&#xff0c;数据保存在内存中&#xff0c;速度快mysql&#xff1a;关系型数据库&#xff0c;数据保存在磁盘中&#xff0c;检索的话&…