完善全貌:Spring、OSGi 和 SpringSource 应用平台

工程 | Adrian Colyer | 2008 年 5 月 1 日 | ...

** 5 月 2 日更新了案例研究:- 详情请参见本文底部 ** 我相信大多数阅读此博客的人都已看到昨天 SpringSource 应用平台的发布公告。如果还没有,请务必查看 Rob 的博客文章,其中描述了一些动机、编程模型和路线图。

有两个常见问题我希望在此篇文章中立即解答。之后,我将描述另外两个令人兴奋的公告,它们与 SpringSource 应用平台本身相辅相成,但昨天并未占据头条:SpringSource 企业 Bundle 仓库和用于 Eclipse 的应用平台工具。它们共同完善了围绕基于 OSGi 的企业应用开发与 Spring 的整个故事。

过去 24 小时内我多次听到的问题是:OSGi 有什么问题 - 为什么我们不能直接使用纯净的 OSGi 服务平台(例如 Equinox、Felix 或 Knopflerfish),而非 SpringSource 应用平台?

OSGi 绝对没有任何问题。

OSGi 是一个出色的基础和服务平台 - 这也是我们和许多其他方选择在其基础上构建的原因。它已在广泛的行业和应用中得到验证,并且支撑着像 Eclipse 和 IBM 的 WebSphere 等应用,以及其他几家供应商的中间件栈。

直接使用 OSGi 规范 API 进行编程缺乏一些我们对企业应用所期望的特质 - 例如使用依赖注入的能力以及在容器外部轻松进行单元和集成测试的应用创建能力。直接使用 OSGi 规范 API 进行编程还迫使您在相对较低的层级处理 OSGi 平台的动态性 - 当您依赖的模块和服务在运行时停止、启动、安装和更新时,您会怎么做?但这里没有根本性的障碍,我们无法通过 Spring Dynamic Modules 项目克服这些障碍。使用 Spring Dynamic Modules 构建的应用可以在任何标准的 OSGi 服务平台上运行,并且我们针对 Equinox、Felix 和 Knopflerfish 测试了所有构建。我们致力于确保 Spring Dynamic Modules 和基于 Spring 的编程模型保持运行时中立。这个立场不会因为 SpringSource 应用平台的引入而改变。

现有的企业库也绝对没有任何问题。

嗯,好吧。确实有一些情况不尽如人意,但总体而言,我们知道如何让它们满足企业应用开发的需求。

那么问题出在哪里呢?

如果 OSGi 工作得很好,并且现有的企业库也满足我们的需求,那么问题出在哪里?困难在于尝试将 OSGi 服务平台与一组未考虑 OSGi 而编写的现有企业库结合时。这不是 OSGi 的错,它拥有一个出色的模型,提供了卓越的模块化、版本控制和操作控制。也不是企业库的错 - 它们并不是为在 OSGi 下运行而编写的。但正是那些让 OSGi 如此吸引人的特性打破了那些企业库开发者所做的假设。例如,OSGi 的模块化模型阻止你查看其他 Bundle 的私有部分。这正是你想要的,直到你意识到你的企业库无法再看到你的应用类型。许多东西都可能崩溃:从 commons logging 到 jsps,从标签库到数据源,从加载时织入到组件扫描,从资源加载到 orm 映射。不胜枚举……(是的,当你将应用代码和它所需的所有库打包到一个 Bundle 中时,你可以让这些东西中的许多工作,但这很大程度上偏离了重点!)。

这就是为什么你会看到许多人在 OSGi 之上构建,但很少将 OSGi 的好处传递到应用编程模型中(Eclipse RCP 是一个罕见的例外)。当你在 OSGi 之上构建,但不一定将该模型暴露给终端用户应用开发时,你可以按照 OSGi 模型构建并使其工作。当你需要提供一个平台,在该平台上可以使用大量的现有企业库时,情况就不同了。如果我们能抛弃所有这些,从头开始使用专门为 OSGi 编写的库,我们会没问题。例如,我们确保了 Spring Framework 完全能够在 OSGi 服务平台内部运行。但这并非现实可行的提案。或者,我们可以等待现有库的开发者将它们全部转换以在 OSGi 下开箱即用(就像我们对 Spring 所做的那样)。但除非其他人都这么做,否则他们这样做的动机是什么?所以我们似乎陷入了先有鸡还是先有蛋的困境。这是 OSGi 企业专家组在过去一年中花费大量时间讨论的问题。SpringSource 应用平台解决的正是这个问题:- 通过使具有标准 OSGi 语义的标准 OSGi Bundle 与现有企业应用库协同工作,从而将企业应用开发引导到 OSGi 的世界中。

我还要再次强调,该平台不仅仅是关于 OSGi:OSGi 支持是我们最兴奋的特性之一,但 SpringSource 应用平台也是一个出色的服务器平台,用于部署标准的 war 文件。我们将在后续文章中描述该平台在这种场景下提供的优势。

希望这篇文章有助于澄清围绕 SpringSource 应用平台与 OSGi 之间关系的困惑。如果您一直关注到这里,可能已经注意到了另一个潜在的问题:让现有企业库在 OSGi 下工作固然不错,但您是否需要将它们所有的 jar 文件都转换为 OSGi Bundle 才能部署它们呢?是的,您需要。事实证明,如果您想正确地对所有导入和导出进行版本控制并确保具有正确的符号名称等,这将是一项大量的工作。好消息是,对于数百个常用的企业应用库,我们已经为您完成了艰苦的工作,并在 SpringSource 企业 Bundle 仓库中提供了 OSGi 就绪的版本...

SpringSource 企业 Bundle 仓库

SpringSource 企业 Bundle 仓库既是一个可供 Ivy 和 Maven 使用的仓库,也是一个在线可搜索的企业库数据库。您可以在 www.springsource.com/repository 找到它。您可以按名称浏览 Bundle,或者只需输入搜索词即可查找名称、导出的包、类或资源匹配的 Bundle。您还可以查看任何 Bundle 的最小(仅满足必需依赖项)和最大(满足尽可能多的可选依赖项)传递依赖项。

摘自常见问题解答

"SpringSource 企业 Bundle 仓库是使用 Spring Framework 开发企业 Java 应用常用的开源库集合。仓库包含 jar 文件 (Bundle) 和库定义 (".libd") 文件。库定义了一组 Bundle,这些 Bundle 通常会为了某个目的(例如 "Spring Framework" 库)一起使用。仓库中包含数百个 Bundle。" 该仓库满足以下条件:

  • 仓库中的每个 jar 文件都是一个有效的 OSGi Bundle。您从仓库下载的任何 jar 都可以原样部署到 OSGi 服务平台和 SpringSource 应用平台中。它也可以在 OSGi 外部作为常规 jar 文件使用。
  • 每个 Bundle 和库都关联着完整的版本信息。Bundle 的包导出信息包含版本信息,Bundle 的包导入信息包含完整的版本范围兼容性信息。
  • 仓库是传递完整的。任何 Bundle 的强制依赖项保证也在仓库中。仓库中任何 Bundle 的大多数可选依赖项也都会存在。任何库定义中列出的 Bundle 保证在仓库中。
  • 仓库是自洽的。在任何构件上传到仓库之前,我们都会验证它是否可以与仓库中所有其他 Bundle 一起安装、解析并在 OSGi 服务平台中启动(使用与 SpringSource 应用平台相同的配置文件)。
  • 仓库可用于基于 Ivy 和 Maven 的构建。
为了维护这些保证,我们围绕构件发布到仓库制定了治理模型。有一个 JIRA 实例,您可以针对该实例提出包含其他库的请求,并报告现有已发布构件的任何问题(与 OSGi manifest 等相关)。

应用开发工具

到目前为止,我们已经讨论了用于将应用开发为 OSGi Bundle 的基于 Spring 的编程模型,用于部署到 OSGi 服务平台的企业库的可用性,以及一个能够让这些传统库在 OSGi 运行时中工作的运行时(SpringSource 应用平台)。拼图缺失的部分是使基于 OSGi 的应用创建变得容易的开发者工具。

Eclipse 已经内置了 OSGi 开发工具。由于每个 Eclipse 插件也是一个 OSGi Bundle,因此 Eclipse PDE 工具(插件开发环境工具)可以用于 OSGi 应用开发。然而,这些工具主要设计用于开发 Eclipse 插件这一事实显而易见,在使用它们进行 OSGi 应用开发时存在一些常见的令人沮丧的地方。一个问题是 META-INF/MANIFEST.MF 文件只能放在项目的根目录下 - 这与 Ivy 和 Maven 等构建工具配合得不好;另一个问题是您整个工作空间只能受限于一个目标平台(用于开发的 Bundle 集合)。PDE 工具真正出色的地方,也是您真正需要的,是它们从 OSGi manifest 构建项目的编译类路径 - 这样在编译、测试和运行时之间就不会出现类路径和类可见性的差异。

与 SpringSource 应用平台一起,我们还发布了一套 Eclipse 插件(可从 SpringSource 应用平台下载页面获取),这些插件使得 OSGi 应用的开发更加容易,特别是针对 SpringSource 应用平台的应用。您的 META-INF/MANIFEST.MF 文件可以位于任何源目录中,并且这些工具从 manifest 条目构建编译类路径。然而,您可以将您的项目与定义到 Eclipse 的 SpringSource 应用平台服务器关联(使用 WTP 功能),而不是单个目标平台。然后,您项目的类路径将从您 manifest 文件中的导入语句派生,针对您工作空间中的其他 Bundle 项目以及关联服务器中安装的 Bundle 进行解析。您在编译时获得的类路径和依赖项的解释与您在运行时获得的完全相同。当然,正常的“部署到服务器”选项也有效。

这是服务器在 Eclipse 内部运行时的情况:

此截图显示了如何使用“Bundle Dependencies”类路径容器管理类路径。请注意,您未在 manifest 文件中导入的包被灰显,表示您当前无法访问它们。

更好的是我们如何能够利用 OSGi 的模块化。一组项目(每个 Bundle 一个)构成了您的应用。当您更改项目中的任何内容时,额外的增量构建器会分析资源差异,并对 SpringSource 应用平台中正在运行的 Bundle 进行实时更新 - 因此您始终运行的是最新代码:每一次,始终如此。这极大地提高了生产力,并提供了出色的开发体验。

案例研究

Matt Raible 发布了一篇题为博客文章,讲述了他尝试在使用 Freemarker 的情况下,在没有使用 SpringSource 应用平台的情况下,让 Spring Web 应用在 OSGi 下工作的冒险经历。这似乎是一个很好的挑战性应用,可以测试我上面关于让现有企业库工作所说的话。好消息是,这个应用在 SpringSource 应用平台上运行得非常顺利。以下是我让它工作的步骤(总共花费时间约为 10 分钟)
  • 从 Matt 的博客下载 zip 文件
  • 运行 'mvn'
  • 将 target/mpapp.war 复制到平台的 pickup 目录
  • 启动平台:bin/startup.sh。
我在控制台得到了以下输出:
com.springsource.platform.deployer.core.DeploymentException: Unable to satisfy constraints of 'myapp' version '0.0.0':
Cannot resolve: myapp  Unsatisfied leaf constraints:
Bundle: myapp_0.0.0 - Import-Package: org.springframework.osgi.web.context.support; version="0.0.0"
Did you mean: 'org.springframework.osgi.context.support'?
Bundle: myapp_0.0.0 - Import-Package: freemarker.ext.servlet; version="0.0.0"
Did you mean: 'javax.servlet'?
Bundle: myapp_0.0.0 - Import-Package: freemarker.core; version="0.0.0"
Did you mean: 'org.hamcrest.core'?
Bundle: myapp_0.0.0 - Import-Package: freemarker.template; version="0.0.0"
Did you mean: 'org.antlr.tool'?
Bundle: myapp_0.0.0 - Import-Package: freemarker.cache; version="0.0.0"
Did you mean: 'org.apache'?
这些是预期的消息,因为我没有在平台中安装 freemarker 或 osgi.web.context 支持 Bundle。
  • 访问 http://www.springsource.com/repository。在搜索框中输入“freemarker”,找到匹配的条目,点击链接下载。将下载的 Bundle 复制到 repository/bundles/usr
  • 简化 manifest 文件,使其指向平台上的新 Bundle 和库。原始的 manifest 文件如下:
Import-Package: javax.servlet,javax.servlet.http,javax.servlet.resources,javax.swing.tree,
javax.naming,org.w3c.dom,org.apache.commons.logging,javax.xml.parsers;resolution:=optional,
org.xml.sax;resolution:=optional,org.xml.sax.helpers;resolution:=optional,
org.springframework.osgi.web.context.support, org.springframework.context.support,
org.springframework.web.context, org.springframework.web.context.support,
org.springframework.web.servlet, org.springframework.web.servlet.mvc,
org.springframework.web.servlet.mvc.support, org.springframework.web.servlet.view,
org.springframework.ui, org.springframework.web.servlet.view.
freemarker, freemarker.cache,freemarker.core,freemarker.template,freemarker.ext.servlet
我将其简化为:
Import-Package: org.apache.commons.logging
Import-Library: org.springframework.spring;version="[2.5.4,3.0.0)"
Import-Bundle: com.springsource.freemarker;version="2.3.12"
当我们知道您正在部署 Web 应用时,常用的导入会在部署时自动添加。Import-Library 和 Import-Bundle 让您可以方便地在单个语句中引用库和 Bundle。我还删除了“Bundle-Classpath”条目,因为应用平台会自动检测 WEB-INF/lib 中的库并将其添加到 Bundle 类路径中。
  • 我编辑了 web.xml 并注释掉了 context-param 声明,因为此处无需使用自定义应用上下文类型
  • 再次运行 'mvn',并将 myapp.war 复制到 pickup 目录中。
  • 应用平台自动重新部署了应用
  • 在浏览器中访问 http://localhost:8080/myapp/ .... 成功!
我认为这是一个很好的演示,说明了该平台的价值主张,即为使企业库在 OSGi 下工作铺平了道路。

订阅 Spring 新闻邮件

通过 Spring 新闻邮件保持联系

订阅

领先一步

VMware 提供培训和认证,助力您的进步。

了解更多

获得支持

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

了解更多

近期活动

查看 Spring 社区的所有近期活动。

查看全部