SPI 机制

一、简述

本文介绍 SPI 机制。

二、什么是 SPI 机制

SPI(Service Provider Interface)机制是 Java 编程语言中的一种机制,用于实现组件之间的解耦和扩展。SPI 允许开发者编写服务接口(Service Interface),并在运行时动态地加载实现了该接口的服务提供者(Service Provider)。
SPI 机制的基本原理如下:

  1. 定义服务接口:开发者定义一个服务接口,描述了一组操作或功能,这些功能可以由不同的提供者来实现。
  2. 服务提供者编写服务:不同的服务提供者可以编写不同的服务(即实现该服务接口,并提供自己的实现逻辑)。
  3. 配置服务注册文件:每个服务提供者都需要在特定的位置提供一个描述文件,通常是META-INF/services/接口全限定名,文件内容为该接口实现类的全限定类名列表。
  4. 服务加载器加载服务:在程序运行时,Java 虚拟机会通过 SPI 机制加载并实例化这些服务提供者,然后调用其方法来完成具体的功能。

三、为什么使用 SPI 机制

  1. 假设我们有一个服务接口 Animal,定义了动物的基本行为:
public interface Animal {void makeSound();
}
  1. Animal 接口有一个实现类
public class Dog implements Animal {@Overridepublic void makeSound() {System.out.println("Dog barks: Woof woof!");}
}
  1. 编写接口和类的使用逻辑
public class Main {public void sound() {Animal animal = new Dog();animal.makeSound();}
}

上面的例子是传统的开发模式(在一个项目中定义接口,实现接口)。现在将以上内容拆分到两个项目:

  1. 在 A 项目中定义接口
public interface Animal {void makeSound();
}
  1. 在 A 项目中定义接口的使用逻辑(但是没有实现类)
import java.util.ServiceLoader;public class Main {public void sound() {// 使用 ServiceLoader 加载 Animal 接口的实现类ServiceLoader<Animal> animals = ServiceLoader.load(Animal.class);// 遍历并调用每个实现类的方法for (Animal animal : animals) {animal.makeSound();}}
}
  1. B 项目中引入 A 项目,并且实现 A 中的接口
public class Dog implements Animal {@Overridepublic void makeSound() {System.out.println("Dog barks: Woof woof!");}
}
  1. B 项目在 META-INF/services/Animal 文件中提供实现类的全限定类名:
com.example.Dog

上面两种方式可以实现同样的功能,第二种方式采用的是 SPI 机制。在 SPI 机制中,A 定义方法的执行逻辑,并开放了一个扩展点交由 B 实现,扩展点的具体功能是由实现者自己处理,这样就实现了解耦。可以看出 SPI 机制的优点在于它的松耦合性和扩展性,因为服务接口与具体的实现是分离的,开发者可以根据需要灵活地替换服务提供者接口的实现。

四、如何使用 SPI 机制

我们这里以 JDBC 为例来说明该如何使用 SPI。

4.1 定义服务接口

Java 团队在 java.sql 包中定义了 Driver 接口

package java.sql;import java.util.logging.Logger;public interface Driver {Connection connect(String url, java.util.Properties info)throws SQLException;boolean acceptsURL(String url) throws SQLException;DriverPropertyInfo[] getPropertyInfo(String url, java.util.Properties info)throws SQLException;int getMajorVersion();int getMinorVersion();boolean jdbcCompliant();public Logger getParentLogger() throws SQLFeatureNotSupportedException;
}

4.2 服务加载器加载服务

Java 团队在 java.sql.DriverManager类中使用 java.util.ServiceLoader来加载 java.sql.Driver接口的实现类(可能不止一个),并实现了一些逻辑。

package java.sql;import java.util.ServiceLoader;public class DriverManager {// 此处省略了一些逻辑内容private static void ensureDriversInitialized() {// 服务加载器加载服务ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);// 迭代遍历能够找到的服务实现Iterator<Driver> driversIterator = loadedDrivers.iterator();try {while (driversIterator.hasNext()) {driversIterator.next();}} catch (Throwable t) {// Do nothing}return null;}// 此处省略了一些逻辑内容
}

4.3 服务提供者编写服务

MySQL 团队在自己项目的 com.mysql.cj.jdbc 包中编写了针对 Java 团队 java.sql.Driver 接口的实现。

package com.mysql.cj.jdbc;import java.sql.DriverManager;
import java.sql.SQLException;public class Driver extends NonRegisteringDriver implements java.sql.Driver {public Driver() throws SQLException {}static {try {DriverManager.registerDriver(new Driver());} catch (SQLException var1) {throw new RuntimeException("Can't register driver!");}}
}

4.4 服务提供者配置服务注册文件

MySQL 团队在自己项目中 META-INF/services/ 目录下配置了服务注册文件。该文件名是接口的全限定名称,文件里面的内容是实现类的全限定名称。
image.png

4.5 SPI 机制处理流程

  1. Java 团队预先定义好接口,定义好接口的使用逻辑
  2. MySQL 团队实现接口,并以规定好的方式配置服务注册文件(即配置文件必须在 META-INF/services 目录下,文件名称以接口全限定名称命名,文件内容为接口实现类的全限定名称)
  3. 当在项目中引入了 MySQL 的驱动之后,JDBC 就能根据之前的服务注册文件找到对应的实现类
  4. 接下来,可以使用 JDBC 连接、操作数据库了

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

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

相关文章

Python基础教程

随着科技的快速发展&#xff0c;编程已成为一项重要的技能。在众多编程语言中&#xff0c;Python因其简洁、易读、强大的功能库而备受青睐。无论你是编程新手&#xff0c;还是希望了解Python的开发者&#xff0c;本文都将为你提供一个Python基础教程&#xff0c;带你走进Python…

计算机网络 路由器基本配置

一、实验内容 1、按照下表配置好PC机IP地址和路由器端口IP地址 2、配置好路由器特权密文密码“abcd&#xff0b;两位班内序号”和远程登录密码“star” 3、验证测试 a.验证各个接口的IP地址是否正确配置和开启 b.PC1 和 PC2 互ping c.验证PC1通过远程登陆到路由器上&#…

目前深圳嵌入式单片机就业环境如何?

深圳作为中国的科技创新中心之一&#xff0c;嵌入式行业的就业环境相对较好。我这里有一套嵌入式入门教程&#xff0c;不仅包含了详细的视频讲解&#xff0c;项目实战。如果你渴望学习嵌入式&#xff0c;不妨点个关注&#xff0c;给个评论222&#xff0c;私信22&#xff0c;我在…

docker 上达梦导入dump文件报错:本地编码:PG GBK,导入女件编码:PGGB18030

解决方案&#xff1a; 第一步进入达梦数据容器内部 docker exec -it fc316f88caff /bin/bash 第二步&#xff1a;在容器中 /opt/dmdbms/bin目录下 执行命令 cd /opt/dmdbms/bin./dimp USERIDSYSDBA/SYSDBA001 FILE/opt/dmdbms/ZFJG_LJ20240407.dmp SCHEMASZFJG_LJUSERIDSYSD…

Lua语法(三)——元表与元方法

参考链接: 系列链接: Lua语法(一) 系列链接: Lua语法(二)——闭包/日期和时间 系列链接: Lua语法(三)——元表与元方法 系列链接: Lua语法(四)——协程 系列链接: Lua语法(五)——垃圾回收 系列链接: Lua语法(六)——面相对象编程 元表与元方法目录 简介正文元表元方法表相关常…

linux安装

1、解压vm ware压缩包 2双击安装 3点击自定义硬件 4双击cd/dvd,给虚拟光驱里放虚拟光盘 5记得启动时链接勾上&#xff0c;勾上起点系统时 虚拟光驱才会一起启动 6点击确认即可&#xff01; 开机 选择第一个 7进入图形化安装界面 8设置时区 9选择硬盘 10网络配置 开启以太网&am…

C语言进阶课程学习记录-数组指针和指针数组分析

C语言进阶课程学习记录-数组指针和指针数组分析 实验-数组指针的大小实验-指针数组小结 本文学习自狄泰软件学院 唐佐林老师的 C语言进阶课程&#xff0c;图片全部来源于课程PPT&#xff0c;仅用于个人学习记录 实验-数组指针的大小 #include <stdio.h>typedef int(AINT…

简述Java中synchronized关键字的底层工作原理

在Java中&#xff0c;synchronized 关键字是一个重要的同步机制&#xff0c;用于控制多线程对共享资源的访问&#xff0c;以防止并发问题。了解 synchronized 的底层工作原理&#xff0c;可以帮助我们更好地编写线程安全的代码。synchronized 关键字可以应用于方法或者代码块&a…

【MoS2】应变增强的单层MoS2光电探测器

这篇文章的标题是《Strain-Enhanced Large-Area Monolayer MoS2 Photodetectors》&#xff0c;作者是Borna Radatovic等人&#xff0c;发表在《ACS Applied Materials & Interfaces》期刊的2024年第16卷。文章主要研究了应变增强的大面积单层MoS2光电探测器的性能和应用潜力…

【题目】【信息安全管理与评估】2022年国赛高职组“信息安全管理与评估”赛项样题1

【题目】【信息安全管理与评估】2022年国赛高职组“信息安全管理与评估”赛项样题1 信息安全管理与评估 网络系统管理 网络搭建与应用 云计算 软件测试 移动应用开发 任务书&#xff0c;赛题&#xff0c;解析等资料&#xff0c;知识点培训服务 添加博主wx&#xff1a;liuliu548…

Testng测试框架(3)-数据驱动TestNG@DataProvider

TestNG 是一个强大的 Java 测试框架&#xff0c;它提供了许多高级功能&#xff0c;如参数化测试、依赖注入、分组等。其中&#xff0c;DataProvider 是 TestNG 中一个非常有用的注解&#xff0c;用于为测试方法提供数据。 DataProvider 的作用 使用 DataProvider 注解的方法可…

【算法刷题day24】回溯算法+简单剪枝

77.组合 文档链接&#xff1a;[代码随想录] 题目链接&#xff1a;77.组合 题目&#xff1a; 给定两个整数 n 和 k&#xff0c;返回范围 [1, n] 中所有可能的 k 个数的组合。 你可以按 任何顺序 返回答案。 class Solution { private:vector<int> path;vector<vector&…

java数据结构与算法刷题-----LeetCode260. 只出现一次的数字 III

java数据结构与算法刷题目录&#xff08;剑指Offer、LeetCode、ACM&#xff09;-----主目录-----持续更新(进不去说明我没写完)&#xff1a;https://blog.csdn.net/grd_java/article/details/123063846 文章目录 与运算取末尾1分组 与运算取末尾1分组 解题思路&#xff1a;时间…

文献速递:深度学习肝脏肿瘤诊断---基于多相增强 CT 和临床数据的恶性肝肿瘤鉴别诊断深度学习

Title 题目 Deep learning for diferential diagnosisof malignant hepatic tumors based on multi-phase contrast-enhanced CT and clinical data 基于多相增强 CT 和临床数据的恶性肝肿瘤鉴别诊断深度学习 Abstract 摘要 Liver cancer remains the leading cause of can…

Node.js 开发技巧

轻松创建 HTTP 服务器&#xff1a; 使用 Node.js&#xff0c;你可以轻松创建自己的 HTTP 服务器。只需几行代码&#xff0c;你就可以像一位传统的酒保一样为客户端提供服务。记住&#xff0c;不要忘记问客户端想要些什么&#xff01; const http require(http);const server …

事件冒泡、事件捕获、事件委托

事件冒泡、事件捕获、事件委托 事件冒泡和事件捕获: 解释事件冒泡和事件捕获的概念。 事件冒泡和事件捕获是DOM事件传播的两个阶段。当一个事件发生在DOM中的某个元素上时&#xff0c;它可以在父元素之间传播。事件捕获阶段从根节点开始&#xff0c;逐级向下直到触发事件的元素…

2024年150道高频Java面试题(二十六)

51. 线程和进程的区别&#xff1f; 线程和进程是操作系统中进行任务调度和资源分配的两个基本概念。 进程&#xff1a; 进程是操作系统进行资源分配的基本单位。每个进程都有独立的地址空间&#xff0c;一个进程崩溃后&#xff0c;在保护模式下不会影响到其他进程&#xff0…

计算机网络——TCP和UDP协议

目录 前言 前篇 引言 TCP与UDP之间的区别 TCP 三次握手 为什么要三次握手而不是两次握手&#xff1f; 丢包问题与乱序问题的解决 四次挥手 为什么客户端需要等待超时时间&#xff1f; UDP协议 TCP和UDP的主要区别 前言 本博客是博主用于复习计算机网络的博客&…

软件开发安全备受重视,浙江某运营商引入CWASP认证课程,

​浙江省某大型运营商是一家实力雄厚、服务优质的通信运营商&#xff0c;致力于为全省用户提供优质、高效的通信服务。数字时代&#xff0c;该运营商顺应信息能量融合发展趋势&#xff0c;系统打造以5G、算力网络、能力中台为重点的新型信息基础设施&#xff0c;夯实产业转型升…

Redis入门到通关之五大基本数据类型及其使用场景

文章目录 一 什么是NoSQL&#xff1f;二 Redis是什么&#xff1f;三 Redis五大基本类型1 String&#xff08;字符串&#xff09;应用场景 2 List&#xff08;列表&#xff09;应用场景 3 Set&#xff08;集合&#xff09;4 sorted set&#xff08;有序集合&#xff09;应用场景…