什么是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不支持

什么是JDK的SPI机制的更多相关文章

  1. dubbo源码分析4——SPI机制_ExtensionFactory类的作用

    ExtensionFactory的源码: @SPI public interface ExtensionFactory { /** * Get extension. * * @param type o ...

  2. NIO 源码分析(04) 从 SelectorProvider 看 JDK SPI 机制

    目录 一.SelectorProvider SPI 二.SelectorProvider 加载过程 2.1 SelectorProvider 加载 2.2 Windows 下 DefaultSelec ...

  3. JDK源码系列(一) ------ 深入理解SPI机制

    什么是SPI机制 最近我建了另一个文章分类,用于扩展JDK中一些重要但不常用的功能. SPI,全名Service Provider Interface,是一种服务发现机制.它可以看成是一种针对接口实现 ...

  4. jdk和dubbo的SPI机制

    前言:开闭原则一直是软件开发领域中所追求的,开闭原则中的"开"是指对于组件功能的扩展是开放的,是允许对其进行功能扩展的,“闭”,是指对于原有代码的修改是封闭的,即不应该修改原有的代 ...

  5. JDK SPI 机制

    一.概述 最早看到 SPI 这个机制是在 dubbo 实现 中,最近发现原来也不是什么新东西,竟然就是 JDK 中内置的玩意,今天就来一探究竟,看看它到底是什么玩意! SPI的全称是 Service ...

  6. Dubbo的SPI机制与JDK机制的不同及原理分析

    从今天开始,将会逐步介绍关于DUbbo的有关知识.首先先简单介绍一下DUbbo的整体概述. 概述 Dubbo是SOA(面向服务架构)服务治理方案的核心框架.用于分布式调用,其重点在于分布式的治理. 简 ...

  7. Java是如何实现自己的SPI机制的? JDK源码(一)

    注:该源码分析对应JDK版本为1.8 1 引言 这是[源码笔记]的JDK源码解读的第一篇文章,本篇我们来探究Java的SPI机制的相关源码. 2 什么是SPI机制 那么,什么是SPI机制呢? SPI是 ...

  8. JDK中的SPI机制

    前言 最近学习类加载的过程中,了解到JDK提供给我们的一个可扩展的接口:java.util.ServiceLoader, 之前自己不了解这个机制,甚是惭愧... 什么是SPI SPI全称为(Servi ...

  9. Dubbo SPI机制之一JDK中的SPI

    首先简单阐述下什么是SPI:SPI 全称为 (Service Provider Interface) ,是JDK内置的一种服务提供发现机制.目前有不少框架用它来做服务的扩展发现,简单来说,就是一种动态 ...

随机推荐

  1. Java实现猜底牌问题(贪婪法)

    1 问题描述 设计一种策略,使在下面的游戏中,期望提问的次数达到最小.有一副纸牌,是由1张A,2张2,3张3,-9张9组成的,一共包含45张牌.有人从这副牌洗过的牌中抽出一张牌,问一连串可以回答是或否 ...

  2. Linux权限管理命令chown、chgrp、umask详解

    命令chown详解 命令chown,所在路径为: 可以看到,这个命令的路径为:/usr/bin/chown ,所以它的执行权限是所有用户 命令的基本功能是改变文件或目录的所有者(只有root可以进行, ...

  3. JDBC连接泄露问题的排查过程总结

    当前使用的Spring JDBC版本是5.0.0.RC1,HikariCP版本是3.1.0. 今天测试同学反馈在前端页面点击次数多了,就报500错误,数据显示不出来.于是我在后台服务日志中观察发现Hi ...

  4. python json unicode utf-8处理总结

    1.直接输出字典中文 在python中经常遇见直接print dict(字典),或者dict转json,但是没有给特定的参数,然后打印json字符串,输出的中文就成了unicode码的情况,如下: d ...

  5. Hadoop之hadoop fs和hdfs dfs、hdfs fs三者区别

      适用范围 案例 备注 小记 hadoop fs 使用范围最广,对象:可任何对象       hadoop dfs 只HDFS文件系统相关       hdfs fs 只HDFS文件系统相关(包括与 ...

  6. Flask简单http接口实现

    # flask demo from flask import Flask, request app = Flask(__name__) # http://127.0.0.1:8080 @app.rou ...

  7. AbstractCollection类中的 T[] toArray(T[] a)方法源码解读

    一.源码解读 @SuppressWarnings("unchecked") public <T> T[] toArray(T[] a) { //size为集合的大小 i ...

  8. 在PyQt5中显示matplotlib绘制的图形

    import sys from PyQt5.QtCore import Qt from PyQt5.QtWidgets import * from plot_pyqt import PlotCanva ...

  9. 详解CurrentHashMap之预习篇

    CurrentHashMap的出现时为了解决HashMap的高并发导致OOM的缺陷,并且能够保证高性能读取.那么解读CurrentHashMap需要具备哪些知识的呢? HashMap 解读 Java ...

  10. phpstorm里面无法配置deployment?

    我的preference里面找不到deployment是什么回事啊? 解决方案: 导致这个问题的原因是PHPStorm的plugins里面没有Remote Hosts Access这个插件,安装一下这 ...