java 模块化osgi_Java 9,OSGi和模块化的未来(第2部分)

java 模块化osgi

重要要点

  • Java 9将于2017年发布,其旗舰功能将是名为Java Platform Module System(JPMS)的新模块系统。 本文探讨了这与现有Java模块标准OSGi的关系以及对它的影响。
  • 自1.0版以来,Java已经增长了20倍,并且需要对平台进行模块化。 解决该问题的尝试很多失败。 同时,OSGi提供应用程序模块化已有16年了。
  • OSGi和JPMS在实现细节上有很大不同。 如果将JPMS视为模块化的通用解决方案,则似乎存在重大缺陷和功能缺失。
  • JPMS的目标是比OSGi更简单易用。 但是,将现有的非模块化产品模块化是复杂性的主要来源,而JPMS似乎并未成功实现这一目标。
  • JPMS在模块化Java平台本身方面做得非常出色,这意味着我们可以针对特定工作负载构建仅包含Java平台相关部分的小型运行时。 对于应用程序模块化,OSGi具有许多优势。 我们已经展示了两者可以结合使用,并且这样做看起来将是一个成功的公式。

这是“ Java 9,OSGi和模块化的未来”系列的第二篇文章,另请参阅第1部分:Java 9,OSGi和模块化的未来

这将继续我们对OSGI和Java平台模块系统(JPMS)的深入研究,该平台计划作为Java 9的一部分发布。在第一部分中,我们从较高的层次比较了两个模块系统,描述了每个系统如何解决模块之间的隔离。 我们深入研究了依赖项是如何工作的,并研究了与反射有关的一些问题。 在第二部分中,我们讨论版本控制,动态模块加载以及OSGi和JPMS未来互操作性的潜力。

版本控制

版本控制是所有软件交付的关键方面。 API和实现都会发生变化,因此,每当我们依赖它们时,我们就隐式地依赖它们,因为它们存在于某个特定的时间点。 任何模块系统都必须能够应对这种现实……通常是在工件和引用它们的依赖项上都支持显式版本。

但是,并非所有更改都具有同等破坏力。 如果我们使用某个模块的1.0.0版构建和测试我们的软件,则当我们部署1.0.1或1.0.5版时,我们的软件似乎可以继续工作。与2.0.0版本或5.2.10版本的依赖项一起部署。 这表明模块系统需要了解并支持兼容性范围。

OSGi始终支持这两个概念。 捆绑软件以及导出的软件包都进行了版本控制。 包的导入是指范围,通常用一个包含性下限和一个排除性上限来表示,例如:“ [[1.0.0,2.0.0)”,这表示从1.0.0到2.0(但不包括2.0)的每个版本.0。 OSGi使用语义版本控制,并且与流行的语义版本控制规范完全一致(尽管OSGi早于该文档)。 松散地,第一个部分是主要版本,指示功能和API的重大更改,第二个部分是次要版本,指示不中断的功能增强。 第三部分表示现有功能的补丁。

OSGi开发人员无需推理或明确声明这些版本范围。 就像导入本身一样,版本范围是在构建时通过分析依赖项的性质自动生成的。 例如,如果我们仅使用API​​包作为使用者,那么我们会得到范围很广的“ [1.0.0,2.0.0)”,其中包括所有次要版本和微型版本。 但是,如果我们将服务接口实现为提供程序,则必须导入范围很窄的包,例如“ [1.0.0,1.1.0)”,这意味着从1.0.0到不包括1.1.0的每个版本。 此处的区别在于,支持1.0.0版功能的提供程序将不支持1.1.0版,因为次要段的增加意味着该提供程序无法自动提供的新功能。 另一方面,消费者可以轻松使用1.1.0或1.2.0等,因为它只是忽略了新功能。

除了生成的导入范围外,OSGi构建工具( bnd )有助于正确确定导出软件包的版本。 版本是包的属性,可以使用package-info.java.上的@Version批注将其直接写入包中package-info.java. 每当包的内容发生更改时,都必须更新此版本:例如,如果我们向服务接口添加方法,则需要将版本从1.0.0递增到1.1.0。 生成工具检查版本是否正确反映了所做更改的性质。 因此,例如,如果我们添加了该方法却根本忘记更改版本,或者如果所做的更改太小(例如仅增加到1.0.1),它将破坏构建。

最后,OSGi具有灵活性,允许在单个应用程序中同时部署模块的多个版本。 如果我们的某些依赖项具有对slf4j或Guava之类的公共库的不同版本的传递依赖项,则可能会出现这种情况。 有一些限制-我们不能在一个模块中直接导入一个软件包的多个版本-但是在确实需要的时候具备这些功能非常宝贵。

所有这些都意味着OSGi具有全面的方法,使我们能够在独立的团队和组织中构建模块,然后将模块组装到应用程序中。 这些工具使我们对所选模块组实际上可以一起工作充满信心。

相反,JPMS几乎不支持任何形式的版本控制。

在module-info.java中无法指示模块的版本,(已编译的module-info.class文件中存在Version属性,但是它不是来自Java来源,目前尚不清楚如何依赖项也没有版本化:JPMS模块只能按名称要求其他模块,而不能按版本要求,当然也不需要按版本范围。 这些功能将必须由外部工具添加,但是由于module-info.java源文件不可扩展并且无法在其中使用Java注释,因此工作受到了阻碍。

JPMS要求指出,要在运行时使用的兼容版本的选择超出范围。 这意味着其他工具将必须完成这项工作,但是如果没有合适的元数据,它们将无法完成工作。 将版本元数据存储在与基本模块元数据相同的描述符中是很自然的,但这是不可能的。

同样,正如我们已经提到的,在JPMS下,禁止同时存在同一模块的多个版本。 另外,禁止多个模块导出同一软件包,甚至禁止重叠的私有软件包。 因此,无论使用什么工具来构造有效的模块集,都必须找到解决冲突传递依存关系的方法。 在许多情况下,“解决方案”将仅仅是某些模块无法与其他模块结合使用。

动力学

作为OSGi基于类加载器的隔离实现的一个副作用,可以在运行时支持模块的动态加载,更新和卸载。 在企业环境中,这似乎并不重要,实际上大多数OSGi的企业部署都不使用动态更新。 OSGi中没有任何内容表明您必须执行动态更新!

但是动态更新在物联网等其他环境中非常宝贵。 在缓慢或间歇性的网络上更新部署在成千上万个设备上的软件非常令人头疼。 OSGi是在任何平台上使用绝对最少数据量直接支持即时更新的极少数技术之一:我们只需要发送实际已更改的模块即可。

最初,在2000年,电信运营商在家庭网关和路由器上使用OSGi来构建智能家庭解决方案的主要原因之一是能够在不进行固件更新的情况下管理软件。 固件更新没有吸引力的原因有很多:下载-固件更新通常需要将兆字节的软件下载到潜在的数百万个设备中; 固件是特定于设备的,因此您最终可能会进行许多不同的更新来创建和管理固件的部署。 测试-固件更新需要大量,耗时且昂贵的压力测试,并且每次都要对每个设备执行一次。 OSGi大大简化了此过程。 可以将更新应用到模块中,并“即时”安装到正在运行的网关和路由器,而无需重新启动; 相同的模块可以在所有设备上使用(通常是从底层设备硬件中抽象出来的),并且重要的是,单元测试可以在一套较小的软件上执行一次,从而节省了大量的时间,精力和金钱。 一个具体的例子是由德国电信(Deutsche Telekom)建立的行业联盟Qivicon。 Qivicon提供的家庭网关包括基于OSGi的软件堆栈,后端基础结构,面向应用程序开发人员的工具以及维护和支持。 使用OSGi作为生态系统的基础,使Qivicon合作伙伴可以更快地将其自己的智能家居产品推向市场。

Qivicon合作伙伴不断集成新设备并开发新的创新增值服务。 这需要复杂的设备管理和软件配置功能,以确保对特定设备平台的软件组件进行依赖性和兼容性管理。 这些功能已在OSGi中使用现有行业标准(例如TR-069OMA-DM)进行了标准化。

此外,动态行为与软件更新无关。

OSGi服务注册表本质上是动态的。 服务可以来去去,并实时通知绑定到服务的组件。 服务可以表示和报告不断变化的现实世界状态。 即使在相对安静的企业应用程序世界中,这也很重要。 例如,OSGi服务可以表示外部数据源的可用性,负载均衡的REST服务的IP地址,甚至可以表示金融市场的开放时间。 使用服务的每个组件都可以决定在该服务不可用时如何做出React:它可以继续运行,也可以注销自己提供的服务。 因此,低级别状态的更改可以可靠地传播到任何有影响的位置。

互操作性与未来

JPMS将于明年随着Java 9的发布在主流Java中发布。存在大量用OSGi编写的应用程序,并且一直在编写更多的应用程序。 它们安全吗,还是必须为新的JPMS模块系统重写它们?

首先要说的是,只要OSGi应用程序不使用不受支持的内部Java API,它们就可以在Java 9上正常运行 。 对于所有Java代码,这都是相同的一般建议。 OSGi仅使用受支持的Java API,并且Oracle坚决承诺Java 9不会破坏此类应用程序。 您对Java 9的任何问题都可能来自使用内部JDK类型的库,这些库在Java 9中不再可用,除非通过特殊的配置标志。 OSGi用户将为此更改做好更好的准备,因为他们的模块已明确列出了导入。 与仅在类路径上堆积JAR的普通Java应用程序相比,基于OSGi的应用程序对其平台依赖性的范围要清楚得多。

在这种最基本的兼容性模式下,OSGi框架和捆绑软件将完全存在于JPMS的“未命名”模块中。 OSGi将继续提供其所有现有的隔离功能以及强大的服务注册表和动态加载。 您在OSGi上的投资是安全的,并且OSGi仍然是新项目的理想选择。

但是希望我们可以做得更好。 当OSGi在模块化Java 9平台上运行时,我们应该能够利用平台中的模块。 例如,OSGi捆绑软件应该可以声明其依赖的平台模块集–也就是说,我们应该能够直接从OSGi捆绑软件依赖于JPMS模块。 OSGi框架应在运行时尊重这些依赖关系,并且这些工具应能够基于这些依赖关系准备运行时。

这里的情况看起来已经很好。 在2015年11月的博客文章中,我描述了一个概念证明,该概念证明是为了演示在JPMS上运行OSGi而构建的。 我详细介绍了OSGi捆绑软件如何在基本平台中声明对特定JPMS模块的依赖关系。 我展示了如果OSGi依赖于不在平台中的JPMS模块,它将如何拒绝捆绑软件。 我没有为组装运行时创建该工具的原型,但是已经存在所有用于开始创建这种工具的工具。

图3显示了将来的互操作性。 我们可以看到捆绑软件A导入了包javax.activation ,该包在JPMS下由java.activation模块导出。 互操作层将知道平台包含该模块,从而允许OSGi对其进行解析。 迁移到Java 9时,捆绑软件A根本不需要更改。捆绑软件B使用来自java.httpclient JPMS模块的java.net.http package ,但是不能表示为OSGi Import-Package,因为它始于“ java 。” (请注意,所有包和模块都隐式依赖于java.base )。

因此,我们提出了一个新的OSGi标头,称为“ Require-PlatformModule”,它表示对JPMS模块的要求。 如果平台不包含java.httpclient模块,这将使OSGi框架在Bundle B上“快速失败”。 它还将使工具能够使用最少的JPMS模块和OSGi捆绑包为应用程序构建完整的运行时。

同样,必须注意的是,这项工作是非官方的概念验证,并且OSGi与JPMS互操作性的实际形状将由规范过程确定。

图3:OSGi – JPMS互操作性概念验证

结论

JPMS通过Jigsaw原型项目,在模块化Java平台方面做得非常好。 这项工作的结果是,有可能构建非常小的运行时,仅包含特定工作负载所需的Java平台部分。

但是,作为应用程序模块化的规范,JPMS存在一些严重的缺点。 缺少对版本控制的任何支持都是一个令人震惊的遗漏,很难在没有外部工具提供的并行元数据系统的情况下构建应用程序。 整个模块的依赖项声明将引入比他们需要的更多的传递性依赖项,这将削弱拥有较小平台所获得的收益。 由于无法反映未导出的程序包,因此不必要地难以使用Java生态系统中的现有框架。

这些设计决策可能是JDK本身的正确决策:它们提高了平台的健壮性和安全性,而又不破坏对所有现有Java应用程序的向后兼容性。 但是,总的来说,它们之间的权衡取舍是应用程序模块的较差选择。

因此,OSGi的未来似乎是光明的:通过将OSGi与精简的模块化Java平台相结合,我们可以兼得两者。 拥有16年的经验,OSGi遇到并解决了JPMS尚未考虑的问题。 OSGi工具和运行时的生态系统广泛而深入。 长期以来,久经考验的独立标准机构提供的支持可确保它面向未来。 你在等什么?

翻译自: https://www.infoq.com/articles/java9-osgi-future-modularity-part-2/?topicPageSponsorship=c1246725-b0a7-43a6-9ef9-68102c8d48e1

java 模块化osgi

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值