Java SPI机制

1. 什么是SPI

SPI 全称 Service Provider Interface,是 Java 提供的一套用来被第三方实现或者扩展的接口,它可以用来启用框架扩展和替换组件。 SPI 的作用就是为这些被扩展的 API 寻找服务实现。

2. SPI和API的使用场景

API (Application Programming Interface) 在大多数情况下,都是实现方制定接口并完成对接口的实现,调用方仅仅依赖接口调用,且无权选择不同实现。 从使用人员上来说,API 直接被应用开发人员使用。

SPI (Service Provider Interface) 是调用方来制定接口规范,提供给外部来实现,调用方在调用时则选择自己需要的外部实现。 从使用人员上来说,SPI 被框架扩展人员使用。

3. SPI的简单实现

3.1 首先定义一组接口

public interface JavaSPITest {
    void spiTest(String msg);
}

3.2 编写接口实现(可以有多个)

这里我定义了两个实现

public class JavaSPITest implements com.lizq.JavaSPITest {

    @Override
    public void spiTest(String msg) {
        System.out.println("JavaSPITest:" + msg);
    }
}
public class JavaSPITest2 implements com.lizq.JavaSPITest {

    @Override
    public void spiTest(String msg) {
        System.out.println("JavaSPITest2:" + msg);
    }
}

3.3 新建META-INF/services目录及对应文件

在 resources 目录下新建 META-INF/services 目录,并且在这个目录下新建一个与上述接口的全限定名一致的文件,在这个文件中写入接口的实现类的全限定名:
在这里插入图片描述
在这里插入图片描述

3.4 通过ServiceLoader加载实现类并调用

ServiceLoader<JavaSPITest> spiTests = ServiceLoader.load(JavaSPITest.class);
for (JavaSPITest u : spiTests) {
	u.spiTest("Test Java SPI");
}

这样一个简单的spi的demo就完成了。可以看到其中最为核心的就是通过ServiceLoader这个类来加载具体的实现类的。

4. SPI原理解析

public final class ServiceLoader<S> implements Iterable<S> {
    //扫描目录前缀
    private static final String PREFIX = "META-INF/services/";

    // 被加载的类或接口
    private final Class<S> service;

    // 用于定位、加载和实例化实现方实现的类的类加载器
    private final ClassLoader loader;

    // 上下文对象
    private final AccessControlContext acc;

    // 按照实例化的顺序缓存已经实例化的类
    private LinkedHashMap<String, S> providers = new LinkedHashMap<>();

    // 懒查找迭代器
    private java.util.ServiceLoader.LazyIterator lookupIterator;

    // 私有内部类,提供对所有的service的类的加载与实例化
    private class LazyIterator implements Iterator<S> {
        Class<S> service;
        ClassLoader loader;
        Enumeration<URL> configs = null;
        String nextName = null;

        //...
        private boolean hasNextService() {
            if (configs == null) {
                try {
                    //获取目录下所有的类
                    String fullName = PREFIX + service.getName();
                    if (loader == null)
                        configs = ClassLoader.getSystemResources(fullName);
                    else
                        configs = loader.getResources(fullName);
                } catch (IOException x) {
                    //...
                }
                //....
            }
        }

        private S nextService() {
            String cn = nextName;
            nextName = null;
            Class<?> c = null;
            try {
                //反射加载类
                c = Class.forName(cn, false, loader);
            } catch (ClassNotFoundException x) {
            }
            try {
                //实例化
                S p = service.cast(c.newInstance());
                //放进缓存
                providers.put(cn, p);
                return p;
            } catch (Throwable x) {
                //..
            }
            //..
        }
    }
}

参考文档:
https://www.cnblogs.com/jy107600/p/11464985.html

### Java 中的 SPI 机制详解 #### 定义与作用 SPI 是 Service Provider Interface 的缩写,是 JDK 内置的一种服务发现机制。通过 SPI 可以使接口与其具体实现解耦合,在运行时动态加载指定的服务实现[^1]。 #### 工作原理 当应用程序启动时,JVM会扫描 `META-INF/services` 文件夹下的配置文件。对于每一个定义好的接口,都会有一个对应的配置文件来指明具体的实现类名称。例如,如果存在名为 `com.example.LoggerService` 接口,则应在资源路径下创建一个名为 `com.example.LoggerService` 的文件,并在此文件中记录该接口的具体实现类名[^2]。 #### 使用案例分析 考虑如下代码片段: ```java public class TestJavaSPI { public static void main(String[] args) { LoggerService loggerService = LoggerService.getService(); loggerService.info("你好"); loggerService.debug("测试Java SPI 机制"); } } ``` 上述例子展示了如何利用 SPI 加载并调用不同日志框架的日志功能。这里假设 `LoggerService` 是一个抽象出来的用于获取日志服务实例的方法;而实际执行的日志操作则由特定的日志库(比如 Logback)完成。因此可以看到控制台输出了来自Logback的信息级别和调试级别的消息。 #### 实现方式概述 要让某个接口支持 SPI 功能,开发者只需要遵循一定的约定即可: - 创建接口; - 在项目的 `resources/META-INF/services/` 下建立同包名相同命名规则的文字文件; - 文本文件内容即为对应接口实现者的全限定类名列表,每行一条记录[^3]。 #### 关键特性总结 - **灵活性**: 不必硬编码依赖关系,允许外部插件形式接入新的业务逻辑处理单元。 - **可维护性**: 减少了修改源码的需求,便于后期维护和技术栈升级迁移工作。 - **扩展性强**: 支持多版本共存和平滑切换不同的算法策略或工具链组件。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值