当前位置: 首页 > article >正文

java中SPI(服务提供者的接口)

java中SPI(服务提供者的接口)

    • 一:什么是SPI
    • 二:java SPI示例
      • 1.SPI服务提供方
      • 2.SPI服务应用方开发者
    • 三:JavaSPI 机制的核心-ServiceLoader

一:什么是SPI

SPI:“服务提供者的接口”,是一种服务发现机制

用于实现框架或库的扩展点,允许在运行时动态地插入或更换组件实现。

它提供了一个框架(JDK1.6后ServiceLoader)来发现和加载服务实现,使得软件模块能够灵活地选择和使用不同的服务提供商。

在这里插入图片描述

在java中通俗讲就是:

  • 对框架或第三方jar包提供者来说可制定规范,提供给开发者可扩展性
  • 对开发者来说可以根据需要轻松替换框架或第三方jar包中提供了SPI机制的接口的实现

二:java SPI示例

1.SPI服务提供方

SPI服务提供方架构图:
在这里插入图片描述
定义接口规范:

public interface SpiService {
    /**
     * 呼叫方式
     */
    void call();
}

加载具体的服务实现:

package com.lmy.config;

import com.lmy.service.SpiService;

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

/**
 * @author : lmy
 * @date : 2024/9/14 上午 11:35
 * 加载具体的服务实现
 */
public class SpiServiceLoader {
    private static volatile SpiServiceLoader LOADER;

    private final SpiService spiService;

    private final List<SpiService> spiServiceList;

    /**
     * 加载服务
     * */
    private SpiServiceLoader() {
        ServiceLoader<SpiService> loader = ServiceLoader.load(SpiService.class);
        List<SpiService> list = new ArrayList<>();
        for (SpiService spiService : loader) {
            list.add(spiService);
        }
        spiServiceList = list;
        if (!list.isEmpty()) {
            // 取第一个
            spiService = list.get(0);
        } else {
            spiService = null;
        }
    }

    /**
     * SpiServiceLoader 单例加载
     * */
    public static SpiServiceLoader getLOADER() {
        if (LOADER == null) {
            synchronized (SpiServiceLoader.class) {
                if (LOADER == null) {
                    LOADER = new SpiServiceLoader();
                }
            }
        }
        return LOADER;
    }


    public void call(){
        if(spiServiceList.isEmpty()){
            System.out.println("SpiService服务未加载!");
        }else {
            SpiService spiService = spiServiceList.get(0);
            spiService.call();
        }
    }

}

默认实现:

package com.lmy.service.impl;

import com.lmy.service.SpiService;

/**
 * @author : lmy
 * @date : 2024/9/14 上午 10:58
 * 默认实现
 */
public class SpiServiceImpl implements SpiService {
    @Override
    public void call() {
        System.out.println("默认手机呼叫");
    }
}

指定服务实现方式:
须在resource下创建META-INF.services,文件名为接口全限定类名,配置为需要被加载的接口实现类的全限定类名
在这里插入图片描述

com.lmy.service.impl.SpiServiceImpl

项目打包发布本地:
在这里插入图片描述

2.SPI服务应用方开发者

开发者引入jar包使用服务:

<dependency>
    <groupId>com.lmy</groupId>
    <artifactId>SPI-interface</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>
package com.lmy.Spi;

import com.lmy.config.SpiServiceLoader;
import org.junit.Test;

/**
 * @author : lmy
 * @date : 2024/9/14 上午 11:48
 */
public class SpiTest {

    @Test
    public void spiTest () {
        SpiServiceLoader loader = SpiServiceLoader.getLOADER();
        loader.call();
    }
}

执行结果:
在这里插入图片描述
开发者根据需要扩展替换为自己的服务实现:

package com.lmy.Spi.service;

import com.lmy.service.SpiService;

/**
 * @author : lmy
 * @date : 2024/9/14 下午 2:09
 */
public class SpiServiceNewImpl implements SpiService {
    @Override
    public void call() {
        System.out.println("卫星直呼");
    }
}

在这里插入图片描述

com.lmy.Spi.service.SpiServiceNewImpl

执行结果:
在这里插入图片描述

三:JavaSPI 机制的核心-ServiceLoader

上面代码可见是通过ServiceLoader 去加载具体的服务实现的
在这里插入图片描述
ServiceLoader 是从JDK1.6 开始提供的一个类,用于加载服务提供者。

进入源码可见:
其中 String PREFIX = “META-INF/services/”;
这个就是JDK的SPI功能规定的具体服务实现的配置信息文件所在的目录 META-INF/services/
在这里插入图片描述
JDK的SPI规定 服务实现者需要在 META-INF/services/ 目录下 新建文件名为 SPI接口全限定类名的文件
文件内容为 服务实现者需要被加载的具体类的全限定类名

具体源码底层实现参考:https://blog.csdn.net/qq_37883866/article/details/139000021 中第3点


http://www.kler.cn/news/313358.html

相关文章:

  • 项目实训:CSS基本布局理解——WEB开发系列38
  • js中两种异步方式:async+await以及then
  • 梧桐数据库(WuTongDB):Volcano/Cascades 优化器框架简介
  • 毕业设计选题:基于ssm+vue+uniapp的捷邻小程序
  • Linux系统编程(基础指令)上
  • 《动手深度学习》线性回归简洁实现实例
  • 【Webpack--013】SourceMap源码映射设置
  • windows环境下配置MySQL主从启动失败 查看data文件夹中.err发现报错unknown variable ‘log‐bin=mysql‐bin‘
  • 使用vite+react+ts+Ant Design开发后台管理项目(二)
  • SpringBoot:关于Redis的配置失效(版本问题)
  • 6. Python 输出长方形,直角三角形,等腰三角形
  • 【Linux基础IO】深入Linux文件描述符与重定向:解锁高效IO操作的秘密
  • 解决“Windows系统中以管理员身份运行程序时无法访问映射的网络磁盘”的问题
  • C# WPF如何实现数据共享
  • C#使用实体类Entity Framework Core操作mysql入门:从数据库反向生成模型2 处理连接字符串
  • 2024年上海小学生古诗文大会倒计时一个月:做2024官方模拟题
  • 人家90年代就尝试过的模式:我们所热衷的“数科公司”
  • 基于spring的ssm整合
  • 航空航司reese84逆向
  • linux文件同步、传输
  • 数据结构不再难懂:带你轻松搞定图
  • linux-L6 linux管理服务的启动、重启、停止、重载、查看状态命令
  • EmguCV学习笔记 VB.Net 12.3 OCR
  • OpenAI GPT o1技术报告阅读(4)- 填字游戏推理
  • 【Git 操作】Git 的基本操作
  • Elasticsearch:检索增强生成背后的重要思想
  • 【VUE3.0】动手做一套像素风的前端UI组件库---先导篇
  • 社团周报系统可行性研究-web后端框架对比-springboot,django,gin
  • Linux内核学习之 -- ARMv8中的Current宏
  • 自注意力与多头自注意力的区别