何时使用ServiceLoader而不是像OSGi这样的东西

19
2个回答

18
如果大多数情况下ServiceLoader适合您的需求,那说明您正在寻找通过类路径上存在文件来进行服务发现。这只是OSGi提供的功能的一小部分。
OSGi允许您在应用程序运行时动态安装捆绑包、发布服务、撤销广告并卸载捆绑包。此外,作为服务的消费者,您可以使用过滤谓词查询急切地查找它们,并检测提供服务的提供程序何时出现或消失。这些捆绑包不需要位于类路径上,并且可以以各种形式提供。我记得有两种:Jar文件和“解压缩目录”。
相比之下,ServiceLoader只做了一件事:它公开了可发现的工厂。通常,您将创建一个工厂样式接口,该接口需要一些参数来决定是否该提供程序可以提供适当的服务,例如将给定字符集名称映射到CharsetDecoder。对于获取和释放来自此类提供程序的服务,没有正式的协议。 OSGi规范了将消费者与服务绑定和解除绑定。当新提供程序上线时,消费者可以接收通知,而当消费者获取和释放服务实例时,提供程序可以接收通知。如果生命周期控制对您的服务很重要,而且您放弃了OSGi,那么您将不得不自己构建这个;ServiceLoader无法做到这一点。
或者,与急切地查找并使用服务不同,您可以采用更为被动的声明性方法,让其中一个OSGi依赖项管理器将您声明的需求与可用的服务提供程序进行匹配。有许多依赖项管理器可供选择。 Spring Dynamic Modules是最具实力的之一。
OSGi提供了许多其他“中间件”功能。我不会在这里向您推销它们,因为您的问题主要关注选择ServiceLoader可能会错过哪些功能。

谢谢 - 很难真正了解OSGi是什么。现在我只需要ServiceLocator所具有的功能,而且我很感激它已经内置了。我只是有点怀疑,因为之前从未听说过它,所以我以为它可能有问题或者什么的。 - Michael Neale
2
我在当前项目中使用了ServiceLoader,唯一让我感到沮丧的是:1)需要将扩展作为Jar文件推送到类路径上(我们的应用程序不鼓励最终用户对类路径进行调整),2)未指定相同服务接口的同级提供者将向消费者公开的顺序。后者使得很难融入“优先级”,例如用户提供的扩展应该优于应用程序的基本服务。因此,当提供者不会重叠或竞争时,最好使用ServiceLoader - seh

5
正如Seh所指出的,如果您只对简单的服务发现感兴趣,那么ServiceLoader是一种轻量级的方法,可以将消费者与提供者解耦。但它不提供任何有关组合服务的帮助。
例如,假设服务A需要使用服务B。这是一个“服务依赖”...但是如果B不可用,A应该怎么办?在OSGi中,我们可以安排如果B不可用,则A也不会可用--假设依赖关系是强制性的;我们还可以支持可选依赖项。另一方面,在使用ServiceLoader时,只要封装它的JAR文件在类路径上,服务A就无法控制其可用性...因此,即使没有必需的“后端”服务,它也必须提供其功能。
使用ServiceLoader时要记住的另一件事是尝试抽象查找机制。发布机制非常好、干净和声明性。但是查找(通过java.util.ServiceLoader)像地狱一样丑陋,实现为一个类路径扫描器,如果将代码放入任何没有全局可见性的环境(如OSGi或Java EE)中,则会出现严重问题。如果您的代码与之纠缠在一起,那么稍后在OSGi上运行它将会很困难。最好编写一个抽象,以后可以替换它。

2
你能详细说明一下针对ServiceLoader的简单抽象,以便日后更容易迁移到OSGi吗?我目前有一个框架,将其拆分为12或13个jar文件,并使用serviceloader机制来“扫描”类路径上的模块作为核心组件。我希望该框架未来在OSGi中运行良好,但不依赖于OSGi。到目前为止,我无法看到如何实现这种二元性。在使用ServiceLoader机制时,即使是半进半出OSGi,是否可能? - Chris
1
最好的抽象化应该是像依赖注入这样的东西。在需要某个服务“Foo”的代码中,不要尝试查找该服务,而是通过setFoo()方法允许它被注入。从其他类中,调用ServiceLoader来获取Foo的实例并将其注入到需要它的代码中。如果您以后转移到OSGi,则只需放弃第二个类并使用声明性服务或蓝图来处理注入OSGi服务。 - Neil Bartlett
“全局可见性”是什么意思?在这种情况下,查找机制如何会出现问题,例如在Java EE环境中? - du369
1
@du369 这是指应用程序类路径。如果您使用多个类加载器,则服务可能无法从一个模块对另一个模块可见,这取决于您如何设置这些类加载器。 - Neil Bartlett

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接