纳尼?这就是SPI

2023-11-20 19:10
文章标签 spi 纳尼

本文主要是介绍纳尼?这就是SPI,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

引入

我们知道每种数据库都是一个完整的独立的系统,假设我们现在的产品要兼容大多数数据库厂商,比如MySQL,Oracle,DB2等,那么怎么做呢?可以制作一些约定,大家都基于这个约定进行相关操作,比如调用者可以通过约定去连接数据库,而数据库厂商则根据这个约定实现连接数据库的具体操作。
在这里插入图片描述

这个看起来有点像设计模式中的门面模式,调用者只需要对这个约定进行相关操作,而具体的实现由相应的厂商实现,假设要从MySQL切换到DB2,只需要在底层悄悄的替换即可,那么具体是怎么替换的呢?

在maven项目中,假设我们目前使用的是MySQL,那么我们的maven依赖是不是有 mysql-connector-java ,换言之,我们可以把这个依赖替换成我们想要的数据库依赖

那么它到底是怎么实现替换依赖就可以切换数据库的呢
以MySQL为例,打开MySQL的jar,看到了META-INFcom 目录
在这里插入图片描述
进入META-INF/services/ 目录下,发现了 java.sql.Driver 文件
在这里插入图片描述
查看就发现了我们熟悉的驱动,而这个驱动就是MySQL厂商实现的
在这里插入图片描述
那他是怎么和我们的java应用程序相关联的呢,通过源码,我们发现是实现了java的Driver接口

package com.mysql.cj.jdbc; import java.sql.SQLException;public class Driver extends NonRegisteringDriver implements java.sql.Driver {//// Register ourselves with the DriverManager//static {try {java.sql.DriverManager.registerDriver(new Driver());} catch (SQLException E) {throw new RuntimeException("Can't register driver!");}}/*** Construct a new driver and register it with DriverManager* * @throws SQLException*             if a database error occurs.*/public Driver() throws SQLException {// Required for Class.forName().newInstance()}
}

这个接口正好是jdk的拓展包里的
在这里插入图片描述
现在可以知道上面描述的约定便是这个 java.sql.Driver 接口。大家都基于这个约定进行相应的操作
这个便是我们常说的SPI机制

SPI

什么是SPI

SPI的全名为Service Provider Interface,当服务的提供者,提供了服务接口的一种实现之后,在jar包的META-INF/services/目录里同时创建一个以服务接口命名的文件。该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。

JAVA中的实现

在Java中的实现就是ServiceLoader(源码部分省略)

public final class ServiceLoader<S>implements Iterable<S>
{private static final String PREFIX = "META-INF/services/";// The class or interface representing the service being loadedprivate final Class<S> service;// The class loader used to locate, load, and instantiate providersprivate final ClassLoader loader;// The access control context taken when the ServiceLoader is createdprivate final AccessControlContext acc;// Cached providers, in instantiation orderprivate LinkedHashMap<String,S> providers = new LinkedHashMap<>();// The current lazy-lookup iteratorprivate LazyIterator lookupIterator;.........
}

简单来说就是会在jar包下寻找 META-INF/services/ 目录下的文件,文件名就是接口名,文件内容就是这个接口的实现类,可以有多个实现类。

解析文件的内容

private Iterator<String> parse(Class<?> service, URL u)throws ServiceConfigurationError{InputStream in = null;BufferedReader r = null;ArrayList<String> names = new ArrayList<>();try {in = u.openStream();r = new BufferedReader(new InputStreamReader(in, "utf-8"));//必须是utf-8的格式int lc = 1;while ((lc = parseLine(service, u, r, lc, names)) >= 0);} catch (IOException x) {fail(service, "Error reading configuration file", x);} finally {try {if (r != null) r.close();if (in != null) in.close();} catch (IOException y) {fail(service, "Error closing configuration file", y);}}return names.iterator();}

在这里进行加载,并且放进缓存中

private S nextService() {if (!hasNextService())throw new NoSuchElementException();String cn = nextName;nextName = null;Class<?> c = null;try {c = Class.forName(cn, false, loader); //通过反射加载类,但不进行初始化,也就是懒加载的} catch (ClassNotFoundException x) {fail(service,"Provider " + cn + " not found");}if (!service.isAssignableFrom(c)) {fail(service,"Provider " + cn  + " not a subtype");}try {S p = service.cast(c.newInstance());providers.put(cn, p); //放进缓存中return p;} catch (Throwable x) {fail(service,"Provider " + cn + " could not be instantiated",x);}throw new Error();          // This cannot happen
}

实战模拟

这是一个约定,在一个单独的模块中,打jar包

public interface IDataBase {String getDataBaseName();
}

这也是一个单独的模块,依赖上面制定的约定,实现DB2数据库,打jar包

public class DB2 implements IDataBase {public String getDataBaseName() {return "db2";}
}

这也是一个单独的模块,依赖上面制定的约定,实现MySQL数据库,打jar包

public class Mysql implements IDataBase{public String getDataBaseName() {return "mysql";}
}

在这里插入图片描述

测试

客户端依赖

<dependencies><dependency><groupId>com.luo</groupId><artifactId>db-interface</artifactId><version>1.0-SNAPSHOT</version></dependency><!--使用DB2--><dependency><groupId>com.luo</groupId><artifactId>db2</artifactId><version>1.0-SNAPSHOT</version></dependency></dependencies>
public class Test {public static void main(String[] args) {ServiceLoader<IDataBase> dataBases = ServiceLoader.load(IDataBase.class);//加载指定类的所有实现Iterator<IDataBase> iterator = dataBases.iterator();while (iterator.hasNext()){  //遍历每一个实现IDataBase next = iterator.next();System.out.println(next.getDataBaseName());}}
}

因为我只依赖了DB2,所以输出db2
在这里插入图片描述

优缺点

优点:解耦合,懒加载

缺点:会一次性加载配置文件中的所有实现类

这篇关于纳尼?这就是SPI的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/396803

相关文章

【STM32】SPI通信-软件与硬件读写SPI

SPI通信-软件与硬件读写SPI 软件SPI一、SPI通信协议1、SPI通信2、硬件电路3、移位示意图4、SPI时序基本单元(1)开始通信和结束通信(2)模式0---用的最多(3)模式1(4)模式2(5)模式3 5、SPI时序(1)写使能(2)指定地址写(3)指定地址读 二、W25Q64模块介绍1、W25Q64简介2、硬件电路3、W25Q64框图4、Flash操作注意事项软件SPI读写W2

stm32之软件SPI读写W25Q64存储器应用案例

系列文章目录 1. stm32之SPI通信协议 文章目录 系列文章目录前言一、电路接线图二、应用案例代码三、应用案例分析3.1 SPI通信模块3.2 W25Q64模块3.3 主程序 前言 提示:本文主要用作在学习江科大自化协STM32入门教程后做的归纳总结笔记,旨在学习记录,如有侵权请联系作者 本案例使用软件SPI通信的方式实现了STM32与W25Q64 Flas

Flink1.10基于工厂模式的任务提交与SPI机制

《2021年最新版大数据面试题全面开启更新》 Flink任务执行模式包含了yarn-session、standalone、per-job、local, 在1.10中又增加k8s的执行模式,那么在任务提交过程中如何根据不同的执行模式进行任务提交呢?主要通过两个接口来实现:PipelineExecutorFactory 与 PipelineExecutor。PipelineExecutorF

转载 SPI的比喻理解

SPI 传输是一个虚拟的移位寄存器方式。 你这么理解就可以: 主机和从机之间有一条 16 格的传送带。主机一格一格拨动它转动(相当于发送时钟)。 如果是主机发送,它就把一个个的东西放在传送带上,转动 8 次,就传到从机一侧了。这时,从机可以从传送带上将东西取下。如果从机没有取东西,这些东西再转 8 次又回到主机一侧。 如果是主机接收,从机就要把 8 个东西一次放上传送带。当主机转动 8 次,东西就

Java SPI机制源码

文章目录 SPI简介使用案例SPI的应用SPI机制源码SPI与类加载器双亲委派机制 SPI简介 Java的SPI(Service Provider Interface)机制允许第三方为应用程序提供插件式的扩展,而不需要修改应用程序本身的代码,从而实现了解耦。Java标准库本身就提供了SPI机制,通常是通过在META-INF/services目录下放置文件来实现的。 S

W25Q32驱动 基于HAL库的SPI

环境 芯片:STM32F103ZET6 库:来自HAL的STM32F1XX.H 原理图 cubeMX配置 W25Q32.h //// Created by seven on 2024/9/2.//#ifndef SPI_W25Q32_H#define SPI_W25Q32_H#include "spi.h"/*** 初始化W25Q32闪存芯片*/void W25Q32

STM32F10XX中SPI的DMA发送数据

参考资料: http://blog.csdn.net/jdh99/article/details/7603029 http://www.openedv.com/posts/list/3159.htm     上面提到的两篇博文比较详细深刻的说明了DMA的工作方式以及SPI的DMA传输方式的特点。结合对Stm32F103VET6中SPI的DMA传输方式的配置和学习谈谈感受,在看下面的内容之前请

解耦利器 - Java中的SPI机制

为什么需要SPI机制 SPI和API的区别是什么 SPI是一种跟API相对应的反向设计思想:API由实现方确定标准规范和功能,调用方无权做任何干预; 而SPI是由调用方确定标准规范,也就是接口,然后调用方依赖此接口,第三方实现此接口,这样做就可以方便的进行扩展,类似于插件机制,这是SPI出现的需求背景。 SPI : “接口”位于“调用方”所在的“包”中 概念上更依赖调用方。 组织上位于

【STM32项目设计】STM32F411健康助手--硬件SPI (硬件NSS/CS)驱动st7735--1.8寸TFT显示屏(1)

#include "lcd_driver.h"static uint16_t SPI_TIMEOUT_UserCallback(uint8_t errorCode);//液晶IO初始化配置void LCD_Driver_Init(void){SPI_InitTypeDef SPI_InitStructure;GPIO_InitTypeDef GPIO_InitStructure;/* 使

【MCAL】TC397+EB-tresos之SPI配置实战 - (同步/异步)

本篇文章首先从理论讲起,从AUTOSAR规范以及MCAL手册两个不同角度(前者偏理论,后者偏实践)介绍了SPI模块的背景概念与理论,帮助读者在实际配置之前能有个理论的框架。然后详细的介绍了在TC397平台使用EB tresos对SPI驱动模块进行配置与调试的实战过程,帮助第一次接触这个模块的读者能够更快的上手来实现符合自己项目要求的开发工作,完成SPI通信。文章分别从同步和异步两种方式介绍了配置