SPI机制


一、SPI介绍

SPI全称Service Provider Interface,是Java的一套用来让第三方提供接口实现或者扩展接口的机制。
SPI可以很灵活的让接口和实现分离,让api提供者只提供接口,第三方来实现,然后可以使用配置文件的方式来实现替换或者扩展,在框架中比较常见,提高框架的可扩展性。

二、SPI和API区别

API: 当实现方提供了接口和实现,我们可以通过调用实现方的接口从而拥有实现方给我们提供的能力,这就是 API ,这种接口和实现都是放在实现方的。(如:AJAX向后端请求)
在这里插入图片描述

当接口存在于调用方这边时,就是 SPI ,由接口调用方确定接口规则,然后由不同的厂商去根据这个规则对这个接口进行实现,从而提供服务。
举个通俗易懂的例子:公司 H 是一家科技公司,新设计了一款芯片,然后现在需要量产了,而市面上有好几家芯片制造业公司,这个时候,只要 H 公司指定好了这芯片生产的标准(定义好了接口标准),那么这些合作的芯片公司(服务提供者)就按照标准交付自家特色的芯片(提供不同方案的实现,但是给出来的结果是一样的)。
在这里插入图片描述

三、Java SPI机制–ServiceLoader

ServiceLoader是Java提供的一种简单的SPI机制的实现,Java的SPI实现约定了以下三件事:

  1. 文件必须放在META-INF/services/目录底下
  2. 文件名必须为接口的全限定名
  3. 文件内容为接口实现类的全限定名
    这样就能够通过ServiceLoader加载到文件中接口的实现。

四、代码实现

spi-interface

定制接口标准
在这里插入图片描述

public interface HelloSPI {

    public void sayHello();
}

spi-provider

模拟第三方,实现接口。
写了两个实现HelloSPI接口的实现类
在这里插入图片描述
引入接口依赖

 <dependency>
     <groupId>com.it</groupId>
     <artifactId>spi-interface</artifactId>
     <version>1.0-SNAPSHOT</version>
 </dependency>

ImageSPI (HelloSPI接口实现类)

public class ImageSPI implements HelloSPI {
    @Override
    public void sayHello() {
        System.out.println("ImageSPI sayHello...");
    }
}

TextSPI (HelloSPI接口实现类)

public class TextSPI implements HelloSPI {
    @Override
    public void sayHello() {
        System.out.println("TextSPI sayHello...");
    }
}

com.it.spi.HelloSPI文件

com.it.spi.impl.ImageSPI
com.it.spi.impl.TextSPI

spi-user

模拟调用者,需引入接口依赖和第三方实现的依赖
在这里插入图片描述
引入依赖

  <dependency>
      <groupId>com.it</groupId>
      <artifactId>spi-interface</artifactId>
      <version>1.0-SNAPSHOT</version>
  </dependency>
  <dependency>
      <groupId>com.it</groupId>
      <artifactId>spi-privider</artifactId>
      <version>1.0-SNAPSHOT</version>
  </dependency>

自定义加载类,将实现HelloSPI接口的第三方实现加载并实例化。

  1. META-INF/services/ 接口全限定名,找到该文件
  2. 读取文件里的内容(实现类的全限定名),并且实例化,放到集合中
public class MyServiceLoader<T> {
    private Class service;
    private ClassLoader classLoader;
    private List<T> providers = new ArrayList<>();

    public MyServiceLoader(Class service){
        this.service = service;
        this.classLoader = Thread.currentThread().getContextClassLoader();
        load();
    }

    @SneakyThrows
    private void load(){
        Enumeration<URL> urls = classLoader.getResources("META-INF/services/" + service.getName());
        while(urls.hasMoreElements()){
            URL url = urls.nextElement();
            String path = url.getPath();
            FileReader fileReader = new FileReader(path);
            BufferedReader reader = new BufferedReader(fileReader);
            String className = reader.readLine();
            while(className != null){
                Class<?> aClass = Class.forName(className);
                if (service.isAssignableFrom(aClass)){
                    T o = (T) aClass.newInstance();
                    providers.add(o);
                }
                className = reader.readLine();
            }
        }
    }

    public List<T> getProviders(){
        return providers;
    }
}

主程序调用自定义加载方法,获取第三方实现类并调用其方法

public class Main {
    public static void main(String[] args) {
        MyServiceLoader<HelloSPI> loader = new MyServiceLoader<>(HelloSPI.class);
        List<HelloSPI> providers = loader.getProviders();
        for (HelloSPI provider : providers) {
            provider.sayHello();
        }
    }
}

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值