SPI和API之间的区别?


Answers:


394
  • API是您为实现目标而调用和使用的类/接口/方法/ ...的描述,以及
  • SPI是您为实现目标而扩展和实现的类/接口/方法/ ...的描述。

换句话说,API会告诉您特定的类/方法为您执行什么操作,而SPI则告诉您必须执行哪些操作才能符合要求。

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

但是,有时它们会重叠。Connection接口两个 SPI和API:当您使用JDBC驱动程序,它需要通过JDBC驱动程序的开发者来实现您可以使用它定期。


听起来不错。只是SPI和API之间的界线可能有点灰白,因为大多数API都有抽象类/接口供用户实现以完成工作……
kctang 2010年

1
@koss:大多数API?我知道你的意思,但我不太看那些东西。
Joachim Sauer 2010年

2
API对开发人员而言更为相关,而SPI对供应商而言则更为相关。
Azfar Niaz 2014年

7
@AzfarNiaz:好吧,因为供应商雇用开发人员来构建他们的产品与开发人员相关;-)
Joachim Sauer

在Java中,注释是SPI的一部分吗?例如,如果我必须添加@SomeAnnotation我的类以使其被某个框架使用,那么SomeAnnotation.class即使我没有在技术上扩展或实现它,该注释类也将被视为SPI的一部分吗?
亚当·伯利

59

有效的Java,第二版

服务提供者框架是一个系统,其中多个服务提供者实现了一项服务,并且系统使实现可供其客户端使用,从而将它们与实现分离。

服务提供者框架包含三个基本组件:服务提供者实现的服务接口;提供者注册API,系统用来注册实现,使客户可以访问它们;和服务访问API,客户端使用该API来获取服务实例。服务访问API通常允许但不要求客户端指定一些用于选择提供程序的条件。在没有这种规范的情况下,API返回默认实现的实例。服务访问API是“灵活的静态工厂”,它构成了服务提供者框架的基础。

服务提供者框架的可选第四组件是服务提供者接口,提供者实施该接口以创建其服务实现的实例。在没有服务提供者接口的情况下,通过类名称注册实现,并以反射方式实例化(项目53)。对于JDBC,Connection充当服务接口的一部分,DriverManager.registerDriver是提供程序注册API,DriverManager.getConnection是服务访问API,Driver是服务提供程序接口。

服务提供者框架模式有多种变体。例如,使用适配器模式[Gamma95,p。1],服务访问API可以返回比提供者所需的服务接口更丰富的服务接口。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();
    }
}

3
感谢您对服务提供商框架的很好描述。这将是有用的。但是,它并没有清楚地描述api和spi之间的“区别”,所以我在这里不会打勾... ;-)
kctang

23

当API另外提供一些具体的实现时,就会出现API与SPI之间的区别。在这种情况下,服务提供商必须实现一些API(称为SPI)

一个例子是JNDI:

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

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

在此处输入图片说明


22

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

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

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

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

->从API演变的角度来看,添加对于客户端来说完全没有问题

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

另一方面,SPI面向提供者,并具有以下属性:

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

-> SPI的演进与API的演进不同,在SPI移除方面不是问题

->添加SPI接口将导致问题并可能破坏现有的实现

有关更多说明,请单击此处:服务提供商接口


12

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

API是一个通用术语-应用程序编程接口的缩写-它表示某个软件公开的某些东西(在Java中,通常是某些Java类),它允许其他软件与其通信。

SPI代表服务提供商接口。它是所有事物的子集,可以是特定于API的特定情况,在这种情况下,库提供了由应用程序(或API库)调用的类,并且通常会更改应用程序能够执行的操作。

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

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

API的用户很少需要查看或交谈SPI类,反之亦然。

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


5

有一个方面似乎并没有特别强调,但是对于理解API / SPI分离背后的原因非常重要。

仅在预期平台发展时才需要API / SPI拆分。如果您编写了一个API并“知道”它以后将不再需要任何改进,则没有真正的理由将您的代码分为两部分(除了进行干净的对象设计之外)。

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

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

让我们在一个众所周知的Java对象Collection和上显示它Collections


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

Collections.emptySet();

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


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

通常,当需要添加其他方法时,Collection2需要创建新接口,例如,扩展前一个接口。然后,SPI客户端可以决定是迁移到新版SPI并实施其另一种方法,还是坚持使用旧版方法。


您可能已经明白了这一点。如果将这两部分组合到一个类中,则将阻止您的API进行任何添加。这也是优秀的Java API和框架之所以不公开的原因,abstract class因为它们会阻止它们在向后兼容性方面的未来发展。

如果仍然不清楚,建议您查看此页面,其中更详细地说明了上述内容。


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


4

我想通过实现API的某些功能,然后将其自身注册为可通过服务查找机制将SPI插槽插入更大的系统中。最终用户应用程序代码直接使用API​​,但可以集成SPI组件。封装和直接使用之间的区别。


感谢克里斯的回复。现有Java(例如Servlet,JDBC等)库的任何示例?...就像如何使它们成为API / SPI。很难仅凭描述就可以看出差异。
kctang

1
API是JDBC,JDBC的SPI是数据库连接器接口,SPI开发人员可以使用它创建新的数据库连接器,开发人员可以选择使用这些数据库连接器。
克里斯·丹尼特

4

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

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


2

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

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

此类技术的两个示例是JTA(事务管理器)和JCA(用于JMS或数据库的适配器)。但是还有其他。

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.