SPI 机制

news2024/11/24 4:59:29

一、简述

本文介绍 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 {
    @Override
    public 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 {
    @Override
    public 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.coloradmin.cn/o/1590060.html

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

相关文章

开源无需授权2024最新同城上门预约家政按摩小程序+公众号H5+APP源码系统下载

2024最新同城上门预约家政按摩源码简介&#xff1a; 开源无需授权2024最新同城上门预约家政按摩小程序&#xff0b;公众号H5&#xff0b;APP源码系统下载&#xff0c;前端采用uni-app开发&#xff0c; 后端thinkphp框架开发。适配多端&#xff08;小程序&#xff0b;公众号H5&…

吴恩达2022机器学习专项课程(一) 5.9 特征工程 5.10 多项式回归

问题预览/关键词 特征工程的重要性什么是特征工程&#xff1f;什么是多项式回归&#xff1f;特征缩放对多项式回归的重要性特征的选择 笔记 1.特征工程的重要性 选择或输入合适的特征&#xff0c;是让算法正常工作的关键步骤之一。 2.特征工程 根据应用场景&#xff0c;运…

.NET SignalR Redis实时Web应用

环境 Win10 VS2022 .NET8 Docker Redis 前言 什么是 SignalR&#xff1f; ASP.NET Core SignalR 是一个开放源代码库&#xff0c;可用于简化向应用添加实时 Web 功能。 实时 Web 功能使服务器端代码能够将内容推送到客户端。 适合 SignalR 的候选项&#xff1a; 需要从服…

Qotom Q720G5英特尔赛扬处理器N4000高性价比无风扇迷你电脑5网口软路由防火墙

在数字时代&#xff0c;迷你电脑已经成为高效、灵活的解决方案&#xff0c;无论是个人用户还是企业用户&#xff0c;都能从中受益。Qotom Q720G5 无风扇迷你电脑就是这样一款强大的选择&#xff0c;它不仅可以作为软路由、防火墙和路由器&#xff0c;还有着更多的潜力等待发掘。…

Nginx转发请求错误

说明&#xff1a;记录一次使用Nginx转发请求的错误&#xff1b; 场景 公司内部有两台服务器都跑了后端项目&#xff0c;在使用Nginx做请求分发时&#xff0c;我发现其中有台服务器一直没有处理请求&#xff08;没打印相关的日志信息&#xff09;&#xff0c;于是我修改了下Ng…

GGUF类型模型文件

在HuggingFace上&#xff0c;我们时不时就会看到GGUF后缀的模型文件&#xff0c;它是如何来的&#xff1f;有啥特点&#xff1f; https://huggingface.co/TheBloke/Llama-2-7B-Chat-GGUF GGUF 由来 Georgi Gerganov&#xff08;https://github.com/ggerganov&#xff09;是著…

线性代数

标量、向量、张量 标量占据的是零维空间向量占据的是一维数据&#xff0c;例如语音信号矩阵占据的是二维数组&#xff0c;例如灰度图像张量占据的是三维乃至更高维的数组&#xff0c;例如RGB图像和视频 内积(点乘)概述 内积(inner product) 计算的则是两个向量之间的关系 两…

【读点论文】Segment Anything,视觉界的GPT,可以通过Prompt完成图像实体理解的视觉基础大模型,处理零样本任务

Segment Anything Abstract 我们介绍了Segment Anything&#xff08;SA&#xff09;项目&#xff1a;一种用于图像分割的新任务、模型和数据集。在数据收集循环中使用我们的高效模型&#xff0c;我们构建了迄今为止&#xff08;迄今为止&#xff09;最大的分割数据集&#xf…

QA测试开发工程师面试题满分问答11: web前端页面视频组件无法播放如何定位bug

当 web 前端页面的视频组件无法播放时&#xff0c;可以从以下维度进行分析和定位可能的 bug&#xff0c;分析维度包括但不限于&#xff1a;前端功能点、缓存、异常、后端功能点、资源占用、并发、网络等&#xff1a; 前端功能点&#xff1a; HTML5 视频支持&#xff1a;检查视频…

openstack之neutron介绍

核心组件 neutron-server&#xff1a;提供API接口&#xff0c;把对应的api请求传给plugin进&#xff1b; neutron-plugin&#xff1a;管理逻辑网络状态&#xff0c;调用agent&#xff1b; neutron-agent&#xff1a;在provider network上创建网络对象&#xff1b; neutron-…

万兆以太网MAC设计(2)MAC_RX模块

文章目录 前言一、模块功能二、代码三、仿真波形 前言 上文我们打通了了万兆以太网物理层和数据链路层&#xff0c;其实就是会使用IP核了&#xff0c;本文将正式开始MAC层设计第一篇&#xff0c;接收端设计。 一、模块功能 MAC_RX模块功能如下&#xff1a; 解析接收的报文&…

Android13 CameraServer启动流程

代码入口 frameworks/av/camera/cameraserver 里面包含了四个文件 我们先来看看Android.bp的内容 package {// See: http://go/android-license-faq// A large-scale-change added default_applicable_licenses to import// all of the license_kinds from "frameworks_a…

什么是MOV视频格式?如何把MP4视频转MOV视频格式?

一&#xff0c;前言 当然可以&#xff0c;MP4视频可以转换为MOV格式。这两种格式都是常见的视频文件格式&#xff0c;它们都可以用于存储和播放视频内容。虽然它们的编码方式和特性有所不同&#xff0c;但使用合适的视频转换工具可以轻松地将MP4视频转换为MOV格式。 二&#…

Science Robotics 封面论文:Google DeepMind 通过深度强化学习赋予双足机器人敏捷的足球技能

创造通用具身智能&#xff0c;即创造能够在物理世界中敏捷、灵巧和理解的智能体——就像动物或人类一样——是人工智能 &#xff08;AI&#xff09; 研究人员和机器人专家的长期目标之一。动物和人类不仅是自己身体的主人&#xff0c;能够流畅而轻松地执行和组合复杂的动作&…

【Hadoop】下载安装及伪分布式集群搭建教程

目录 1.概述 2.环境准备 3.hadoop安装 3.1.下载安装配置 3.2.伪分布式集群 3.3.注意事项 4.Hadoop集群的组成 1.概述 hadoop有三种安装模式 单机模式&#xff0c;只在一台机器上运行&#xff0c;存储用的本地文件系统而不是HDFS。 伪分布式模式&#xff0c;存储采用HD…

2024认证杯数学建模A题保暖纤维保暖能力原创论文讲解(含完整python代码)

大家好呀&#xff0c;从发布赛题一直到现在&#xff0c;总算完成了认证杯数学中国数学建模网络挑战赛第一阶段A题目保暖纤维的保暖能力完整的成品论文。 本论文可以保证原创&#xff0c;保证高质量。绝不是随便引用一大堆模型和代码复制粘贴进来完全没有应用糊弄人的垃圾半成品…

openGauss_5.0.1 企业版安装及问题记录(CentOS系统):主备模式服务器安装

目录 &#x1f4da;第一章 官方地址&#x1f4d7;安装包下载地址&#x1f4d7;文档指南 &#x1f4da;第二章 安装&#x1f4d7;准备工作&#x1f4d7;开始安装&#x1f4d5;创建XML配置文件&#x1f4d5;初始化安装环境&#x1f4d5;执行安装&#x1f4d5;验证 &#x1f4da;第…

前端用 HTML5 + CSS3 + JavaScript,后端连接什么数据库更简单?

当前端使用 HTML5、CSS3 和 JavaScript 进行开发时&#xff0c;后端连接何种数据库是一个非常重要的问题&#xff0c;因为数据库的选择直接影响着后端代码的编写、数据存储与查询的效率以及系统的可维护性。 1. 关系型数据库&#xff08;SQL 数据库&#xff09;&#xff1a; …

关于“使用java中的二维矩阵方法生成二维码“ 以及 “Java加载外部字体文件时出错的原因“

生成二维码 铁铁们,这两日写了一个导出二维码的接口,要求有一个是在二维码下方生成字体,现在奉上生成二维码的代码: controller层 Operation(summary "导出机构二维码",description "导出机构二维码")GetMapping("/orgCode")public void getO…

Session缓存、Hibernate处理对象的状态了解

Session接口 Session接口是Hibernate向应用程序提供的操纵数据库的最主要的接口&#xff0c;它提供了基本的保存&#xff0c;更新&#xff0c;删除和查询的方法。 Session是有一个缓存, 又叫Hibernate的一级缓存 session缓存是由一系列的Java集合构成的。当一个对象被加入到…