懒人遥控服务端

编程入门 行业动态 更新时间:2024-10-26 02:37:57

<a href=https://www.elefans.com/category/jswz/34/1741988.html style=懒人遥控服务端"/>

懒人遥控服务端

懒人遥控服务端

毫无疑问,OSGi中最难的主题是如何处理服务动态。 在本文中,我们将深入探讨问题的基本本质,然后提出解决问题的一些创新策略。 尽管假定基本熟悉 OSGi,但该技术的新读者也应该觉得这很有趣。 讨论将直接基于OSGi API,并且可以作为OSGi服务层的非正式介绍。 在本文的结尾,我们将介绍一些框架,这些框架捕获了我们开发的API模式,从而使开发人员不必直接处理OSGi。

问题

有两个关键因素使服务动态异常难以实现。

并发

在深入研究之前,我们应该先消化一个基本且令人吃惊的事实:实际上,OSGi框架不会运行它自己的线程! 它只是堆上的“死” 线程安全对象结构。 “主”线程用于设置此结构并启动初始捆绑包。 比起进入睡眠状态,它的功能仅仅是防止JVM由于缺少活动线程而关闭。 通常,仅在所有其他线程均已死的情况下,才在框架关闭序列结束时唤醒“主”线程。 它用于在死掉并让JVM退出之前执行一些最终清理。 这意味着所有有用的工作必须由在初始启动序列期间从捆绑包启动的线程来完成。 我们称这些为“活动捆绑包” 。 多数束通常是“被动束” 。 也就是说,它们不从BundleActivator.start()方法启动线程。 相反,他们设置了一些服务对象的导入,然后将它们组合成新的服务对象,最后将其导出。 在start()调用返回后,包就坐在那里,等待线程调用它的导出服务。 所有这些可能都是优雅而轻巧的,这也意味着OSGi框架不执行任何线程模型-它退避一步,让捆绑包在它们之间进行分类。 框架对象结构充当“被动捆绑包”(实际上是ID为0的捆绑包),仅当“主动捆绑包”中的线程调用来与另一个捆绑包或与框架本身进行某种交互时,才会变为动画。 因为在任何时候,随机数量的线程都可以调用OSGi内核,所以框架实现者可以为他们完成工作。 我们作为应用程序编码人员也不能免于遭受苦难。

并发因素是这样的:在任何时候OSGi应用程序都同时受到两个独立的控制流。 我们将它们称为“业务逻辑流”“动态管理流” 。 第一个代表应用程序完成的有用工作,与OSGi无关。 在这里,我们选择设计(线程池,工作队列等),并对我们的捆绑软件进行编码以遵循规则。 但是,第二个控制流程完全不在我们的控制范围之内 。 通常,它是在同时运行我们自己的管理应用程序处理捆绑软件生命周期(包括安装和卸载)时发生的。 通常有不止一个这样的应用程序-每个应用程序都有自己的线程规则,就像我们的应用程序一样。 一些示例包括通过HTTP进行配置,通过JMX甚至是telnet控制台界面进行管理和监视。 这些中的每一个都可以通过OSGi核心到达,在我们依赖的bundle上调用BundleActivator.stop() ,并导致我们需要的服务的退出。 发生这种情况时,我们必须准备好在释放服务对象方面进行合作。 用前面提到的第二个因素解释了这种奇怪的安排。

直接服务参考

第二个因素与捆绑之间交换对象的方式有关。 同样,OSGi也是非侵入性的且轻量级的:导入包具有对导出包所拥有的对象的直接引用 。 这种设计的主要好处是OSGi不会在捆绑包之间引入方法调用开销-调用服务与调用私有对象一样快。 缺点是导入包必须与导出包配合才能正确释放服务。 如果进口商保留有关终止服务的提法,则至少会发生两种有害影响:

  • 对陈旧服务的调用可能会崩溃。
    由于该服务不再由捆绑软件支持,因此调用该服务可能会由于所需的服务或其他资源不可用而导致异常。 即,进口商将永远不会正常工作,因为它不愿放弃陈旧的服务并尝试获得新的服务。
  • 由于ClassLoader保留而导致内存泄漏。
    即使卸载捆绑软件,该捆绑软件的ClassLoader也将保留在内存中。 显然,堆上的每个对象必须具有一个具体的实现类,在这种情况下,该类由死捆绑包的ClassLoader 。 即使导入程序通过从第三个库包加载的接口看到服务对象,也会发生此泄漏。

所有这一切意味着,导入程序必须跟踪服务的可用性,并保证在收到服务注销事件后的有限时间内,释放对服务对象的所有引用。 相反,当服务重新联机时,必须将其拾取并传播到捆绑服务中使用该服务的点。

解决方案

到目前为止,我们竭尽全力来描述..错误..服务动态的痛苦 。 现在我们很痛苦,让我们讨论一下补救办法。 目前,我们已经用尽了正确的进口商政策的主题。 现在,让我们添加一个服务导出策略。 进出口政策的总和​​应构成关于服务动态的完整理论。 我们将探讨两种出口政策及其相应的理论。

急于

这所学校虽然要求提供安全的服务电话。 它的座右铭是:

导出服务就是宣布它可以使用了

考虑一下这对于由导入对象组成的服务意味着什么。 这些对象称为“所需服务” 。 服务也可以是“可选的 -例如,日志记录。在急切的座右铭下,当必需的服务出现故障时,导出将不再可用。因此,也必须从OSGi服务注册表中将其撤回。这也是另一种方式-当需要时服务又回来了,复合服务必须再次注册,这导致服务注册和注销的级联,因为相关服务的链条汇聚在一起并分崩离析。实现这种闪烁行为的难度从硬到异常都不同。必须在公共对象内部以适当的同步来跟踪导出,而且这种动态依赖关系管理常常还需要跟踪来自非服务源的事件,这使得情况变得更加复杂,例如,我们跟踪动态配置以等待其生效。

让我们假设我们设法为每个捆绑包编写了所有样板。 现在想象一下,线程在执行业务控制流(即有用的工作)时如何在OSGi容器中竞争。 它将从驱动特定应用程序的活动捆绑包开始其路径。 一旦调用服务对象,它将离开它的“ home”捆绑包,并输入导出服务的捆绑包。 如果该服务又通过其他服务实现,则该线程将跳入每个服务的捆绑包,依此类推。 为了使原始服务呼叫成功,每一跳也必须成功。 事实证明,我们正在尝试实现一种事务性行为-对服务链(或更一般地说是树)的调用要么完全成功,要么因为根本没有注册根服务而不能首先进行。 在如此有力的保证下,活动捆绑包会提前(或急切地 )知道某些活动无法执行,并且可以采取替代措施。 例如,不是对错误做出React,而是主动执行相应的错误处理。

Eager模型所承诺的安全性取决于OSGi框架提供服务状态事件的特定方式。 通过将ServiceListener挂接到OSGi服务注册表来完成服务跟踪。 不管是好是坏,OSGi框架都会同步调用这些侦听 ,即在执行实际服务注销的管理控制流中。 这为管理和应用程序控制流程提供了一个在进口商捆绑包内开会的机会。 然后,导入器可以针对同一个专用锁同步两个流。 为什么这样 因为如果应用程序控制流当前正在访问服务,我们可以阻止管理控制流。 相反,如果管理流首先获得了锁定,则它将在释放锁定时将服务链级联。 然后,应用程序流程可以继续检查以查看服务是否可用。

不幸的是,显而易见,这种安全是不可能的 。 想象一下,一些管理控制流程开始执行并停止从业务控制流程的当前位置移开两跳的捆绑包。 由于业务流尚未进入已停止的捆绑软件,因此将无法阻止管理流关闭其服务。 结果,我们的线程将碰壁。 显然,沿着路径的每个单独的捆绑包不会进行任何数量的“本地”同步将保证整个路径的完整性。 需要的是第三方-某种事务管理器,用于在业务流程开始遍历之前锁定整个路径。 由于目前尚不存在这样的管理器,因此我们可以得出结论,服务闪烁无法防止由于服务消失而导致的错误。 我们只是必须接受我们需要处理此类错误并尝试制定有效的方案来做到这一点。

这就提出了一个问题,即是否还有其他好处可以证明由服务闪烁引起的复杂性。 我们可以辩称,尽管我们无法保证服务呼叫将成功,至少服务闪烁可以告诉我们服务呼叫保证失败的确切时间。 这使我们能够在所需服务中断后立即执行各种辅助React。 例如,如果捆绑包在您的IDE中绘制按钮,而直接或传递依赖关系消失了,则它可以弹出对话框或从工具栏隐藏按钮。 如果没有连锁服务的连锁破坏,按钮将在工具栏上就在那里,并且用户每次单击都会获得异常。 这是一个可疑的回报,几乎不能证明样板投资是合理的。 如果我们考虑其他并发症,这将变得更加缺乏吸引力。 为什么在持续2秒的例行捆绑更新期间我们应该大声吹喇叭? 也许我们应该只是“闪烁”工具栏上的按钮并推迟对话框,直到故障持续10秒钟以上。 此期限是否可以配置? 还有谁应该做出React-仅活动包还是服务链中的每个包? 由于我们不希望被对话框(或其他一般React)所淹没,因此必须引入一些应用程序范围的策略。 简而言之,我们付出了巨大的努力才能收回可疑的收益,而且副作用还给模块化应用程序带来了全新的横切关注点。

看到极端动态如何导致问题多于解决之外,让我们尝试将服务跟踪行为减少到最低限度。 由于Eager模型的大部分复杂性是过度强大的服务出口政策的结果,因此我们首先放松一下:

导出服务就是在包中声明一个入口点

由于导出只是声明,因此不需要任何动态闪烁。 我们只是接受由于缺少直接或传递服务依赖项而调用服务会导致异常的情况。 我们将此模型称为“懒惰”模型,因为在这里除非尝试尝试将其丢失,否则我们不会了解丢失的服务。 完整的动力学学说变成:

  • 显式注册仅在捆绑包启动期间使用。
    通常,捆绑软件应在BundleActivator.start()中遵循以下顺序:
    1. 进口
      下面介绍了如何轻松实现此目的。
    2. 构造
      从导入和实现类构建捆绑包内部结构。 将其根存储在激活器的非最终字段中。
    3. 启用
      如果这是一个活动捆绑包,则启动它的线程。
    4. 出口
      如果有要导出的对象,请立即注册它们,并将其ServiceRegistrations存储在激活器的非最终字段中。

  • 精心安排此顺序,以免在完全设置所有组件之前,不将捆绑软件的任何部分暴露给外部调用。 完成此序列后,将启动捆绑软件并将其挂接到服务注册表。 它的内部结构免于垃圾回收,因为它是从激活器内部引用的,而激活器又是从OSGi服务层引用的。 现在,管理控制流程可以离开激活器并开始其业务。 如果捆绑包已经启动了一些线程来执行业务流程,则它们可以在不再执行激活器之后继续进行工作。

  • 进口商快速失败
  • 必须跟踪每个导入的服务。 尝试使用缺少的服务时,将引发RuntimeException 。 此异常通常称为ServiceUnavailableException (或SUE)。
  • 服务错误的处理方式与常规RuntimeExceptions (故障)相同
    我们处理经典故障方案在使用服务对象时抛出的所有未经检查的异常(包括SUE):传播到顶级catch块(故障屏障),在堆栈展开或到达时在try/finally块中进行清理捕获块。 最后以某种合理的方式完成崩溃的活动。 详细地:
    1. 如果该服务是可选的,我们将捕获,处理并继续。
      如果服务对于手头的工作不是很关键,则无需崩溃。 运行时异常立即被捕获并记录下来。 采取了任何适当的故障转移措施,并愉快地进行了执行。 也就是说,我们将故障转换为偶然性 。 服务是否可选取决于具体的应用。 我们甚至可以想象部分可选的服务,其中只有一些方法调用被包装在try/catch故障屏障中,而其他方法导致更全面的崩溃。
    2. 如果需要该服务并且我们是被动捆绑,我们将清理自己的资源并让异常传播。
      被动捆绑包不会驱动业务逻辑,因此不拥有调用它们的活动。 因此,他们无权决定如何完成这些活动,并且应让异常传播到拥有的活动捆绑包。 他们仍然必须从try/finally块中清除与服务调用相关的任何内部资源。 因为良好的编码风格无论如何都需要执行这种清理,所以事实证明,对于被动捆绑,惰性服务动态不会产生任何作用。
    3. 如果需要该服务,并且我们是活动捆绑包 ,则将当前活动声明为崩溃,记录,清理,尝试应急措施。
      如果我们是导致崩溃活动的捆绑包,则我们有责任以一种或另一种方式完成它。 好的设计需要我们在业务逻辑代码周围包裹一个故障屏障,以吸收崩溃。 如果需要资源清理,我们照常进行。 比我们执行应用程序逻辑所指示的要多:显示错误对话框,发送错误响应等。
  • 显式服务注销仅在捆绑软件关闭期间使用。
    所有捆绑软件应在BundleActivator.stop()执行以下序列:
    1. 取消出口
      通过在先前存储的ServiceRegistration对象上调用unregister()来关闭所有导出的服务。 这是对所有当前客户端的通知,开始删除他们的引用,对OSGi框架的通知是停止让新客户端获得对该服务的引用。 在关闭开始时这样做是出于礼貌,因为在BundleActivator.stop()完成后,所有服务都会自动注销。
    2. 停用
      如果我们是一个活动包,请对线程进行正常关闭。 释放我们拥有的所有非堆资源:关闭套接字和文件,释放UI小部件等。这必须以线程安全的方式完成,因为合法的客户端调用可能仍在进行中。 此步骤的目标是将捆绑包的内容转换为快速失败的对象球。 从此以后,此球的功能将彻底关闭在关闭时已困在我们服务内的任何业务控制流。 由于停用是从管理控制流中同时执行的,因此很明显应该建立一些同步。 这表明,至少就正常关机而言,每个OSGi服务实现都必须为并发访问做好准备。 因为大多数OSGi服务都是并发的,所以额外的close()方法的适用范围不太高。 如果服务是无状态的,则可以免费停用-被困的业务控制流将崩溃,因为它们试图通过中断的服务导入跳入另一个捆绑软件。
    3. 分离
      这可以通过显式使BundleActivator包含的任何字段为BundleActivator 。 至此,我们从包中分离了上一步中创建的故障快速球。 一旦所有业务控制流陷入崩溃,球将被垃圾收集。 在stop()完成之后,捆绑软件仅应为其BundleActivator实例和ClassLoader消耗内存。 这些都由OSGi运行时直接管理。 如果某些管理控制流通过OSGi核心再次调用BundleActivator.start() (例如,用户在其JMX控制台中单击“启动捆绑包” BundleActivator.start() ,则捆绑包将再次变为运行时结构。

  • 关机策略的目标是,直到垃圾被收集之前,将捆绑服务保持在一致的“正确工作或干净崩溃”状态。 在启动序列中,我们不会在对象变得一致之前就将它们暴露给控制流,而在关闭序列中,我们会保持对象一致,直到垃圾被收集为止。 在非OSGi并发Java中也可以观察到这种“始终一致”的策略。 例如,关闭线程的常用方法是触摸布尔标志(调用unregister ),销毁线程使用的所有资源(中断导入,关闭套接字等),并使线程因自然原因而过期。

懒惰原则的优点是,我们设法将棘手的服务动态问题几乎完全折叠为适当处理异常的较简单问题。 我们通过将服务不一致引起的崩溃引导到每个非动态Java应用程序内置的错误处理路径中来实现这一目标。 事实证明,动态性并不那么可怕,它们通常迫使我们拥有良好的错误处理和清理设计。 即服务动态是关于安全崩溃的

对抗代码失真

我们的平滑计划存在很大的折痕-消耗服务的代码仍然很难编写,并且对我们其余的应用程序代码造成了破坏。 让我们回头查看并发和直接服务引用如何共同造成维护噩梦。 一句话:

在任何时候(并发),我们都可能需要刷新对服务对象的所有引用(直接引用)。

这意味着我们必须始终严格控制每个引用,这类似于C ++程序员面临的资源清理问题。 与JVM中的任何对象一样,可以通过两种方式引用服务对象:


    • 这些引用存储在对象字段中。 它们必须由我们的代码显式管理。 管理控制流涉及我们用来跟踪服务可用性的ServiceListener的堆引用。 因此,侦听器中的代码必须与接触堆引用的所有其他代码同步。
    • 叠放
      这些引用驻留在活动线程的堆栈上,并在它们的调用框架到期时自动清除。 管理控制流无法触摸堆栈引用,因此不需要同步

为了使事情易于管理,开发人员会Swift改变平衡,转而使用自动清理堆栈引用。 一般整个包包含每个服务的单个堆参考。 从那里可以将该服务作为方法参数传播到其使用站点。 如果无法做到这一点,则将构建get()方法的长路径以将服务拉至相应的使用站点。 不久,开发人员变得不知所措,并将Singleton模式应用于此服务参考。 这将一种代码失真替换为另一种,可能更糟的一种。

实现正确的惰性导入行为的另一种方法是将服务引用封装在一个holder对象中。 所有必需的同步,事件处理和清除都将封装在保持器中。 然后,我们可以将此持有人代替实际服务传播到所有服务使用站点。 这是一个简单的懒惰持有人:

import static org.osgi.framework.Constants.OBJECTCLASS;import static org.osgi.framework.ServiceEvent.UNREGISTERING;import org.osgi.framework.BundleContext;import org.osgi.framework.InvalidSyntaxException;import org.osgi.framework.ServiceEvent;import org.osgi.framework.ServiceListener;import org.osgi.framework.ServiceReference;public class ServiceHolder<T> implements ServiceListener {private final BundleContext bc;private final Class<T> type;private ServiceReference ref;private T service;/*** Called from BundleActivator.start().** (management control flow)*/public ServiceHolder(Class<T> type, BundleContext bc) {this.type = type;this.bc = bc;/* Track events for services of type T */try {bc.addServiceListener(this, "(" + OBJECTCLASS + "=" + type.getName() + ")");} catch (InvalidSyntaxException e) {throw new RuntimeException("Unexpected: filter is correct", e);}}/*** Called by the app when it needs the service.** (application control flow)*/@SuppressWarnings("unchecked")public synchronized T get() {/* Lazy-bind to a suitable service */if (service == null) {ref = bc.getServiceReference(type.getName());/* Can't find a service - fail fast */if (ref == null) {throw new RuntimeException("Service " + type + " unavailable.");}service = (T) bc.getService(ref);}return service;}/*** Called by the container when services come and go.** (management control flow)*/public synchronized void serviceChanged(ServiceEvent e) {/* Is a service going away? */if (UNREGISTERING == e.getType()) {/* Is this the service we hold? */if (ref == e.getServiceReference()) {/* Release the service */service = null;ref = null;}}}}

那里! 现在,这看起来像是一篇真正的程序员文章。 假设我们要导入以下关键任务服务。

interface Hello {void greet(String who);}In BundleActivator.start() we must setup a ServiceHolder.public Activator implements BundleActivator {private ServiceHolder<Hello> helloHolder;...public void start(BundleContext bc) {helloHolder = new ServiceHolder<Hello>(Hello.class, bc);...}...

服务消耗代码如下所示:

...private final ServiceHolder<Hello> holder;...void salutations() {/* Pop the service into a self-cleaning reference */Hello hello = holder.get();hello.greet("InfoQ");hello.greet("OSGi");}...

或者,如果我们缩短一点:

...void salutations() {holder.get().greet("InfoQ");holder.get().greet("OSGi");}...

这种额外的间接级别仍然远非最佳。 如果我们决定将本地类迁移到另一个捆绑并将其作为服务使用,则将代码与这些持有者打包在一起会导致清晰度下降,阻碍测试并成为重构的噩梦。 但这是正确的唯一方法。 还是?

如果我们斜视代码,那么显而易见,我们正在朝着经典的代理设计模式发展。 让我们通过将持有人包装在原始服务界面中来完成代理:

class HelloProxy implements Hello {private final ServiceHolder<Hello> holder;public HelloProxy(ServiceHolder<Hello> holder) {this.holder = holder;}public void greet(String who) {holder.get().hello(who);}}

现在,我们可以在激活器中创建一个单一的HelloProxy ,并通过Hello类型的引用在各处使用它,就好像它是原始服务一样。 除了现在,我们可以将“服务”存储在最终字段中,并将其传递给构造函数。 将此与其余的Lazy原则结合起来,我们就可以轻松地将动态样板,锁定在代理(激活器)和业务逻辑代码之间的动态模板分离开来。 而且,业务代码现在看起来像是常规的非动态Java程序:

...private final Hello hello;...void salutations() {hello.greet("InfoQ");hello.greet("OSGi");}...

在这一点上,我们必须做一个重要的观察, 即渴望和懒惰的模型不是相互排斥的。 。 正如上面的代码在每个惰性捆绑包的核心中所说明的那样,运行跟踪代码类似于将支持紧急捆绑包的代码。 一个懒惰的捆绑包用稳定的代理层包裹了此跟踪核心,从而保护了应用程序代码(及其控制流)免受下面发生的所有运动的影响。 仍然,如果需要,我们可以将代码插入跟踪层,并具有混合的eager(pre-proxy)/ lazy(post-proxy)捆绑包。 例如,渴望的部分可以对服务进行修饰,甚至可以完成对服务的转换,然后再将它们包装在代理中并传递给惰性部分。 事实证明(不包括服务闪烁),惰性模型实际上是渴望模型向更高抽象级别的自然演进。

服务层运行时

除了手工编码代理变得非常乏味的事实之外,其他一切都将是很棒的。 幸运的是,这种代理生成非常容易编写为库,甚至更好地编写为活动的Service Layer Runtime捆绑包。

从OSGi 4.1开始,实现一种特殊类型的捆绑包成为可能,该捆绑包可以驱动其他捆绑包的服务交互。 我们称这些框架为服务层运行时(或SLR),因为它们对应用程序开发人员隐藏了原始的OSGi服务层。 尽管SLR具有各种形状和大小,但它们不可避免地包含依赖项注入组件。 这是因为DI是服务的自然匹配,服务通常从单个点(如激活器)进入捆绑包,并且需要在整个捆绑包内部传播。 相反,使用大量的单身人士会产生问题,并且组织吸气剂的“消防队”非常乏味。 将这项任务委托给DI可以极大地缓解。

以下是对当今最流行的单反相机的非常简短的评论。 由于全面的并排比较将需要至少两篇文章,因此省略了功能丢失。

豌豆

Peaberry是对日益流行的Guice框架的纯Java扩展。 发现使用Peaberry的有效方法是本文的主要灵感。 该框架在很大程度上就像使用纯Guice。 获取服务代理所需要做的就是将服务接口绑定到特殊的Peaberry提供程序:

bind(Hello.class).toProvider(Peaberry.service(Hello.class).single());

然后使用ASM即时生成代理。 正常的Guice从那里接管并像注入其他物体一样将它们注入。 用这种方式编写的代码看起来很像普通的Java SE,动态代理与本地对象几乎没有区别。 Peaberry通过允许我们将代码挂接到跟踪层并在需要时对服务对象进行修饰(在将它们包装到代理之前)来支持渴望/懒惰的混合。 Peaberry的独特之处在于它能够无缝地混合来自不同来源的服务-例如,Eclipse注册表中的对象可以与OSGi服务透明地混合。

阿拉斯Peaberry尚不完美。 动态配置是它落后于其他SLR的一个区域。 用户必须导入ConfigurationAdmin服务以更改配置,或者导出ManagedService来接收动态配置。 Peaberry的另一个缺点是,它仍然需要用户编写一个简单的BundleActivator ,在其中设置Guice Injector 。 令人欣慰的是,Peaberry目前正在积极开发中,并且这些差距一定会很快消除。

Spring动态模块

通过透明生成的服务代理来支持动态。 Spring DM依赖于Spring组件模型来进行依赖注入。 组件具有与Eager模型保持一致的动态(停用)生命周期。 虽然Peaberry倾向于使编码器默认情况下使用Lazy并根据需要使用Eager,但Spring DM似乎默认为50/50 Eager / Lazy样式。 尽管它似乎提供了比Peaberry更多的功能,但Spring DM的使用却要重得多。

Spring DM即将作为OSGi标准(称为Blueprint Service)继续存在 。 Blueprint Service不仅是简单的名称更改,还是从Spring DM精心开发而来的,以更好地与OSGi运行时集成。

声明式服务

这是OSGi唯一标准化的SLR,因此值得更多关注。 OSGi DS尝试使用传统的Java方法解决动力学问题。 它的高级之处在于它具有带有setter依赖项注入的组件模型。 它是低级的,因为它使组件暴露于更多的服务动态中(没有代理)。 依赖关系可以定义为“动态”或“静态”。

对于动态依赖关系,组件必须提供一对bind()/unbind()回调。 OSGi DS将进行跟踪并调用相应的回调。 然后,组件将自己接管执行所有交换和同步操作。 在这种情况下,OSGi DS仅保存开发人员的跟踪代码。

默认情况下,依赖性为“静态”,并且组件仅需要提供bind()/set()方法。 现在,该组件不必担心服务对象的同步或释放。 相反,只要依赖项发生更改,OSGi DS就会重新创建整个组件。 las,这是确保释放旧服务对象的唯一方法。 由于它是如此昂贵,因此不建议使用静态依赖项。

OSGi DS还遵循Eager服务导出策略:如果某个组件作为服务公开,并且其中某些必需的依赖项消失,则该组件未注册。 结果还导致相关组件的级联停用。 正如我们所看到的,这种生命周期方案无法防止异常导致传递依赖项失败。 用户代码仍必须适当地进行错误处理。

OSGi DS还支持非依赖关系注入“查找”样式,在这种样式中,您的组件将接收ComponentContext ,以从中提取服务。

在即将发布的OSGi 4.2规范中,OSGi DS将会得到很好的改进。 这些改进集中在允许OSGi DS代码成为纯POJO。 除非我们愿意,否则将有可能真正地永远不会看到OSGi服务API的任何元素。

iPojo

从架构上讲,iPojo似乎是“ OSGi DS,但操作正确”。 在这里,我们还将组件部署为捆绑包,每个组件的依赖关系都由中央SLR捆绑包管理。 与OSGi DS一样,可以将组件作为服务公开给OSGi注册表。 但是从这里开始,iPojo开始与OSGi DS背道而驰。 最重要的是,它是高度模块化的,允许组件为每种依赖关系类型指定不同的可插拔处理程序 。 与OSGi DS不同,iPojo做了彻底的工作来保护用户代码不受服务动态影响。 服务不是存储在代理中,而是存储在公共的ThreadLocal缓存中。 当线程进入组件时,将填充高速缓存,当线程离开时,将刷新高速缓存。 所有这些都是通过字节码编织魔术实现的。 这使iPojo可以在资源受限的设备上使用。

致谢

作者要感谢Roman Roelofsen在本文的长期发展过程中提供了意见和批评。

翻译自: /?topicPageSponsorship=c1246725-b0a7-43a6-9ef9-68102c8d48e1

懒人遥控服务端

更多推荐

懒人遥控服务端

本文发布于:2024-02-11 20:43:33,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1683367.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:懒人   服务端

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!