Java-SPI机制

发布时间:2024年01月24日
SPI基本概念

SPI(Service Provider Interface)是一种服务发现机制,为某个接口寻找服务实现的机制。这有点类似 IoC 的思想,将装配的控制权移交到了程序之外。SPI 将服务接口和具体的服务实现分离开来,将服务调用方和服务实现者解耦

SPI 和 API的区别

在这里插入图片描述
API:实现方提供了接口和实现,调用方调用实现方的接口进行使用,这就是 API ,即接口和实现都是在实现方
SPI:接口存在于调用方这边时,就是 SPI ,由接口调用方确定接口规则,然后由不同的厂商去根据这个规则对这个接口进行实现,从而提供服务

实例

SLF4J (Simple Logging Facade for Java)是 Java 的一个日志门面(接口),其具体实现有几种,比如:Logback、Log4j、Log4j2 等等,而且还可以切换,在切换日志具体实现的时候我们是不需要更改项目代码的,只需要在 Maven 依赖里面修改一些 pom 依赖就好了
在这里插入图片描述
简单的代码示例
1)定义接口
新建logger接口

public interface Logger {
    void info(String msg);
    void debug(String msg);
}

2)LoggerService
主要是为服务使用者提供特定接口,它不实现接口,但其可以拥有具体的接口实现方法
它能提供功能的核心原因是ServiceLoader 代码中会涉及ServiceLoader的使用,可以先看。这里只需要知道ServiceLoader可以帮服务提供者(LoggerSerive)做以下事情:
1.LoggerService将服务接口注册到/META-INF/services,ServiceLoader通过 URL 工具类从该目录下面找到对应的文件
2.读取这个文件的名称找到对应的待实现的接口
3.通过 InputStream 流将文件里面的具体实现类的全类名读取出来
4.根据获取到的全类名,先判断跟 spi 接口是否为同一类型,如果是的,那么就通过反射的机制构造对应的实例对象,将构造出来的实例对象添加到服务提供者的Providers 的列表中

public class LoggerService {
    private static final LoggerService SERVICE = new LoggerService();

    private final Logger logger;
	//实现Logger接口的方法类
    private final List<Logger> loggerList;
    private LoggerService() {
        ServiceLoader<Logger> loader = ServiceLoader.load(Logger.class);
        List<Logger> list = new ArrayList<>();
        for (Logger log : loader) {
            list.add(log);
        }
        // LoggerList 是所有 ServiceProvider
        loggerList = list;
        if (!list.isEmpty()) {
            // Logger 只取一个
            logger = list.get(0);
        } else {
            logger = null;
        }
    }

    public static LoggerService getService() {
        return SERVICE;
    }

    public void info(String msg) {
        if (logger == null) {
            System.out.println("info 中没有发现 Logger 服务提供者");
        } else {
            logger.info(msg);
        }
    }
    public void debug(String msg) {
        if (loggerList.isEmpty()) {
            System.out.println("debug 中没有发现 Logger 服务提供者");
        }
        loggerList.forEach(log -> log.debug(msg));
    }
}

此时并没有为 Logger 接口提供任何的实现,所以输出结果中没有按照预期打印相应的结果。

新建一个项目具体实现Logger接口

Logback

实现logger接口

public class Logback implements Logger {
    @Override
    public void info(String s) {
        System.out.println("Logback info 打印日志:" + s);
    }

    @Override
    public void debug(String s) {
        System.out.println("Logback debug 打印日志:" + s);
    }
}

在 src 目录下新建 META-INF/services 文件夹,然后新建文件 xxxx.spi.Logger (SPI 的全类名 即接口的全类名),文件里面的内容是:xxxx.spi.service.Logback (Logback 的全类名,即 SPI 的实现类的包名 + 类名)

测试
public class TestJavaSPI {
    public static void main(String[] args) {
    	//loggerService中可以加载具体的实现类
        LoggerService loggerService = LoggerService.getService();
        loggerService.info("你好");
        loggerService.debug("测试Java SPI 机制");
    }
}

ServiceLoader(源码部分阅读待补充)

接下来,详细看看ServiceLoader
这个类是一个 final 类型的,所以是不可被继承修改,同时它实现了 Iterable 接口。之所以实现了迭代器,是为了方便后续我们能够通过迭代的方式得到对应的服务实现。

public final class ServiceLoader<S> implements Iterable<S>{ xxx...}
文章来源:https://blog.csdn.net/weixin_44925329/article/details/135791769
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。