SpringSource Application Platform 由 OSGi 捆绑包构建而成,并支持同样由 OSGi 捆绑包构建的应用程序。平台支持 OSGi 的标准功能,但也支持一些额外的清单头部。许多人曾问:“为什么 SpringSource 要添加专有头部?”以及“新头部有什么含义?”,因此本文解释了背景动机以及 Import-Library 和 Import-Bundle 的含义。
标准 OSGi 捆绑包支持
平台基于
OSGi R4.1 标准,或者如果您更喜欢,可以认为是
JSR 291,并使用
Equinox 作为其 OSGi 实现。结果是,您可以使用平台的工具开发标准的 OSGi 捆绑包,并将这些捆绑包部署到平台,正如许多用户自平台发布以来一直在做的那样。
因此,熟悉 OSGi 的开发者可以将平台用作标准的 OSGi 容器,并受益于平台的功能,例如:
- 通过管理控制台或将捆绑包放入平台的 pickup 目录来部署捆绑包的能力;
- 诊断功能,如解析失败诊断、应用程序特定跟踪和自动死锁检测;
- 与 Spring 和 Spring Dynamic Modules 的强大集成,适用于希望使用这些框架的开发人员;以及
- 从存储库自动供应依赖项。
然而,平台还旨在让接触 OSGi 很少或没有经验的企业应用程序开发人员能够轻松受益于 OSGi,这给平台带来了一些额外的要求。
企业应用程序的其他要求
正如 Sam 最近关于平台部署选项的
博客 所解释的,您可以将现有的单体 WAR 文件部署到平台上,而无需了解 OSGi - 平台会为您处理一切。但是,要从共享库、共享服务以及最终的 PAR 文件范围中受益,就需要将单体 WAR 文件分解为 OSGi 捆绑包。这有多难?
好吧,过程中的一些步骤相对容易,特别是如果遵循了良好的软件工程实践并且代码已被组织成服务、域和基础设施组件。这些组件可以转换为捆绑包,它们之间的依赖关系可以使用 META-INF/MANIFEST.MF 中的标准 OSGi Import-Package 和 Export-Package 头部来表达。
一个更困难的步骤是表达对 Spring 和 Hibernate 等企业框架的依赖。使用标准的 OSGi Import-Package 和 Require-Bundle 头部来表达这些依赖关系是完全可能的,如果您想创建将在其他 OSGi 容器中运行的 OSGi 捆绑包,那么您应该这样做,但这种方法有一些隐藏的成本。
首先,开发人员必须精确地确定构成给定框架的包。仅仅导入应用程序代码使用的包是不够的,因为许多企业框架在应用程序加载时会在应用程序的字节码中注入进一步的依赖关系。开发人员必须通过试错来发现需要导入哪些额外的实现包,以确保编织后的应用程序能够正确运行。
然后是迁移到框架的新版本的工作,其中构成框架的包的精确集已发生更改。编织所需的附加包通常不是由公共契约定义的,因此可能会发生更改。
此外,生成的包导入未能正确捕获设计意图,这使得将来维护或扩展应用程序更加困难。
我们真的不想给用户带来这些负担,因此我们创建了一些额外的 SpringSource Application Platform 特定清单头部,Import-Library 和 Import-Bundle,作为表达对企业框架依赖关系的便捷方式。正如您将在下面看到的,这些头部只是“语法糖”,它们本身就是标准的 OSGi 包导入。
Import-Library
基本语法与其他清单头部类似:
Import-Library: <librarySymbolicName>;version=<versionRange>
其中
<librarySymbolicName> 是库的“符号名称”,
<versionRange> 是使用 OSGi 版本范围表示法可接受的库版本范围。库定义指定了库的符号名称和版本,这两个共同唯一地标识了平台上的库。
如果您不熟悉 OSGi 版本范围表示法,最常用的形式是最小版本范围,例如 2
,表示“版本 2 或更高版本”,以及“半开”范围,例如 [2.2.1,2.2.2)
,表示 2.2.1(含)和 2.2.2(不含)之间的任何版本。如果省略 version=<versionRange>,并省略分号分隔符,则默认范围包含所有版本。
对于每个库导入,平台会选择具有给定符号名称和给定版本范围内的最高版本库。然后,平台会将库导入替换为一组包导入,这些包导入匹配该库捆绑包导出的所有包。平台会检测到捆绑包导入两个或多个导出公共包的库的情况,会发出适当的日志消息,并阻止导入捆绑包的安装。
因此,例如,以下头部导入了介于 2.5.4(含)和 2.5.5(不含)之间的某个版本的 Spring Framework 库。
Import-Library: org.springframework.spring;version="[2.5.4,2.5.5)"
可选库导入
您可以使用以下语法指示库导入是可选的。请注意,特殊分隔符
:=
表示一个“指令”,它会修改清单头部的语义,而分隔符
=
表示一个“匹配属性”,如
version。
Import-Library: <librarySymbolicName>;version=<versionRange>;resolution:=optional
如果未指定 resolution,或者指定为 mandatory,则包含导入库头部的捆绑包将无法安装,如果不存在具有给定符号名称且版本在给定范围内的库。但是,如果指定了 resolution:=optional,则在没有合适的库可用时,将忽略该库导入。
因此,例如,以下头部从 2.5 版本开始导入某个版本的 Spring Framework 库,但如果不存在合适的库,则会被忽略。
Import-Library: org.springframework.spring;version="2.5";resolution:=optional
导入多个库
如果您需要导入多个库,请在单个
Import-Library 清单头部中指定一个逗号分隔的库导入列表,如下例所示:
Import-Library: org.foo.p;version="[1,2)",org.bar.q;version="[2,3)"
Import-Bundle
Import-Bundle 是一个额外的便利功能,适用于库仅由一个捆绑包组成且创建库定义不便的情况。语法非常类似于
Import-Library,只是它引用的是捆绑包的符号名称和版本,而不是库的。
正如您所期望的,对于每个导入的捆绑包,平台会选择具有给定符号名称和给定版本范围内的最高版本捆绑包。然后,平台会将捆绑包导入替换为一组包导入,这些包导入匹配该捆绑包导出的包。
因此,例如,以下头部导入了 Hibernate 对象关系映射 捆绑包。
Import-Bundle: com.springsource.org.hibernate;version="[3.2.6,3.2.7)"
为什么不重载 Require-Bundle?
如果您熟悉 OSGi,您可能会问自己,为什么我们不重载
Require-Bundle 而引入
Import-Bundle。
嗯,我们希望 Require-Bundle 保留其标准语义,包括将拆分的包组合在一起的能力。但我们希望 Import-Library 和 Import-Bundle 具有与 Import-Package 相同的底层语义,从而避免拆分包的复杂性。
我们还预计,随着平台随时间的推移而发展,我们将需要向 Import-Library 和 Import-Bundle 添加进一步的指令,这些指令不适合添加到 Require-Bundle。
下一步是什么?
平台
beta 计划 正在进行中,我们将听取所有关于平台功能(包括新的清单头部)的反馈。
对于希望利用 Platform 的头信息,但又需要生成可在其他 OSGi 容器上运行的 bundle 的用户,我们计划推出一个工具,该工具将替换 `Import-Library` 和 `Import-Bundle`……