一旦从捆绑上下文中检索到 OSGi 服务的实例,它是否会在服务停止时失效?
我最初的测试表明,即使在服务包停止后也可以使用服务实例,这与我对 OSGi 的动态特性的理解相矛盾。
我想这归结为从 OSGi 容器中的另一个包中检索服务(通过 ServiceTracker)实际上做了什么,它是创建一个新实例还是给你一个指向在容器中注册的实例的指针?
服务停止后使用服务实例有什么危险吗?
这是一个非常好的问题,所以我深入研究规范以寻找明确的答案。事实证明,有一整节都在讨论这个问题 - 请参阅OSGi Service Platform Core Specification, Release 4, Version 4.2的第 132 页开始的5.4 Stale References部分。
根据规范回答您的问题:
未注册的服务的行为是未定义的。此类服务可能会继续正常工作或自行决定抛出异常。
为了防止潜在的问题:
Bundles 必须侦听框架生成的事件以清理和删除过时的引用。
该规范还提供了一些提示如何最小化过时引用的后果。
你是对的,因为它与 OSGi 的动态特性相矛盾。我相信无法保证该服务将可用,尽管 OSGi 容器和服务本身的不同实现可能会有不同的行为。
例如,如果服务是使用 Spring DM 创建和注册的,那么检索到的服务实际上是底层实现的基于 Spring 的代理,并且实现仍然可以消失。因此,直接引用实现的服务引用可以防止该对象被删除,而基于代理的引用则不会。
OSGi 规范说:
Bundles 是在普通应用程序编程中可见的实体。例如,当一个包停止时,它的所有服务都将被注销。
因此,您应该无法从已停止的捆绑包中获取服务。但从技术上讲,它是可以使用的,至少只要你持有对服务对象的引用(没有人可以把它从你身边拿走,它不会被 GC 处理)。但我认为使用该服务并不省钱。它可能依赖于其他捆绑资源,这些资源在捆绑停止后不可用。
一旦从捆绑上下文中检索到 OSGi 服务的实例,它是否会在服务停止时失效?
不,引用本身不会失效。只要容器内有东西持有它,它也不能被 GC'ed。
但是,它是否仍然有用,仅取决于服务实现本身,而不是容器。
我最初的测试表明,即使在服务包停止之后也可以使用服务实例,这与我对 OSGi 的动态特性的理解相矛盾。
规范本身表明不应持有此类引用,但由实施者负责正确实施规范;意味着没有矛盾,只是您可以实施和部署不符合规范的行为的捆绑包。
我想这归结为从 OSGi 容器中的另一个包中检索服务(通过 ServiceTracker)实际上做了什么,它是创建一个新实例还是给你一个指向在容器中注册的实例的指针?
容器不会创建新的服务实例,除非涉及到 ServiceFactory(请参阅规范)。查找服务应始终为您提供指向容器中已注册实例的指针。
服务停止后使用服务实例有什么危险吗?
这仅取决于服务实现;但是,通过在捆绑包中执行此操作,您会自动创建不符合规范的捆绑包。
在实践中,许多服务都是为了释放资源和引用而实现的,并且不再正确响应。
关于您的问题,在服务停止后使用服务实例是否危险。引用 4.2 核心规范(5.4 Stale References):
未注册的服务的行为是未定义的。此类服务可能会继续正常工作或自行决定抛出异常。
我不想在这里引用规范的整个部分,但以下句子很好地讨论了使用过时引用的危险:
陈旧引用是对属于已停止的捆绑包的类加载器或与未注册的服务对象相关联的 Java 对象的引用。标准 Java 不提供任何通用方法来清理过时的引用,并且包开发人员必须仔细分析他们的代码以确保删除过时的引用。
陈旧的引用具有潜在的危害性,因为它们会阻碍 Java 垃圾收集器收集已停止捆绑包的类,甚至可能是实例。这可能会导致内存使用量显着增加,并可能导致更新本机代码库失败。强烈建议使用服务的捆绑包使用服务跟踪器或声明式服务。
服务参考永远不应该作为参考。您应该始终在运行时查找服务。但是,这会将您与 OSGI api 联系起来,但这并不总是需要的。
看看 - OSGI serviceTracker - OSGI 声明式服务 - OSGI BluePrint - Spring DM - Peaberry - iPojo
所有这些都为您提供动力,其中大多数都没有 OSGI api 可供使用。
问候,
莱恩·托伦