• 什么是JDK的SPI机制


    什么是SPI和API

     Application Programming Interface (API)?

    • The API is the description of classes/interfaces/methods/... that you call and use to achieve a goal, and

    • the SPI is the description of classes/interfaces/methods/... that you extend and implement to achieve a goal.

    SPI 全称为 (Service Provider Interface) ,是JDK内置的一种服务提供发现机制。SPI是一种动态替换发现的机制, 比如有个接口,想运行时动态的给它添加实现,你只需要添加一个实现。我们经常遇到的就是java.sql.Driver接口,其他不同厂商可以针对同一接口做出不同的实现,mysql和postgresql都有不同的实现提供给用户,而Java的SPI机制可以为某个接口寻找服务实现。

    首先放个图:我们在“调用方”和“实现方”之间需要引入“接口”,可以思考一下什么情况应该把接口放入调用方,什么时候可以把接口归为实现方。

    先来看看接口属于实现方的情况,这个很容易理解,实现方提供了接口和实现,我们可以引用接口来达到调用某实现类的功能,这就是我们经常说的api,它具有以下特征:

    1. 是对实现的说明(我可以给你提供什么)

    2. 组织上位于实现方所在的包中

    3. 实现和接口在一个包中

    当接口属于调用方时,我们就将其称为spi,全称为:service provider interface,spi的规则如下:

    1. 是对实现的约束(要提供这个功能,实现者需要做那些事情)

    2. 组织上位于调用方所在的包中

    3. 实现位于独立的包中(也可认为在提供方中)

    简而言之

    API会告诉您特定的类/方法为您执行什么操作,而SPI则告诉您必须执行哪些操作才能符合要求。通常,API和SPI是分开的。例如,在JDBC中,Driver类是SPI的一部分:如果只想使用JDBC,则不需要直接使用它,但是实现JDBC驱动程序的每个人都必须实现该类。但是,有时它们会重叠。Connection接口既是SPI,又是API:您在使用JDBC驱动程序时通常会使用它,并且需要由JDBC驱动程序的开发人员来实现。

     

    JDK

    在jdk6里面引进的一个新的特性ServiceLoader,从官方的文档来说,它主要是用来装载一系列的service provider。而且ServiceLoader可以通过service provider的配置文件来装载指定的service provider。当服务的提供者,提供了服务接口的一种实现之后,我们只需要在jar包的META-INF/services/目录里同时创建一个以服务接口命名的文件。该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。 可能上面讲的有些抽象,下面就结合一个示例来具体讲讲。

    public interface Search {
      public List<String> searchDoc(String keyword);  
    }

    文件搜索实现

    public class FileSearch implements Search{
      @Override
      public List<String> searchDoc(String keyword) {
          System.out.println("文件搜索 "+keyword);
          return null;
      }
    }

    数据库搜索实现

    public class DatabaseSearch implements Search{
      @Override
      public List<String> searchDoc(String keyword) {
          System.out.println("数据搜索 "+keyword);
          return null;
      }
    }

    接下来可以在resources下新建META-INF/services/目录,然后新建接口全限定名的文件:com.cainiao.ys.spi.learn.Search,里面加上我们需要用到的实现类

    com.github.spi.learn.FileSearch

    测试

    public class TestCase {
      public static void main(String[] args) {
          ServiceLoader<Search> s = ServiceLoader.load(Search.class);
          Iterator<Search> iterator = s.iterator();
          while (iterator.hasNext()) {
              Search search = iterator.next();
              search.searchDoc("hello world");
          }
      }
    }

    可以看到输出结果:文件搜索 hello world

    如果在com.github.spi.learn.Search文件里写上两个实现类,那最后的输出结果就是两行了。 这就是因为ServiceLoader.load(Search.class)在加载某接口时,会去META-INF/services下找接口的全限定名文件,再根据里面的内容加载相应的实现类。

    这就是spi的思想,接口的实现由provider实现,provider只用在提交的jar包里的META-INF/services下根据平台定义的接口新建文件,并添加进相应的实现类内容就好。

    那为什么配置文件为什么要放在META-INF/services下面? 可以打开ServiceLoader的代码,里面定义了文件的PREFIX如下:

    private static final String PREFIX = "META-INF/services/"

    Java SPI的使用很简单。也做到了基本的加载扩展点的功能。但Java SPI有以下的不足:

    • 需要遍历所有的实现,并实例化,然后我们在循环中才能找到我们需要的实现。

    • 配置文件中只是简单的列出了所有的扩展实现,而没有给他们命名。导致在程序中很难去准确的引用它们。

    • 扩展如果依赖其他的扩展,做不到自动注入和装配

    • 不提供类似于Spring的IOC和AOP功能

    • 扩展很难和其他的框架集成,比如扩展里面依赖了一个Spring bean,原生的Java SPI不支持

  • 相关阅读:
    centos7安装ImageMagick
    php安装imagemagick扩展
    php编译安装redis扩展
    springboot和springsecurity使用JWT令牌
    springboot和springsecurity整合OAuth2
    SpringSecurity 整合SpringBoot结合jwt与rsa实现分布式认证授权
    【名额有限】云开发AI拓展能力等你来体验!
    干货:如何借助小程序云开发实现小程序支付功能(含源码)
    云开发数据库又增新技能!
    聚焦“云开发圆桌论坛”,大前端Serverless大佬们释放了这些讯号!
  • 原文地址:https://www.cnblogs.com/munan56/p/12220623.html
Copyright © 2020-2023  润新知