SpringSource 应用平台清单文件头

工程 | Glyn Normington | 2008年5月8日 | ...

SpringSource 应用平台由 OSGi 捆绑包构建而成,并支持同样由 OSGi 捆绑包构建的应用程序。该平台支持 OSGi 的标准功能,但也支持一些额外的清单文件头。许多人曾提出疑问:“为什么 SpringSource 添加了专有头文件?”以及“新头文件的语义是什么?”,因此本文解释了背后的动机以及Import-LibraryImport-Bundle的语义。

标准 OSGi 捆绑包支持

该平台基于OSGi R4.1标准,或者如果您更喜欢的话,是JSR 291,并使用Equinox作为其 OSGi 实现。这意味着您可以使用平台的工具开发标准 OSGi 捆绑包,并将这些捆绑包部署到平台上,自平台推出以来,许多用户一直在这样做。

因此,精通 OSGi 的开发人员可以使用该平台作为标准 OSGi 容器,并受益于平台的功能,例如:

  • 能够使用管理控制台或通过将捆绑包放入平台的pickup目录来部署捆绑包;
  • 诊断功能,例如解析失败诊断、特定于应用程序的跟踪和自动死锁检测;
  • 与 Spring 和 Spring 动态模块的强大集成,适用于想要使用这些框架的开发人员;以及
  • 从存储库自动配置依赖项。
但是,该平台还旨在使几乎没有或根本没有 OSGi 使用经验的企业应用程序开发人员也能轻松受益于 OSGi,这给平台带来了一些额外的要求。

企业应用程序的额外要求

正如 Sam 最近关于平台部署选项的博客文章所解释的那样,您可以将现有的单片 WAR 文件部署到平台上,而无需了解 OSGi——平台会为您处理所有事情。但是,为了受益于共享库、共享服务以及最终的 PAR 文件范围,必须将单片 WAR 文件分解成 OSGi 捆绑包。这有多难呢?

嗯,此过程中的某些步骤相对容易,尤其是在遵循良好的软件工程实践并将代码组织成服务、域和基础设施组件的情况下。这些组件可以转换为捆绑包,并且它们之间的依赖关系可以使用标准 OSGi Import-Package 和 Export-Package 头文件在META-INF/MANIFEST.MF中表达。

一个更困难的步骤是表达对企业框架(如 Spring 和 Hibernate)的依赖。完全可以使用标准 OSGi Import-Package 和 Require-Bundle 头文件来表达这些依赖关系,如果您目标是创建将在其他 OSGi 容器中运行的 OSGi 捆绑包,那么这正是您应该做的,但这方法有一些隐含的成本。

首先,开发人员必须准确确定构成给定框架的包。仅仅导入应用程序代码使用的包是不够的,因为当应用程序加载时,一些企业框架会将更多依赖项编织到应用程序的字节码中。开发人员必须通过反复试验来发现还需要导入哪些额外的实现包,以确保编织后的应用程序行为正确。

然后是将框架从一个版本迁移到下一个版本的工作,其中构成框架的包的精确集合已经发生了变化。编织所需的额外包通常不由公共契约定义,因此可能会发生变化。

此外,生成的包导入没有正确捕捉设计意图,这使得将来维护或扩展应用程序更加困难。

我们真的不想给我们的用户带来这些负担,因此我们创建了一些额外的 SpringSource 应用平台特定的清单文件头,Import-LibraryImport-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>(当然也包括分号分隔符),则默认范围包括所有版本。

对于每个库导入,平台都会选择具有给定符号名称和给定版本范围内平台存储库中最高版本的库。然后,平台会将库导入替换为一组包导入,这些包导入与库的捆绑包导出的所有包匹配。平台会检测捆绑包导入两个或多个导出公共包的库的情况,发出相应的日志消息,并失败安装导入的捆绑包。

例如,以下头文件导入某个版本的Spring Framework 库,版本范围为 2.5.4(包含)到 2.5.5(不包含):

    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-LibraryImport-Bundle具有与Import-Package相同的底层语义,从而避免拆分包的复杂性。

我们还预计,随着平台的不断发展,我们将需要向Import-LibraryImport-Bundle添加更多指令,而这些指令不适合添加到Require-Bundle中。

下一步是什么?

平台测试版计划正在进行中,我们将倾听所有关于平台功能的反馈,包括新的清单文件头。

对于希望利用平台头文件,但需要生成可在其他 OSGi 容器上运行的捆绑包的用户,我们计划开发一个工具,用等效的标准包导入来替换Import-LibraryImport-Bundle语法糖。

我们还将与 OSGi 联盟的同事讨论新的清单头文件,以确定是否存在 OSGi 未来应解决的普遍需求。

获取 Spring 新闻通讯

关注 Spring 新闻通讯

订阅

抢先一步

VMware 提供培训和认证,助您快速提升。

了解更多

获取支持

Tanzu Spring在一个简单的订阅中提供 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件。

了解更多

即将举行的活动

查看 Spring 社区所有即将举行的活动。

查看全部