服务提供者接口(SPI)和应用程序编程接口(API)有什么区别?
更具体地说,对于Java库,是什么使它们成为API和/或SPI?
服务提供者接口(SPI)和应用程序编程接口(API)有什么区别?
更具体地说,对于Java库,是什么使它们成为API和/或SPI?
当前回答
服务提供者接口是所有提供者都必须实现的服务接口。如果现有的提供者实现都不适合您,那么您需要编写自己的服务提供者(实现服务接口)并在某处注册(请参阅Roman的有用文章)。
如果重用服务接口的现有提供者实现,基本上就是在使用该特定提供者的API,其中包括服务接口的所有方法以及它自己的一些公共方法。如果您在SPI之外使用提供者API的方法,那么您使用的是提供者特定的特性。
其他回答
API是类/接口/方法/…你用它来实现目标,还有 SPI是类/接口/方法的描述…你通过扩展和实现来实现一个目标。
换句话说,API告诉你一个特定的类/方法为你做了什么,而SPI告诉你必须做什么才能符合。
通常API和SPI是分开的。例如,在JDBC中,驱动程序类是SPI的一部分:如果你只是想使用JDBC,你不需要直接使用它,但是每个实现JDBC驱动程序的人都必须实现这个类。
然而,有时它们会重叠。Connection接口既是SPI也是API:当您使用JDBC驱动程序时,您通常会使用它,并且它需要由JDBC驱动程序的开发人员实现。
有一个方面似乎没有被强调太多,但对于理解为什么以及何时使用API/SPI非常重要。
API/SPI的分离只在你实际编写一个平台时才需要,而且它预计会不断发展。如果你写了一个API,并且“知道”它将来不需要任何向后兼容的改进,那么就没有真正的理由把你的代码分成两部分。此外,如果你根本不写平台(你有所有的API消费者代码在控制之下),唯一的好处将是良好和干净的对象设计,因为你可以在任何时候重构你需要的东西。
但是,一旦您的平台至少有一个第三方客户端,并且您希望以向后兼容的方式进行更改,那么您很可能应该使用API/SPI拆分。
让我们在一个众所周知的Java对象Collection和Collections上展示它。
Collections是一组实用程序静态方法。通常表示API对象的类被定义为final,因为它确保(在编译时)没有客户端可以“实现”该对象,并且它们可以依赖于“调用”其静态方法。
Collections.emptySet();
因为所有的客户端都是“调用”而不是“实现”,JDK的作者可以在未来版本的JDK中自由地向Collections对象中添加新方法。他们可以确定它不会破坏任何客户端,即使可能有数百万的使用。
SPI:集合是一个接口,它意味着任何人都可以实现自己的版本。因此,JDK的作者不能向其中添加新方法,因为这会破坏所有编写自己的Collection实现(*)的客户端。
通常情况下,当需要添加额外的方法时,需要创建新的接口,如Collection2,它是对前者的扩展。然后SPI客户端可以决定是否迁移到新版本的SPI并实现它的附加方法,或者是否坚持使用旧的方法。
你可能已经看到了这一点。如果你把这两个部分合并到一个类中,你的API将被阻止任何添加。这也是为什么优秀的Java api和框架不公开抽象类的原因,因为它们会阻碍它们未来在向后兼容性方面的发展。
如果还有什么不清楚的地方,我建议你去看看这个页面,上面有更详细的解释。
(*)注意,这只适用于Java 1.8,它引入了在接口中定义默认方法的概念。
我认为SPI通过实现API的某些特性,然后通过服务查找机制将自身注册为可用,从而嵌入到更大的系统中。API由最终用户应用程序代码直接使用,但可以集成SPI组件。这就是封装和直接使用之间的区别。
API和SPI之间的区别在于API还提供了一些具体的实现。在这种情况下,服务提供者必须实现一些api(称为SPI)
一个例子是JNDI:
JNDI为上下文查找提供了接口和一些类。在IntialContext中提供了默认的查找上下文的方法。该类将在内部使用SPI接口(使用NamingManager)实现特定于提供者的实现。
为了更好地理解,请参阅下面的JNDI体系结构。
服务提供者接口是所有提供者都必须实现的服务接口。如果现有的提供者实现都不适合您,那么您需要编写自己的服务提供者(实现服务接口)并在某处注册(请参阅Roman的有用文章)。
如果重用服务接口的现有提供者实现,基本上就是在使用该特定提供者的API,其中包括服务接口的所有方法以及它自己的一些公共方法。如果您在SPI之外使用提供者API的方法,那么您使用的是提供者特定的特性。