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

SPI机制

1、什么是SPI,有什么作用?

SPI ,(service provider interface),翻译过来就是,服务者提供接口,是一种服务发现机制。

他允许在 “运行时动态的加载实现特定接口的类”,而不需要在代码中显示指定该类,从而实现服务的解耦。如图所示,调用方制定接口标准,服务提供者实现对应的接口来提供服务。这样调用方就可以动态的使用不同服务提供方的服务了。

比如最经典的jdbc,提供了统一的接口 java.sql.Driver,mysql或者oracle实现这个接口,提供自己的服务。这样,我们在项目使用的过程中,只要添加对应的依赖,就可以使用对应的数据库了。

2、spi是如何使用的?

1、首先作为调用方,制定一个统一的接口。

package com.example.testspi;

/**
 * TestSpiService
 *
 * @since 2024/12/28
 */
public interface TestSpiService {
    void test();
}

2、作为服务方,依赖调用方,然后实现这个接口 (注意了,这里不是调用方依赖服务方了)

package com.example.testspi.provider;

import com.example.testspi.TestSpiService;

/**
 * Provider01
 *
 * @since 2024/12/28
 */
public class Provider01 implements TestSpiService {
    @Override
    public void test() {
        System.out.println("Provider01 is execute...");
    }
}
package com.example.testspi.provider;

import com.example.testspi.TestSpiService;

/**
 * Provider01
 *
 * @since 2024/12/28
 */
public class Provider02 implements TestSpiService {
    @Override
    public void test() {
        System.out.println("Provider02 is execute...");
    }
}

3、然后在服务方的资源下面,归档一个文件。

注意:

  1. 文件归档的路径必须是 META-INF/services
  2. 文件名必须是要实现的接口:com.example.testspi.TestSpiService
  3. 文件内容是实现这个接口的实现类,如果有多个就换行写
com.example.testspi.provider.Provider01
com.example.testspi.provider.Provider02

4、如何调用?

使用 ServiceLoader.load(Class<S> service)进行调用

    @Test
    void testSpi() {
        ServiceLoader<TestSpiService> load = ServiceLoader.load(TestSpiService.class);
        for (TestSpiService next : load) {
            next.test();
        }
    }

-----
Provider01 is execute...
Disconnected from the target VM, address: '127.0.0.1:64558', transport: 'socket'
Provider02 is execute...

3、源码跟踪

1、ServiceLoader.load(Class<S> service)方法会创建一个 ServiceLoader对象,这个对象会生成一个懒加载的迭代器。

# 源码

    public void reload() {
        providers.clear();
        lookupIterator = new LazyIterator(service, loader);
    }

    private ServiceLoader(Class<S> svc, ClassLoader cl) {
        service = Objects.requireNonNull(svc, "Service interface cannot be null");
        loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
        acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
        reload();
    }

 

2、当遍历ServiceLoader对象时,会调用迭代器的next方法,然后调用上述懒加载迭代器的hasNextService方法,然后获取对应文件里面的全限定名,然后通过反射的方式,进行调用。

这里解释了为什么对归档的文件路径和名称都有要求。

4、Springboot中的spring.factories文件。

Springboot启动的时候,会去classpath下的META-INF/spring.factories路径加载对应的内容,然后根据其中定义的key来加载对应value的自动配置类。

--- 这个也是用到了SPI机制。

为什么需要这个文件呢?

我们知道springboot启动的时候会自动扫描指定路径下面带@component的bean,但是如果是依赖了第三方组件,我们没有办法穷举这些路径。spring.factories 文件的作用是让 Spring Boot 在启动时自动加载第三方 JAR 包中的配置类,并通过自动配置机制注册 Bean 和其他组件。这大大简化了开发者的工作,特别是在使用第三方库时,减少了大量的手动配置。


http://www.kler.cn/a/457307.html

相关文章:

  • Redis(基础篇 + 实践篇 )
  • 低代码引擎插件开发:开启开发的便捷与创新之路
  • vue2+echarts实现水球+外层动效
  • Ubuntu 24.04 LTS 解决网络连接问题
  • JVM实战—8.如何分析jstat统计来定位GC
  • OpenCV的人脸检测模型FaceDetectorYN
  • YOLO11全解析:从原理到实战,全流程体验下一代目标检测
  • Spring Boot使用多线程
  • JVM(Java虚拟机)的组成部分详解
  • Redis 在小型项目中的实战运用
  • 单纯形法Simplex Method
  • 零跑汽车一路狂飙
  • 基于Qt的qss登录界面优化
  • 深入解析Spring Boot中的@ConfigurationProperties注解
  • 深入理解 LeetCode 978:最长湍流子数组
  • FPGA随记---时序约束
  • API安全学习笔记
  • QML学习(三) QML 的基本语法介绍
  • Yocto 项目中的交叉编译:原理与实例
  • 若依整合 Gitee 登录
  • BGP路由常用的属性
  • C语言-详细讲解-给定数字n,生成共有n个括号的所有正确的形式
  • Stream `Collectors.toList()` 和 `Stream.toList()` 的区别(Java)
  • python 不应该将列表作为函数的默认参数
  • 工业大数据分析算法实战-day14
  • 【每日学点鸿蒙知识】节点析构问题、区分手机和pad、 Navigation路由问题、Tabs组件宽度、如何监听Map