开源,开放战略:SpringSource 宣言

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

作为一家开源软件提供商,我们认为我们也应该对我们的战略保持开放。我们想分享我们是如何走到今天、我们要走向何方以及为什么这段旅程对 Spring、Spring 用户和 SpringSource 都有益。

我们的历史

Spring 的故事始于 2001 年,当时我开始编写我在 2002 年出版的《Expert One-on-One J2EE Design and Development》一书中同时发布的 30,000 行框架代码。我的目标是帮助其他人避免我自 1999 年以来在完成 J2EE 项目时遇到的陷阱。

很快,显而易见的是,其他人喜欢代码中的思想——例如依赖注入和 Spring 数据访问抽象——并从中受益匪实践。有读者找到我,要求我发布代码,并表示愿意贡献。

我很快就看到了开源的一些重要优势。

  • 大多数用户免费获得所需功能
  •     	<li> It…

实现企业集成模式 第 0 部分

工程 | Iwein Fuld | 2008 年 5 月 19 日 | ...

在我关于 Spring Integration 的演讲之后,我收到了很多关于澄清和示例的问题。为了满足需求,我将开始一个小系列,介绍如何使用 Spring Integration 实现不同的集成模式。第一篇文章将重点介绍基础知识。它将向您展示如何启动并运行,并带您了解其中一个示例。

如果您之前从未听说过 Spring Integration,最好阅读Mark Fisher 关于它的介绍性博客或浏览项目网站来熟悉它。一般来说

让我先说一个免责声明:...

我为什么还要关心 OSGi?

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

InfoQ 有一个讨论帖总结了对 SpringSource Application Platform 公告的反应。Michael Burke 在该帖中提出了一个很棒的问题,可以转述为“抛开围绕 OSGi 的炒作,如果我将当前打包为 EAR 的应用程序移植到 OSGi 捆绑包,我能期望看到哪些好处?”。

我开始在 InfoQ 的讨论帖中回答这个问题,但我的回答太长不适合作为评论,所以我将在这里解答。

这个问题提得很好。基于 OSGi 的应用程序与传统的基于 JEE EAR 的应用程序之间的主要区别在于模块化得到了改进。所以问题就变成了,这种改进的模块化是否能带来任何好处,如果能,是什么好处?《设计规则,模块化的力量》一书对这个问题进行了非常详尽的处理。这是一个很好的背景,但我感觉 Michael 可能在寻找比那本书中更少理论性的东西……

使用 SpringSource Application Platform 的 Provisioning 仓库

工程 | Andy Wilkinson | 2008 年 5 月 9 日 | ...

SpringSource Application Platform 的主要优势之一是它能够按需提供依赖项。这样做的好处有两个方面:它确保平台的内存占用尽可能小,并且允许部署应用程序,而无需将其所有依赖项封装在单一的部署单元中,例如 WAR 文件。要利用这些功能,您需要了解平台的 Provisioning 仓库,这篇博客旨在提供这方面的知识。

Provisioning 仓库在哪里,它是如何工作的?

默认情况下,平台的 Provisioning 仓库位于安装根目录下的 repository 目录中:Provisioning 仓库的目录结构 如您所见,主要有三个目录:bundlesinstalledlibrariesinstalled 用于平台内部使用,所以我们将在这里重点关注 bundleslibraries 目录。每个目录都包含许多子目录,用于分隔不同类型的依赖项
  • ext 包含与平台一起提供的外部依赖项,但它们本身不是平台的一部分。
  • subsystems 包含构成平台的所有子系统。
  • usr 最初是空的,旨在包含用户添加的依赖项,即您的应用程序所依赖的、但平台尚未提供的任何内容。
平台在初始启动期间会扫描 repository 目录结构中的捆绑包和库。我将在本文后面讨论如何配置此搜索。当在仓库中找到捆绑包和库时,它们的符号名称、导出的包等详细信息会被添加到仓库的内存索引中。扫描完成后,内存索引会被缓存到磁盘。在开发过程中,最小化平台的启动时间是我们的首要任务。这种缓存使平台在启动时节省了一些时间:除非检测到仓库内容已更改,否则它可以跳过扫描。

运行时 Provisioning

在纯粹的 OSGi 环境中,捆绑包的依赖项只能由环境中已安装的其他捆绑包来满足。例如,如果尚未安装导出 org.apache.commons.dbcp 包的捆绑包,那么安装和启动导入该包的捆绑包将会失败。这对用户来说非常麻烦,因为他们必须手动安装捆绑包的所有依赖项。值得庆幸的是,SpringSource Application Platform 通过按需动态安装依赖项显著改进了这一点。

当平台启动已部署的应用程序时,它的...

可移植性、炸鱼薯条

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

很高兴听到关于SpringSource Application Platform的如此多的讨论,无论是在线上还是在 JavaOne 会场。其中一个最有见地的评论来自 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 的标准特性,但也支持一些额外的 manifest 头信息。有些人问过为什么 SpringSource 添加了专有头信息?新头信息的语义是什么?,所以这篇博文解释了 Import-LibraryImport-Bundle 的背景动机和语义。

标准 OSGi 捆绑包支持

该平台基于 OSGi R4.1 标准构建,或者如果您愿意,也可以说是基于 JSR 291,并使用 Equinox 作为其 OSGi 实现。因此,您可以使用平台的工具开发标准 OSGi 捆绑包,并将这些捆绑包部署到平台上,许多用户自平台发布以来一直在这样做。

因此,熟悉 OSGi 的开发者可以将平台用作标准的 OSGi 容器,并受益于平台的以下特性:

  • 能够使用 Admin Console 或将捆绑包放入平台的 pickup 目录来部署捆绑包,
  • 诊断,例如解析失败诊断、应用程序特定跟踪和自动死锁检测,
  • 与 Spring 和 Spring Dynamic Modules 的强大集成,适用于希望使用这些框架的开发者,以及
  • 从仓库自动 Provisioning 依赖项。
然而,平台还旨在让几乎或完全没有接触过 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 特定的 manifest 头信息,Import-LibraryImport-Bundle,作为表达对企业框架依赖关系的便捷方式。正如您将在下面看到的,这些头信息实际上只是语法糖,它们最终被转换为标准的 OSGi 包导入。

Import-Library

基本语法与其他 manifest 头信息类似
    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)"

可选的库导入

您可以使用以下语法指示库导入是可选的。请注意特殊的 := 分隔符,它表示一个修改 manifest 头信息语义的指令,与表示匹配属性(如 version)的 = 分隔符相对。
    Import-Library: <librarySymbolicName>;version=<versionRange>;resolution:=optional

如果未指定 resolution,或指定为 mandatory,如果不存在具有给定符号名称且在给定版本范围内的库,则包含 import library 头信息的捆绑包将无法安装。但如果指定了 resolution:=optional,则在没有合适的库可用时,该库导入将被忽略。

所以,例如,以下头信息导入了 Spring Framework 库的 2.5 版本或更高版本,但在没有合适的库可用时将被忽略

    Import-Library: org.springframework.spring;version="2.5";resolution:=optional

导入多个库

如果您需要导入多个库,请在单个 Import-Library manifest 头信息中指定一个逗号分隔的库导入列表,如下例所示
    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 项目正在进行中,我们将听取关于平台所有功能(包括新的 manifest 头信息)的反馈意见。

对于希望利用平台头信息,但需要生成能在其他 OSGi 容器上运行的捆绑包的用户,我们计划开发一个工具来替换 Import-LibraryImport-Bundle

SpringSource Application Platform 部署选项

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

自上周三发布 SpringSource Application Platform 以来,许多开发者下载了 1.0.0 beta 版本并开始试用该平台。因此,人们开始询问:“如何将我的应用程序部署到平台上,以及我有哪些部署和打包选项?”此外,开发者们热切地要求看到可运行的示例。作为回应,S2AP 团队将在未来几周发布几个示例应用程序,演示这些功能及更多内容,但在您获得这些示例之前,我想先为您提供一个高层概览...

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

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

许多人一直在问,除了 OSGi 和 Spring Dynamic Modules 本身提供的功能之外,SpringSource Application Platform 究竟为 Spring 应用程序做了什么,使它们能够在 OSGi 下良好运行。Adrian 昨天的博文强调了一些常见问题,现在让我们看看一些细节。

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

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

其余但不太重要的问题包括:JSP 支持、TLD 扫描、注解匹配和资源查找。总的来说,为了使应用程序顺利部署,需要解决相当数量的问题。

加载时织入

加载时织入 (Load-time weaving, LTW) 是最难以可靠支持的功能之一。在基本层面,它需要钩入 Equinox 的 ClassLoader,以便在 defineClass 调用期间可以附加和使用标准的 ClassFileTransformers。除此之外,许多 LTW 的使用需要访问一个临时 ClassLoader,该加载器可用于检查类型以决定在织入期间需要发生什么,而不会影响真实的 ClassLoader

实现这种基本级别的支持实际上是相当简单的。困难在于当织入由一个捆绑包中的类驱动,但需要织入另一个捆绑包中的类时。这在企业应用程序中非常常见,例如一个捆绑包包含领域实体,而另一个捆绑包包含使用 JPA EntityManager 的类型。平台通过确保应用程序中的所有捆绑包都可以使用适当的 ClassFileTransformers 进行织入来处理这种复杂性。

当你开始将织入传播到其他捆绑包时,你确实需要知道何时停止。如果你只是简单地将织入应用于所有捆绑包,那么应用程序之间会相互干扰。平台通过显式地设定织入的作用域,使其仅应用于应用程序中的模块来防止这种情况发生。

LTW 的另一个问题是它使刷新变得复杂。当一个捆绑包被刷新时,OSGi 会刷新所有依赖于它的捆绑包。这意味着,在我上面给出的示例中,刷新域捆绑包将导致 EntityManager 捆绑包被刷新。然而,刷新 EntityManager 不会刷新域捆绑包,这意味着织入可能不同步。平台通过将刷新传播到受织入影响的其他捆绑包来处理这个问题。

类路径扫描

对于类路径扫描,主要问题是 Equinox 不会暴露标准的 jar:file: 资源。平台在中间放置了一个适配器,以便库能看到它们期望的资源协议。这有一个很好的附带效果,使得很多第三方库能够正常工作——这不仅仅是类路径扫描的一个修复。

线程上下文类加载器管理

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

平台通过创建一个 ClassLoader 来解决这个问题,该加载器导入了您的应用程序中每个模块的所有导出包。然后将这个 ClassLoader 作为线程上下文 ClassLoader 暴露出来,使第三方库能够看到应用程序中所有导出的类型。

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

完善全貌:Spring、OSGi 和 SpringSource Application Platform

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

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

人们提出了一些常见问题,我想在这篇博文中立即予以解答。之后,我将介绍另外两个令人振奋的公告,它们与 SpringSource Application Platform 本身相辅相成,但昨天并未成为头条新闻:...

介绍 SpringSource Application Platform

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

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

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

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

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

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

SpringSource Application Platform Architecture

为了保持最小的运行时内存占用,OSGi 捆绑包由 DMK Provisioning 子系统按需安装。这使得应用程序可以安装到运行中的平台中,并且其依赖项可以从外部仓库满足。这不仅消除了手动安装应用程序所有依赖项的需要(这很繁琐),而且将内存使用量保持在最低水平。

DMK 本身只需要最少的捆绑包即可运行,并且配置了一个profile 来精确控制加载的额外模块集合。例如,DMK 不需要 Tomcat 存在,但默认的平台 profile 包含 Tomcat 以允许部署 Web 应用程序。如果您想在没有 Tomcat 的情况下运行平台,您只需编辑 profile,它将不会被安装。(如果您尝试这样做 - 请记住移除 Web 支持意味着 Web 模块将不再能部署,因此请删除 pickup 目录中的内容,这样平台在启动时就不会尝试安装 Admin 和启动屏应用程序。)安装了 admin console 的默认平台配置仅占用 15MB 内存。

我对企业级 Java 一直感到沮丧的一点是,应用程序经常被硬塞进人为划分的孤岛中,并且缺乏对不同应用程序类型的明确支持。考虑一个在线商店的应用程序。该应用程序有一个 Web 前端、一个消息驱动的订单处理模块、一个批处理驱动的库存重新排序模块以及一个 B2B Web 服务模块。如今,许多这样的应用程序都会被打包成 WAR 或 EAR 文件,并且这些模块看起来非常相似,对模块类型的差异几乎没有支持。有趣的是,许多人会将此类应用程序称为 Web 应用程序,而不是一个包含 Web 模块的应用程序。

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

在 1.0 平台发布中,我们支持 webbundle personality,这使您能够构建复杂的 Web 应用程序。未来的版本将包含对更多 personality 的支持,详情将在稍后介绍。

构建应用程序

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

  1. Java EE WAR
  2. 原始 OSGi 捆绑包
  3. 平台归档文件 (PAR)

平台直接支持标准的 WAR 文件。部署时,WAR 文件会被转换成一个 OSGi 捆绑包并安装到 Tomcat 中。所有标准的 WAR 契约都被遵守,您现有的 WAR 文件应该可以直接放入并部署,无需更改。

任何符合 OSGi 规范的捆绑包都可以直接部署到平台中,并且可以充分利用针对 Import-PackageRequire-Bundle 引用的任何依赖项进行的即时 Provisioning。

PAR 格式是平台应用程序打包和部署的推荐方法。PAR 简单来说就是将 OSGi 捆绑包(模块)集合在一起的标准 JAR 文件,并附带一个唯一标识应用程序的名称和版本。PAR 文件作为一个单一单元部署到平台中。平台将从 PAR 中提取所有模块并安装它们。第三方依赖项将按需即时安装。

PAR 格式相对于直接将捆绑包部署到平台有三个主要优点。首先,它更简单。一个中等规模的企业应用程序可能包含 12 个或更多捆绑包——手动部署这些捆绑包将过于繁琐。其次,PAR 文件在应用程序中的所有捆绑包周围形成一个显式的作用域,这可以防止使用重叠类型或服务的应用程序相互冲突。这个作用域也被一些高级功能(如加载时织入)用来确保一个应用程序的织入不会干扰另一个应用程序的织入。最后,PAR 形成了一个逻辑分组,用于定义哪些模块是一个应用程序的一部分,以及应用程序有哪些第三方依赖项。管理工具使用这个分组来提供应用程序的详细视图。一个典型的 PAR 应用程序看起来像这样

PAR File Structure

应用程序中模块之间的依赖关系通常使用 Import-PackageExport-Package 来表达。对第三方库的依赖也可以用同样的方式表达,但对于许多库来说,这可能会出错且耗时。在使用 Hibernate 等库时,您通常需要导入比您最初预期的更多的包。为了解决这个问题,您可以使用 Require-Bundle,但这有一些语义上的粗糙之处,例如拆分包(split packages),其中一个逻辑包被分割到两个或多个类加载器中,导致运行时出现问题。平台引入了两种新的机制来引用第三方依赖项:Import-BundleImport-LibaryImport-Bundle 类似于 Require-Bundle,但它防止了拆分包以及 Require-Bundle 的其他问题。Import-Library 提供了一种机制,可以在一个声明中引用一组捆绑包(例如 Spring Framework 中的所有捆绑包)导出的所有包

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 捆绑包和 Spring Framework 库。Spring Framework 库包含了在应用程序中使用 Spring 所需的所有捆绑包。

Import-LibraryImport-Bundle 在底层会被展开为 Import-Package,因此与标准的 OSGi 语义一致。

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

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

可服务性

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

为了帮助诊断问题,平台在日志消息和跟踪消息之间有清晰的区分。日志消息旨在供最终用户使用,让您无需翻阅海量跟踪内容即可访问最重要的故障信息。所有应用程序故障都会在日志输出中显示并编码 - 代码是访问知识库或支持内容的便捷方式。要理解这为何如此有用,请考虑平台启动时的以下输出:

[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…

获取 Spring 新闻邮件

订阅 Spring 新闻邮件,保持连接

订阅

抢先一步

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

了解更多

获取支持

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

了解更多

近期活动

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

查看全部