SPI和API的区别?

服务提供者接口(SPI)应用程序编程接口(API)之间的区别是什么?

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

117250 次浏览

我认为SPI通过实现API的某些特性,然后通过服务查找机制将自身注册为可用,从而嵌入到更大的系统中。API由最终用户应用程序代码直接使用,但可以集成SPI组件。这就是封装和直接使用之间的区别。

在Java世界中,不同的技术都是模块化的,并且可以“插入”到应用服务器中。这两者之间是有区别的

  • 应用服务器
    • (SPI)
    • 李< / ul > < / >
    • 插拔技术
      • (API)
      • 李< / ul > < / >
      • 最终用户应用程序

      这类技术的两个例子是JTA(事务管理器)和JCA (JMS或数据库适配器)。但还有其他原因。

      这种可插技术的实现者必须在应用程序服务器中实现可插的SPI,并提供供最终用户应用程序使用的API。来自JCA的一个例子是ManagedConnection接口,它是SPI的一部分,而连接是最终用户API的一部分。

  • API是类/接口/方法/…你调用和使用来实现一个目标,并且
  • SPI是类/接口/方法的描述…为了达到一个目标,你必须努力。

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

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

然而,有时它们会重叠。Connection接口这两个 SPI和API:当你使用JDBC驱动程序时,你经常使用它,它需要由JDBC驱动程序的开发人员实现。

有效的Java,第二版:

服务提供者框架是一个 包含多个服务的系统 提供者实现服务,而 系统实现 对其客户机可用,解耦

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

a的可选第四个成分 服务提供者框架是一个 服务提供者接口,其中 提供程序实现创建 他们的服务实例 实现。在没有的时候 服务提供者接口, 实现由 类名和实例化 (项目53)。在这种情况下 JDBC,连接扮演的部分 服务接口, 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();
}
}

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

API是一个通用术语——应用程序编程接口(Application Programming Interface)的首字母缩写——它指的是软件公开的某些东西(在Java中,通常是一些Java类),允许其他软件与其通信。

SPI代表服务提供者接口。它是特定于API的所有东西的子集,在这种情况下,库提供了由应用程序(或API库)调用的类,并且通常会改变应用程序能够做的事情。

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

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

API的用户很少需要查看或与SPI类通信,反之亦然。

在NetBeans中,当你看到术语SPI时,它通常是指模块可以在运行时注入的类,这些类允许NetBeans做新事情。例如,有一个用于实现版本控制系统的通用SPI。不同的模块为CVS、Subversion、Mercurial和其他版本控制系统提供了该SPI的实现。然而,处理文件的代码(API端)不需要关心是否有版本控制系统,或者它是什么。

服务提供者接口是所有提供者都必须实现的服务接口。如果现有的提供者实现都不适合您,那么您需要编写自己的服务提供者(实现服务接口)并在某处注册(请参阅Roman的有用文章)。

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

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

一个例子是JNDI:

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

为了更好地理解,请参阅下面的JNDI体系结构。

Enter image description here

API代表应用程序编程接口,其中API是访问某种软件或平台提供的服务/功能的方法。

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

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

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

从API发展的角度来看,添加对客户端来说根本不是问题

但是API一旦被客户端使用,就不能(也不应该)修改/删除 除非有适当的沟通,否则就彻底退化了 客户期望< / p >

另一部分的SPI是针对提供者的,具有以下属性:

>—>SPI是一种扩展/改变软件或平台行为的方法(可编程vs。 编程)< / p >

->SPI的进化不同于API的进化,SPI的移除不是一个问题

添加SPI接口会导致问题,可能会破坏现有的实现

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

有一个方面似乎没有被强调太多,但对于理解为什么以及何时使用API/SPI非常重要。

API/SPI的分离只在你实际编写一个平台时才需要,而且它预计会不断发展。如果你写一个API和“know",它将不需要任何未来向后兼容的改进,没有真正的理由将你的代码分成两部分。此外,如果你根本不写平台(你有所有的API消费者代码在控制之下),唯一的好处将是很好的&干净的对象设计,因为你可以在任何时候重构你需要的东西。

但是,一旦您的平台至少有一个第三方客户端,并且您希望以向后兼容的方式进行更改,那么您很可能应该使用API/SPI拆分。

让我们在一个著名的Java对象CollectionCollections上展示它。


API: Collections是一组实用程序静态方法。通常表示API对象的类被定义为final,因为它可以确保(在编译时)没有客户端可以“implement"该对象,并且它们可以依赖于“calling"它的静态方法,例如。

Collections.emptySet();

由于所有的客户端都是“calling"而不是“implementing", JDK的作者在JDK的未来版本中将自由添加新方法放入Collections对象中。他们可以确定它不会破坏任何客户端,即使可能有数百万的使用。


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

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


你可能已经看到了这一点。如果你把这两个部分合并到一个类中,你的API将被阻止任何添加。这也是为什么优秀的Java api和框架不公开abstract class的原因,因为它们会阻碍它们未来在向后兼容性方面的发展。

如果仍然不清楚,我建议检查这个页面,它更详细地解释了上面的内容。


(*)注意,在Java 1.8引入接口中定义的default方法的概念之前,这是正确的。