// 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();
}
}
9 回答
API是您调用并用于实现目标的类/接口/方法/ ...的描述,以及
SPI是您扩展和实现以实现目标的类/接口/方法/ ...的描述 .
换句话说,API告诉您特定的类/方法为您做了什么,SPI告诉您必须做什么来符合 .
通常API和SPI是分开的 . 例如,在JDBC中the Driver class是SPI的一部分:如果您只是想使用JDBC,则不需要直接使用它,但实现JDBC驱动程序的每个人都必须实现该类 .
然而,有时它们重叠 . The Connection interface是 both SPI和API:当您使用JDBC驱动程序时,它会定期使用它,并且需要由JDBC驱动程序的开发人员实现 .
来自Effective Java,第2版:
当API另外提供一些具体实现时,API和SPI之间的区别就出现了 . 在这种情况下,服务提供商必须实现一些API(称为SPI)
一个例子是JNDI:
JNDI为上下文查找提供接口和一些类 . IntialContext中提供了查找上下文的默认方法 . 此类内部将使用SPI接口(使用NamingManager)进行提供程序特定的实现 .
有关更好的理解,请参阅下面的JNDI体系结构 .
API 代表应用程序编程接口,其中API是用于访问由某种软件或平台提供的服务/功能的手段 .
SPI 代表服务提供者接口,其中SPI是注入,扩展或改变软件或平台行为的方式 .
API is normally target for clients to access a service and it has the following properties:
SPI on the other part are targeted for providers and has the following properties:
有关更多说明,请单击此处:Service Provider Interface
NetBeans的常见问题解答:What is an SPI? How is it different from an API?
我想通过实现API的某些功能将SPI插入更大的系统,然后通过服务查找机制将其自身注册为可用 . 最终用户应用程序代码直接使用API,但可以集成SPI组件 . 这是封装和直接使用之间的区别 .
服务提供者接口是所有提供者必须实现的服务接口 . 如果现有的提供程序实现都不适合您,则需要编写自己的服务提供程序(实现服务接口)并在某处注册(请参阅Roman的有用帖子) .
如果您正在重用服务接口的现有提供程序实现,那么您基本上使用的是该特定提供程序的API,其中包括服务接口的所有方法以及它自己的一些公共方法 . 如果您在SPI之外使用提供程序API的方法,那么您将使用提供程序特定的功能 .
在Java世界中,不同的技术意味着模块化并可“插入”应用程序服务器 . 然后有一个区别
应用程序服务器
[SPI]
可插拔技术
[API]
最终用户应用程序
这些技术的两个例子是JTA(事务管理器)和JCA(JMS或数据库的适配器) . 但还有其他人 .
然后,这种可插拔技术的实现者必须将SPI实现为可插入应用程序 . 服务器并提供最终用户应用程序使用的API . JCA的一个例子是ManagedConnection接口,它是SPI的一部分,Connection是最终用户API的一部分 .
有一个方面似乎没有被突出显示,但对于理解API / SPI拆分存在背后的原因非常重要 .
API/SPI split is only required when platform is expected to evolve. 如果您编写API和"know"它将不需要任何未来的改进,没有真正的理由将代码分成两部分(除了进行干净的对象设计) .
但这种情况几乎从未出现过这种情况,人们需要自由地将API与未来的需求一起发展 - 以向后兼容的方式 .
请注意,以上所有假设您构建的平台是其他人使用和/或扩展的,而不是您自己的API,您可以控制所有客户端代码,因此可以根据需要进行重构 .
让我们在一个众所周知的Java对象
Collection
和Collections
上展示它 .API:
Collections
是一组实用程序静态方法 . 通常,代表API对象的类被定义为final
,因为它确保(在编译时)没有客户端可以"implement"该对象,并且它们可以依赖于其静态方法,例如,由于所有客户端都是"calling"但不是"implementing",因此JDK的作者在JDK未来版本的
Collections
对象中是 free to add new methods . 他们可以肯定它不会破坏任何客户,即使可能有数百万的用法 .SPI:
Collection
是一个界面,暗示任何人都可以实现自己的版本 . 因此,JDK can't add new methods into it 的作者会破坏所有编写自己的Collection
实现(*)的客户端 .通常,当需要添加额外的方法时,新的接口,例如,需要创建扩展前者的
Collection2
. 然后SPI客户端可以决定是否迁移到新版本的SPI并实现它的附加方法或是否坚持使用旧版本 .你可能已经看到了这一点 . 如果您将两个部分组合到一个类中,则会阻止您的API添加任何内容 . 那个's also the reason why good Java APIs and Frameworks don'暴露
abstract class
,因为他们会阻止他们在向后兼容方面的未来发展 .如果仍然不清楚,我建议检查this page,这将更详细地解释上述内容 .
(*)请注意,这只适用于Java 1.8,它引入了接口中定义的
default
方法的概念 .