可移植性、炸鱼薯条

工程 | Rod Johnson | 2008 年 5 月 9 日 | ...

在 JavaOne 会议期间,在线上和线下,关于 SpringSource Application Platform 的讨论非常多。WebSphere 事务架构师 Ian Robinson 的一个最有见地的 评论 之一是:

这一切会影响 WebSphere 吗?嗯,Spring 核心框架本身没有任何变化。无论 SpringSource Application Platform 的未来如何,Spring 核心框架项目仍然可以与 WebSphere 互补。就像炸鱼薯条一样。
Ian 的说法完全正确。SpringSource Application Platform 是 Spring 部署的另一种选择。在……方面没有任何改变。

SpringSource Application Platform Manifest 头部

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

SpringSource Application Platform 由 OSGi 捆绑包构建而成,并支持同样由 OSGi 捆绑包构建的应用程序。平台支持 OSGi 的标准功能,但也支持一些额外的清单头部。许多人曾问:“为什么 SpringSource 要添加专有头部?”以及“新头部有什么含义?”,因此本文解释了背景动机以及 Import-LibraryImport-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-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>,并省略分号分隔符,则默认范围包含所有版本。

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

因此,例如,以下头部导入了介于 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-LibraryImport-Bundle 具有与 Import-Package 相同的底层语义,从而避免拆分包的复杂性。

我们还预计,随着平台随时间的推移而发展,我们将需要向 Import-LibraryImport-Bundle 添加进一步的指令,这些指令不适合添加到 Require-Bundle

下一步是什么?

平台 beta 计划 正在进行中,我们将听取所有关于平台功能(包括新的清单头部)的反馈。

对于希望利用 Platform 的头信息,但又需要生成可在其他 OSGi 容器上运行的 bundle 的用户,我们计划推出一个工具,该工具将替换 `Import-Library` 和 `Import-Bundle`……

SpringSource 应用平台部署选项

工程 | Sam Brannen | 2008 年 5 月 6 日 | ...

自上周三发布 SpringSource 应用平台以来,众多开发人员下载了 1.0.0 测试版并开始试用该平台。结果,人们开始询问:“如何在平台上部署我的应用程序?我有哪些部署和打包选项?”此外,开发人员迫切希望看到可用的示例。作为回应,S2AP 团队将在未来几周发布几个示例应用程序,演示这些功能以及更多内容,但在您拿到这些示例之前,我想先给您一个高层次的…

在 OSGi 上使用 SpringSource Application Platform 运行 Spring 应用程序

工程 | Rob Harrop | 2008 年 5 月 2 日 | ...

很多人一直在问 SpringSource Application Platform 究竟能为 Spring 应用程序做什么,使其在 OSGi 上运行良好,这超出了 OSGi 和 Spring Dynamic Modules 开箱即用的功能。Adrian 昨天的帖子重点介绍了一些一般性问题,现在我们来看一些细节。

在 OSGi 上运行 Spring 应用程序面临的三个最具挑战性的方面是

  • 加载时编织
  • 类路径扫描
  • 线程上下文类加载器管理

其余但不太有趣的方面包括:JSP 支持、TLD 扫描、注解匹配和资源查找。总而言之,有一些相当多的问题需要解决才能使应用程序平稳部署。

加载时编织

加载时编织是支持的一项最棘手的功能。在基本层面上,它需要挂钩到 Equinox 的 ClassLoader,以便在 defineClass 调用期间可以附加和使用标准的 ClassFileTransformers。在此基础上,许多 LTW 的用法需要访问一个一次性的 ClassLoader,该 ClassLoader 可用于检查类型以决定在编织期间需要做什么,而不会影响实际的 ClassLoader

这个基本级别的支持实际上是相当容易实现的。困难之处在于,当编织由一个 bundle 中的类驱动,但另一个 bundle 中的类需要被编织时。这在企业应用程序中很常见,其中一个 bundle 包含领域实体,另一个 bundle 包含使用 JPA EntityManager 的类型。Platform 通过确保应用程序中的所有 bundle 都可以使用适当的 ClassFileTransformers 进行编织来处理这种复杂性。

当您开始将编织传播到其他 bundle 时,您确实需要知道何时停止。如果您只是将编织应用于所有 bundle,那么应用程序将会相互干扰。Platform 通过显式地限定编织范围,使其仅应用于应用程序中的模块,从而防止这种情况发生。

LTW 的另一个问题是它会使刷新复杂化。当一个 bundle 被刷新时,OSGi 将刷新所有依赖于它的 bundle。这意味着,在我上面给出的例子中,刷新领域 bundle 会导致 EntityManager bundle 被刷新。然而,刷新 EntityManager **不会**刷新领域 bundle,这意味着编织可能不同步。Platform 通过将刷新传播到受编织影响的其他 bundle 来处理此问题。

类路径扫描

对于类路径扫描,主要问题是 Equinox 不暴露标准的 jar:file: 资源。Platform 在中间放置了一个适配器,以便库可以看到它们期望的资源协议。这有一个很好的副作用,就是使**大量**第三方库能够工作——这不仅仅是类路径扫描的修复。

线程上下文类加载器管理

许多第三方库使用线程上下文 ClassLoader 来访问应用程序类型和资源。OSGi 中的每个 bundle 都有自己的 ClassLoader,因此,在任何时候只能公开一个 bundle 作为线程上下文 ClassLoader。这意味着,如果第三方库需要查看分布在多个 bundle 中的类型,它将无法按预期工作。

Platform 通过创建一个 ClassLoader 来解决这个问题,该 ClassLoader 导入您应用程序中每个模块的所有导出的包。然后,此 ClassLoader 作为线程上下文 ClassLoader 公开,使第三方库能够看到您应用程序中的所有导出类型。

这只是 Platform 所解决问题的一小部分,但希望它能让您对 Platform 对 Spring Framework 用户意味着什么有所了解。

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

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

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

有一些常见的疑问,我想在这篇文章中直接解决。之后,我将描述另外两个激动人心的公告,它们补充了 SpringSource 应用平台本身,但昨天没有登上头条:……

介绍SpringSource Application Platform

工程 | Rob Harrop | 2008 年 4 月 30 日 | ...

经过数月紧张的编码,我很高兴地宣布 SpringSource Application Platform 1.0 的 beta 版本发布。

2007 年初,我们开始讨论替代单体式、重量级应用程序服务器的可能方案,企业 Java 已与这些服务器形影不离。客户正在寻找一个轻量级、模块化且足够灵活的平台,以满足其开发和部署需求。

Spring 和 Tomcat 的组合表明,开发人员和运维人员可以在生产环境中成功使用一个轻量级的平台。尽管这种组合取得了成功,但缺乏模块化和对非 Web 应用程序的显式支持限制了其适用性和灵活性。

我们着手构建 SpringSource Application Platform 以满足这些需求并消除这些限制。

Platform 的核心是 Dynamic Module Kernel (DMK)。DMK 是一个基于 OSGi 的内核,充分利用了 OSGi 平台的模块化和版本控制功能。DMK 基于 Equinox 并扩展了其在配置和库管理方面的功能,同时为 Platform 提供了核心功能。

SpringSource Application Platform Architecture

为了保持最小的运行时占用空间,OSGi bundle 由 DMK 配置子系统按需安装。这使得应用程序可以安装到运行中的 Platform 中,并且其依赖项可以从外部存储库中满足。这不仅消除了手动安装所有应用程序依赖项的繁琐工作,而且最大限度地减少了内存使用。

DMK 本身需要一套最小的 bundle 来运行,并通过一个 **profile** 进行配置,以精确控制加载的附加模块集。例如,DMK 不需要 Tomcat,但默认的 Platform profile 包含 Tomcat 以允许部署 Web 应用程序。如果您想在没有 Tomcat 的情况下运行 Platform,只需编辑 profile 即可,它就不会被安装。(如果您尝试这样做,请记住,删除 Web 支持意味着 Web 模块将不再部署,因此请删除 pickup 目录中的内容,以免 Platform 在启动时尝试安装 Admin 和 splash 屏幕应用程序。)默认的 Platform 配置(安装了管理控制台)仅占用 15MB 内存。

我对企业 Java 的一个长期不满是,应用程序经常被塞进牵强的孤岛,并且缺乏对不同应用程序类型的显式支持。考虑一个在线商店的应用程序。该应用程序有一个 Web 前端、一个消息驱动的订单处理模块、一个批处理驱动的库存重新排序模块和一个 B2B Web 服务模块。如今,许多此类应用程序将被打包成 WAR 或 EAR,并且模块将非常相似,对模块类型差异的支持很少。有趣的是,许多人会将此称为 Web 应用程序,而不是带有 Web 模块的应用程序。

在 SpringSource Application Platform 中,应用程序是模块化的,每个模块都有一个 **personality**,用于描述它是什么类型的模块:Web、批处理、Web 服务等。Platform 以特定于 personality 的方式部署每个 personality 的模块。例如,Web 模块在 Tomcat 中使用 Web 上下文进行配置。应用程序中的每个模块都可以独立于其他模块进行更新,同时保持作为大型应用程序一部分的身份。无论您构建哪种类型的应用程序,编程模型仍然是标准的 Spring 和 Spring DM。

在 1.0 Platform 版本中,我们支持 **web** 和 **bundle** personality,这使您能够构建复杂的 Web 应用程序。未来的版本将根据后续详述包含对更多 personality 的支持。

构建应用程序

Platform 支持三种形式的应用程序打包

  1. Java EE WAR
  2. 原始 OSGi bundle
  3. 平台归档 (PAR)

Platform 直接支持标准的 WAR 文件。在部署时,WAR 文件将被转换为 OSGi bundle 并安装到 Tomcat 中。所有标准的 WAR 合约都得到遵守,您现有的 WAR 文件应该可以直接部署而无需更改。

任何符合 OSGi 标准的 bundle 都可以直接部署到 Platform 中,并可以充分利用对 `Import-Package` 和 `Require-Bundle` 所引用的任何依赖项进行的即时配置。

PAR 格式是为 Platform 打包和部署应用程序的推荐方法。PAR 只是一个 OSGi bundle(模块)的集合,这些 bundle 被分组到一个标准的 JAR 文件中,并带有一个唯一标识应用程序的名称和版本。PAR 文件作为单个单元部署到 Platform。Platform 将提取 PAR 中的所有模块并进行安装。第三方依赖项将根据需要即时安装。

与直接将 bundle 部署到 Platform 相比,PAR 格式有三个主要优势。首先,它更简单。一个中等规模的企业应用程序可能包含 12 个以上的 bundle - 手动部署这些 bundle 将非常繁琐。其次,PAR 文件为应用程序中的所有 bundle 形成了一个显式的范围,防止使用重叠类型或服务的应用程序之间发生冲突。此范围还用于一些高级功能,例如加载时织入,以确保一个应用程序的织入不会干扰另一个应用程序的织入。最后,PAR 形成了一个逻辑分组,用于定义哪些模块是应用程序的一部分以及应用程序有哪些第三方依赖项。此分组由管理工具用于提供应用程序的详细视图。典型的 PAR 应用程序如下所示:

PAR File Structure

应用程序中模块之间的依赖关系通常使用 `Import-Package` 和 `Export-Package` 来表示。对第三方库的依赖关系也可以用同样的方式表示,但对于许多库来说,这可能容易出错且耗时。在使用 Hibernate 等库时,您通常需要导入比最初预期的更多的包。为了解决这个问题,您可以*使用* `Require-Bundle`,但这存在一些语义上的粗糙之处,例如分割包,其中一个逻辑包被分割到两个或多个类加载器中,导致运行时问题。Platform 引入了两种新的机制来引用第三方依赖项:`Import-Bundle` 和 `Import-Library`。`Import-Bundle` 类似于 `Require-Bundle`,但它避免了分割包和其他与 `Require-Bundle` 相关的问题。`Import-Library` 提供了一种机制,可以在单个声明中引用一组 bundle 导出的所有包,例如 Spring Framework 中的所有 bundle。

Bundle-SymbolicName: com.myapp.dao.jdbc
Bundle-Version: 1.0.0
Import-Bundle: org.apache.commons.dbcp;version="1.2.2.osgi"
Import-Library: org.springframework.spring;version="2.5.4.A"

在这里,我有一个依赖于 Commons DBCP bundle 和 Spring Framework 库的模块 bundle。Spring Framework 库包含使用 Spring 在应用程序中所需的所有 bundle。

`Import-Library` 和 `Import-Bundle` 在后台会展开为 `Import-Package`,因此与标准的 OSGi 语义一致。

Platform 理解模块的 personality,并能从中推断出如何配置模块的执行环境。在部署 Web 模块时,典型的 Spring MVC 应用程序所需的所有 Servlet 基础设施都会自动创建,从而无需在应用程序中重新创建这些样板代码。在 1.0 最终发布版中,将为 Web 模块 personality 添加更多智能功能,以支持 Spring Web Flow 等其他技术。

无论您选择哪种打包格式,编程模型都只是 Spring Framework 和 Spring Dynamic Modules,其他 Spring Portfolio 产品运行在其之上。

可服务性

可服务性是整个工程团队的关键考虑因素。我们花费了大量时间来研究日志消息的格式和堆栈跟踪的大小,以确保诊断应用程序问题尽可能容易。每当发现一项重复且耗时的任务时,我们都会寻找一种方法来自动化它或完全消除它。

为了帮助诊断问题,Platform 在日志和跟踪消息之间进行了严格的划分。日志消息旨在供最终用户使用,让您无需筛选大量跟踪内容即可获取最重要的失败信息。所有应用程序故障都会显示并编码在日志输出中 - 代码可方便地用于访问知识库或支持内容。为了理解这一点为何如此有用,请考虑以下 Platform 启动输出:

[2008-04-29 12:12:01.124] main                     <SPKB0001I> Platform starting.
[2008-04-29 12:12:04.037] main                     <SPKE0000I> Boot subsystems installed.
[2008-04-29 12:12:06.013] main                     <SPKE0001I> Base subsystems installed.
[2008-04-29 12:12:07.396] platform-dm-1            <SPPM0000I> Installing profile 'web'.
[2008-04-29 12:12:07.674] platform-dm…

Web 应用程序和 OSGi

工程 | Costin Leau | 2008 年 4 月 29 日 | ...

自 Spring Dynamic Modules 的第一个里程碑以来,运行 OSGi 中 Web 应用程序的请求开始涌入。这可能是最受欢迎的功能之一,而且毫不奇怪,一旦 1.0 最终版发布,Web 支持就一直是 1.1 分支的主要关注点。我很高兴地报告,随着刚刚发布的 M2,正如 Juergen 已经暗示的那样,Spring-DM 不仅支持标准的 war(自 1.1.0 M1 起可用),还支持在 OSGi 中运行的 Spring-MVC 应用程序。在本文中,我想简要讨论典型的 OSGi Web 场景和 Spring-DM 的方法。但首先,

为什么要在 OSGi 中部署 WAR?

简单问题:OSGi *原生* 提供版本控制、包连接和热重加载。想象一下在您的应用程序中利用这些功能:您可以停止将库嵌入WEB-INF/lib并将它们在 Web 应用程序之间共享,避免 taglibs 重复(同时保持多个版本运行),并实时更新应用程序的特定部分。这尤其有用,因为 Web 应用程序往往是分层的,因此在其生命周期中会经历大量更改。

为什么 OSGi 中的 Web 应用程序存在问题?

Servlet 规范围绕着一个*Web 容器*的概念:一个为 Web 组件提供运行时环境的运行时环境,该环境提供标准服务,如生命周期管理(对象创建和处置、线程分配)、并发、HTTP 请求处理等。另一方面,OSGi 平台也作为具有服务注册表、包连接和版本控制(仅举几例)的托管环境。为了解决这个问题,OSGi 委员会设计了 compendium 规范的一部分,即 Http Service

如今,可移植性比以往任何时候都更重要

工程 | Juergen Hoeller | 2008 年 4 月 29 日 | ...

昨天,我写了一篇关于Spring 如何帮助最大化应用程序可移植性的博客。尽管可移植性问题一直是企业 Java 领域的一个持续性话题,但这篇博客非常及时。今天,Oracle 宣布其以 67 亿美元收购 BEA Systems 的交易已完成。两家公司产品集存在大量重叠,因此这必将为 WebLogic 和 OC4J 的客户群带来不确定性。WebLogic 和 OC4J 可能都属于“J2EE 服务器”类别,但它们是截然不同的产品,具有截然不同的特性。

由于许多企业……

框架层面的可移植性

工程 | Juergen Hoeller | 2008 年 4 月 28 日 | ...

可移植性是 Spring 生态系统的关键因素。我们相信框架层面的可移植性:应用程序组件是针对特定框架(或框架代)编写的,例如 Spring 2.5;然后框架负责适应任何底层托管环境。然而,特定的应用程序框架独立于托管环境。只要托管环境的基本功能足够,就可以将一个全新的框架版本部署到一个已建立的托管平台代上。这种方法……

会议季仍在继续

工程 | Rod Johnson | 2008年4月24日 | ...

昨天,我在德国威斯巴登举行的 JAX 会议 上发表了开幕主题演讲。JAX 是欧洲最大的 Java 会议之一,有超过 2000 名与会者。主题是企业 Java 的未来,我详细阐述了我 最近一篇关于预测的博文 中的主题,并深入探讨了 Java EE 6 的影响 以及应用程序服务器的未来。
我已 上传了幻灯片,其中包括对企业 Java 演进过程中一段有趣时期的 8 项预测。这是我第一次在同一场演讲中提及约瑟夫·斯大林、莫妮卡·莱温斯基和蒙提·派森。

获取 Spring 新闻通讯

通过 Spring 新闻通讯保持联系

订阅

领先一步

VMware 提供培训和认证,助您加速进步。

了解更多

获得支持

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件,只需一份简单的订阅。

了解更多

即将举行的活动

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

查看所有