Java SPI 原理、样例

news2024/11/17 12:21:24

在 Java 中,SPI(Service Provider Interface)全称为服务提供者接口,它是一种用于实现框架扩展和插件化的机制。

一、SPI 作用

允许在运行时动态地为接口查找服务实现,而不需要在代码中显式地指定具体的实现类。

这使得框架具有更好的可扩展性,第三方可以通过实现特定的接口来为框架提供额外的功能。这在很多框架和库中都有广泛的应用,例如数据库驱动、日志框架等。

二、基本原理

1、定义服务接口:首先定义一个接口或抽象类,这是服务的规范。
2、实现服务接口:编写接口的具体实现类。
3、注册服务实现:在 META-INF/services 目录下创建一个以接口全限定名为文件名的文件,并在该文件中列出所有实现类的全限定名。
4、加载服务实现:使用 ServiceLoader 类来加载和使用服务实现。

三、代码样例

1、定义服务接口

假设现在有一个权威机构,比如 Java,它需要对数据存储进行规范。

它定义了一个数据存储接口,和一个加载实现类的工具类。

package com.storage.specification;

/**
 * 数据存储接口
 */
public interface StorageProvider {

    void save(String data);
}
---------------------------------------------------
package com.storage.specification.spi;

import com.storage.specification.StorageProvider;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ServiceLoader;

/**
 * 加载 存储服务 工具
 */
public class ProviderLoader {

    public static StorageProvider getStorageProvider(ClassLoader classLoader) {
        // 从类路径 resources/META-INF/services 下加载 存储服务提供者
        ServiceLoader<StorageProvider> loader = ServiceLoader.load(StorageProvider.class, classLoader);
        Iterator<StorageProvider> iterator = loader.iterator();
        List<StorageProvider> providers = new ArrayList<>();
        while (iterator.hasNext()) {
            providers.add(iterator.next());
        }
        return providers.get(0);
    }
}

 步骤:

1)用IDEA 新建一个 maven 项目,取名叫 storage-specification

72210ef906724b369f01d2e4e93435c1.png

 

2)新建上述两个类

ef918c0b36394b809b2bf1b165f76832.png

 

3)install 项目到本地仓库,以便后面项目可以用 gav 坐标引用到

3f98b8a6f289464a86ba527dbecfacf3.png

 

2、实现服务接口

 现在有两个供应商 Mysql 和 Redis,它们拿到 Java 给的规范,分别提供他们的 二进制文件 存储实现和内存数据库实现。

Mysql 实现:

1)用IDEA 新建一个 maven 项目,取名叫 mysql-storage-provider

ce6ec51b40b84682bb9cf38d585bbeba.png

 

2)pom 文件依赖 存储规范 gav

d659cb7e4fe64bfa974a7c4a1aeb8ef5.png

 

3) 编写实现接口

package com.mysql.storage;

import com.storage.specification.StorageProvider;

public class MysqlStorageProvider implements StorageProvider {

    @Override
    public void save(String s) {
        System.out.println("mysql save " + s);
    }
}

4)在项目 resources/META-INF/services 目录下

创建一个以接口全限定名 

com.storage.specification.StorageProvider 为文件名的文件(注意不要带后缀),

并在该文件中列出所有实现类的全限定名 com.mysql.storage.MysqlStorageProvider

83c56e97667248b4802bbde0f36f0013.png

 

5)install 项目到本地仓库,以便后面项目可以用 gav 坐标引用到

422d9523acff44959d660b62988bc35f.png

 以同样的方式创建一个 Redis 项目,取名 redis-storage-provider

576d491552644239b74f4243976001cb.png

 

区别只在

1)实现类不同

package com.redis.storage;

import com.storage.specification.StorageProvider;

public class RedisStorageProvider implements StorageProvider {

    @Override
    public void save(String s) {
        System.out.println("redis save " + s);
    }
}

2) 实现类的全限定名不同

f9505b6029a4405ebff457d9ce1d567d.png

 

3)同样 install 项目到本地仓库,以便后面项目可以用 gav 坐标引用到

8a66d82e0ea14976b27b0ea2c2cfbdfa.png

3、定义服务使用者

1)用 IDEA 新建一个 maven 项目,取名叫 storage-user

b92732ff51ce4073879f42fdd7b8bada.png

2)pom 依赖引入 规范 gav 和 mysql gav

<dependencies>
        <dependency>
            <groupId>com.storage.specification</groupId>
            <artifactId>storage-specification</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>com.mysql.storage</groupId>
            <artifactId>mysql-storage-provider</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
</dependencies>

 3) 调用 mysql 存储

0e235b38189546b28c369ee0e7627049.png

4)切换 redis 依赖

<dependencies>
        <dependency>
            <groupId>com.storage.specification</groupId>
            <artifactId>storage-specification</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>com.redis.storage</groupId>
            <artifactId>redis-storage-provider</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
</dependencies>

 5)调用 redis 存储

可以看到,我们只修改了依赖的实现包,并没有修改代码,就实现了服务的替换。

而且我们的代码中并没有显示地调用服务的实现类。

355d9b822bfa4533b64df3954ee6ef98.png

四、总结

1、SPI 是 JAVA 提供给我们的一种用于实现框架扩展和插件化的机制。它可以让我们在不修改代码的时候,很轻松地替换服务的实现。

2、Spring 有自己的 SPI,Spring 的 SPI 路径是 resources/META-INF/spring.factories。

3、SPI 机制在框架集成中很常见。像 javax.validation 就是通过 SPI 的方式集成了 HibernateValidator 实现。

 

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

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

相关文章

跨多场景帧重建DENSER:使用小波估计进行城市动态场景重构

Abstract 本文提出了一种名为DENSER的高效方法&#xff0c;该方法利用三维高斯点云(3DGS)技术来重建动态城市环境。尽管通过神经辐射场(NeRF)隐式方法和3DGS显式方法的若干场景重建技术在较复杂的动态场景中展示了出色的效果&#xff0c;但在建模前景物体的动态外观时仍存在挑…

PY32F002B

墨水屏&#xff1a; 前景和背景 在屏幕和图像处理中&#xff0c;前景和背景是两个重要的概念&#xff1a; 前景&#xff08;Foreground&#xff09;&#xff1a; 指的是图像或屏幕上最显著的部分&#xff0c;通常是用户关注的主要内容。例如&#xff0c;在一张照片中&#xf…

【开源免费】基于SpringBoot+Vue.JS技术交流分享平台(JAVA毕业设计)

博主说明&#xff1a;本文项目编号 T 053 &#xff0c;文末自助获取源码 \color{red}{T053&#xff0c;文末自助获取源码} T053&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析…

基于STM32热力二级管网远程监控系统设计(论文+源码)_kaic

摘 要 城市集中供热的运行管理中&#xff0c;热力二级管网作为供暖系统的重要的一环&#xff0c;通过对供热管网的远程集中监控、调节和对温度、压力、流量等参数的采集分析及处理&#xff0c;可以实现日常生产调度管理的自动化&#xff0c;提高运行管理效率。同时&#xff0c…

论文速递 | Management Science 8月文章合集

编者按 在本系列文章中&#xff0c;我们对顶刊《Management Science》于8月份发布文章中进行了精选&#xff08;共9篇&#xff09;&#xff0c;并总结其基本信息&#xff0c;旨在帮助读者快速洞察行业最新动态。 推荐文章1 ● 题目&#xff1a;Optimal Mechanism Design with …

TikTok不符合推荐页资格是为什么?该如何解决?

TikTok推荐页&#xff0c;就是平台上的For You一栏&#xff0c;即是TikTok的核心功能之一&#xff0c;它会根据用户的兴趣和行为推送个性化内容。然而&#xff0c;并非所有视频都有机会进入推荐页。如果你在TikTok上发布的视频显示不符合推荐页的资格&#xff0c;那么很可能是由…

记一次因视频编码无法在浏览器播放、编码视频报错问题

起因 ... f cv2.VideoWriter_fourcc(*h264) ...我这边使用h264编码会提示 OpenCV: FFMPEG: tag 0x34363268/h264 is not supported with codec id 27 and format mp4 / MP4 (MPEG-4 Part 14) OpenCV: FFMPEG: fallback to use tag 0x31637661/avc1 [ERROR:02.711] global /i…

AI模型托管数量突破百万大关

B站&#xff1a;啥都会一点的研究生公众号&#xff1a;啥都会一点的研究生 AI圈又有哪些新鲜事&#xff1f; Hugging Face AI模型托管数量突破百万大关 AI托管平台Hugging Face迎来里程碑&#xff0c;其托管的AI模型数量已超过100万个&#xff0c;标志着AI产业的蓬勃发展。H…

Java_集合_双列集合_Map

第一章Map集合 Map是双列集合顶级接口 什么叫做双列集合:一个元素有两部分构成:key和value -> 键值对 1.1.HashMap 常用方法: V put(K key, V value) -> 添加元素,返回的是被替换的value值 V remove(Object key) ->根据key删除键值对,返回的是被删除的value…

Servlet——springMvc底层原理

我们也先了解一下什么的动态资源&#xff0c;什么是静态资源。 静态资源&#xff1a;无需程序运行就可以获取的资源&#xff08;照片、html、css、js等&#xff09; 动态资源&#xff1a;需要通关程序运行才可以获得的资源。 &#xff08;其实动态、静态的资源都与Servlet有…

智慧水利综合解决方案

1. 智慧水利综合解决方案概述 智慧水利综合解决方案旨在通过集成先进技术&#xff0c;实现水利管理的智能化和高效化。该方案涵盖平台建设、业务系统建设和系统集成服务三大应用场景&#xff0c;通过数字孪生、GIS平台开发等技术手段&#xff0c;全面提升水利行业的管理能力和…

Android页面跳转与返回机制详解

在Android开发中&#xff0c;页面跳转是实现应用功能交互的重要手段之一。本文将从Activity之间的跳转、Activity与Fragment之间的跳转、Fragment之间的跳转以及页面返回的问题四个方面进行详细解析。 一、Activity之间的跳转 Activity是Android应用的基本构建块&#xff0c;…

【C++笔记】初始模版和STL简介

【C笔记】初始模版和STL简介 &#x1f525;个人主页&#xff1a;大白的编程日记 &#x1f525;专栏&#xff1a;C笔记 文章目录 【C笔记】初始模版和STL简介前言一.初始模版1.1泛型编程1.2函数模版1.3类模板 二.STL简介2.1什么是STL2.2STL的版本2.3STL的六大组件2.4STL的重要…

9.28作业

QQ登录界面的实现 代码展示 wight.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QLineEdit> #include <QPushButton> #include <QVBoxLayout> #include <QRadioButton> #include <QIcon> #include <QLabel>…

TypeScript介绍和安装

TypeScript介绍 TypeScript是由微软开发的一种编程语言&#xff0c;它在JavaScript的基础上增加了静态类型检查。静态类型允许开发者在编写代码时指定变量和函数的类型&#xff0c;这样可以在编译时捕获潜在的错误&#xff0c;而不是等到运行时才发现问题。比如&#xff0c;你…

优雅使用 MapStruct 进行类复制

前言 在项目中&#xff0c;常常会遇到从数据库读取数据后不能直接返回给前端展示的情况&#xff0c;因为还需要对字段进行加工&#xff0c;比如去除时间戳记录、隐藏敏感数据等。传统的处理方式是创建一个新类&#xff0c;然后编写大量的 get/set 方法进行赋值&#xff0c;若字…

讯飞星火编排创建智能体学习(二)决策节点

目录 概述 决策节点 文生图节点 连接节点 测试结果 概述 在上一篇博文讯飞星火编排创建智能体学习&#xff08;一&#xff09;最简单的智能体构建-CSDN博客&#xff0c;我介绍了编排创作智能体&#xff0c;这篇来介绍一下“决策节点”。 决策节点 在编排创作智能体中&…

YOLOv8最新改进2023 CVPR 结合BiFormer

1,原理部分 作为视觉转换器的核心构建块,衰减是捕获长距离依赖性的强大工具。然而,这种能力是有代价的:它会产生巨大的计算负担和沉重的内存占用,因为所有空间位置的成对标记交互都是计算的。一系列作品试图通过将手工制作和与内容无关的稀疏性引入 attention 来缓解这个问…

一文上手skywalking【上】

一、skywalking预览 1.1 skywalking 概述 ​ Apache SkyWalking, 适用于分布式系统的应用程序性能监控工具&#xff0c;专为微服务、云原生和基于容器的 &#xff08;Kubernetes&#xff09; 架构而设计。官方地址: https://skywalking.apache.org/ 适用于分布式系统的应用程…

从密码学看盲拍合约:智能合约的隐私与安全新革命!

文章目录 前言一、什么是盲拍合约&#xff1f;二、盲拍合约的优势1.时间压力的缓解2.绑定与秘密的挑战 三、盲拍合约的工作原理1.提交盲出价2.披露出价3.结束拍卖4.退款机制 四、代码示例总结 前言 随着区块链技术的发展&#xff0c;智能合约在各种场景中的应用越来越广泛。盲…