SPI和API的区别?

服务提供者接口(SPI)和应用程序编程接口(API)有什么区别?

更具体地说,对于Java库,是什么使它们成为API和/或SPI?

  • 这个API是你调用和用来实现目标的类/接口/方法/ …的描述
  • SPI是为了实现目标而扩展和实现的类/接口/方法/ …的描述

换句话说,API告诉你一个特定的类/方法为你做了什么,SPI告诉你你必须做什么来符合。

通常API和SPI是分开的。 例如,在JDBC中, Driver类是SPI的一部分:如果您只是想使用JDBC,则不需要直接使用它,但是实现JDBC驱动程序的每个人都必须实现该类。

但是有时它们重叠。 Connection接口 既是 SPI又是API:当你使用JDBC驱动程序时,你可以经常使用它,它需要由JDBC驱动程序的开发人员来实现。

从有效的Java第二版:

服务提供者框架是多个服务提供者实现服务的系统,并且系统使得实现对其客户可用,从而将它们从实现中解耦。

服务提供者框架有三个基本组成部分:服务提供者实现的服务接口; 供应商注册API,系统用来注册实施,让客户访问他们; 和一个服务访问API,客户用它来获取服务的一个实例。 服务访问API通常允许但不要求客户端指定一些标准来select提供者。 在没有这样的规范的情况下,API返回一个默认实现的实例。 服务访问API是构成服务提供者框架的基础的“灵活的静态工厂”。

服务提供者框架的可选第四个组件是服务提供者接口,提供者实现这些接口以创build其服务实现的实例。 在没有服务提供者接口的情况下,实现通过类名进行注册并reflection性地实例化(条目53)。 在JDBC的情况下,Connection扮演服务接口的一部分,DriverManager.registerDriver是提供者注册API,DriverManager.getConnection是服务访问API,Driver是服务提供者接口。

服务提供者框架模式有许多变种。 例如,服务访问API可以使用适配器模式返回比提供者所需更丰富的服务接口[Gamma95,p。 139]。 下面是一个简单的实现与服务提供者接口和默认提供者:

 // Service provider framework sketch // Service interface public interface Service { ... // Service-specific methods go here } // Service provider interface public interface Provider { Service newService(); } // Noninstantiable class for service registration and access public class Services { private Services() { } // Prevents instantiation (Item 4) // Maps service names to services private static final Map<String, Provider> providers = new ConcurrentHashMap<String, Provider>(); public static final String DEFAULT_PROVIDER_NAME = "<def>"; // Provider registration API public static void registerDefaultProvider(Provider p) { registerProvider(DEFAULT_PROVIDER_NAME, p); } public static void registerProvider(String name, Provider p){ providers.put(name, p); } // Service access API public static Service newInstance() { return newInstance(DEFAULT_PROVIDER_NAME); } public static Service newInstance(String name) { Provider p = providers.get(name); if (p == null) throw new IllegalArgumentException( "No provider registered with name: " + name); return p.newService(); } } 

API和SPI之间的区别在于API另外提供了一些具体的实现。 在这种情况下,服务提供商必须实现几个API(称为SPI)

一个例子是JNDI:

JNDI为上下文查找提供接口和一些类。 在IntialContext中提供了查找上下文的默认方法。 这个类在内部将使用SPI接口(使用NamingManager)来提供特定的实现。

请参阅下面的JNDI架构以获得更好的理解。

在这里输入图像说明

API代表应用程序编程接口,其中API是访问由某种软件或平台提供的服务/function的手段。

SPI代表服务提供者接口,其中SPI是注入,扩展或改变软件或平台行为的方式。

API通常是客户访问服务的目标,它具有以下属性:

– > API是访问服务以实现某种行为或输出的编程方式

– >从API演进的angular度来看,对于客户来说,增加并不是问题

但是API一旦被客户使用,它就不能(也不应该)被修改/删除,除非有适当的通信,因为它完全降低了客户的期望

另一方面,SPI是针对提供商的,具有以下特性:

– > SPI是一种扩展/改变软件或平台行为的方式(可编程或可编程)

– > SPI进化不同于API进化,SPI去除不是问题

– >增加SPI接口会导致问题,并可能破坏现有的实现

更多解释请点击这里: 服务提供商接口

NetBeans常见问题解答: 什么是SPI? 它与API有什么不同?

API是一个通用术语 – 应用程序编程接口(Application Programming Interface)的缩写 – 它指的是某种软件公开的内容(通常是一些Java类),允许其他软件与之通信。

SPI代表服务提供商接口。 它是所有API的一个子集,特定于应用程序(或API库)所调用的类,并且通常会改变应用程序所能做的事情。

经典的例子是JavaMail。 它的API有两个方面:

  • API端 – 如果您正在编写邮件客户端或想要阅读邮箱,则调用该端口
  • 如果您提供有线协议处理程序以允许JavaMail与新型服务器(如新闻服务器或IMAP服务器)通信

API的用户很less需要查看或讨论SPI类,反之亦然。

在NetBeans中,当您看到术语SPI时,通常会谈到模块可以在运行时注入的类,这些类允许NetBeans执行新的操作。 例如,有一个通用的SPI来实现版本控制系统。 不同的模块为CVS,Subversion,Mercurial和其他版本控制系统提供SPI的实现。 但是,处理文件的代码(API方面)不需要关心是否有版本控制系统,或者它是什么。

服务提供者接口是所有提供者必须实现的服务接口。 如果没有一个现有的提供者实现为你工作,你需要编写自己的服务提供者(实现服务接口),并在某处注册(请参阅Roman的有用post)。

如果您正在重新使用服务接口的现有提供者实现,则基本上使用该特定提供者的API,其中包括所有服务接口方法以及一些自己的公共方法。 如果您在SPI之外使用提供者API的方法,则使用提供者特定的function。

我想通过实现API的某些function,SPI插入更大的系统,然后通过服务查找机制注册自己。 API直接由最终用户应用程序代码使用,但可能集成了SPI组件。 这是封装和直接使用的区别。

在Java世界中,不同的技术意味着模块化和“可插入”到应用程序服务器中。 那之间有一个区别

  • 应用程序服务器
    • [SPI]
  • 可插拔技术
    • [API]
  • 最终用户应用程序

这种技术的两个例子是JTA(事务pipe理器)和JCA(JMS或数据库的适配器)。 但也有其他的。

这种可插拔技术的实施者必须实现SPI才能在应用程序中插入。 服务器并提供供最终用户应用程序使用的API。 JCA的一个例子是作为SPI一部分的ManagedConnection接口,以及作为最终用户API一部分的Connection 。

有一个方面似乎没有被突出显示,但是理解API / SPI分裂存在的原因是非常重要的。

只有当平台预期发展时才需要API / SPI拆分。 如果你编写一个API并且“知道”它不需要任何未来的改进,那么没有真正的理由把你的代码分成两部分(除了做清洁对象devise)。

但是,这种情况几乎从来就不是这样,人们需要有自由将API与未来需求一起发展 – 以向后兼容的方式。

请注意,以上所有内容都假设您正在构build其他人使用和/或扩展的平台,而不是您自己的API,因为您可以控制所有客户端代码,因此可以根据需要进行重构。

让我们来看一个众所周知的Java对象CollectionCollections


API: Collections是一组实用的静态方法。 通常代表API对象的类被定义为final因为它确保(在编译时)没有客户端能够“实现”该对象,并且可以依赖于“调用”其静态方法,例如

 Collections.emptySet(); 

由于所有客户端都是“调用”而不是“实现” ,因此JDK的作者可以在将来的JDK版本的Collections对象中自由添加新的方法 。 他们可以肯定,即使可能有数以万计的用法,也不能破坏任何客户。


SPI: Collection是一个接口,意味着任何人都可以实现自己的版本。 因此,JDK的作者不能添加新的方法,因为它会破坏所有编写自己的Collection实现(*)的客户端。

通常,当需要添加额外的方法时,需要创build新的接口,例如扩展前者的Collection2 。 然后,SPI客户端可以决定是否迁移到新版本的SPI,并实现它的附加方法或是否与旧版本保持一致。


你可能已经看到了这一点。 如果将两个部分组合到一个类中,则API将被阻止添加。 这也是为什么好的Java API和框架没有公开abstract class因为它们会阻碍它们在向后兼容方面的未来发展。

如果有些东西还不清楚,我build议查看这个更详细的解释上面的页面 。


(*)注意,只有在引入接口中定义的default方法概念的Java 1.8之前,才是如此。