内容


使用 WebSphere Application Server V7 Feature Pack for OSGi Applications 从 Spring Framework 迁移到 OSGi

在 OSGi 容器中运行 Spring 应用程序,然后迁移到一个基于标准的依赖性注入容器

Comments

简介

Spring Framework 及其相关项目是广泛使用的一套 Apache licensed 库,旨在让 Java Standard Edition (Java SE) 和 Java Enterprise Edition (Java EE) 环境更便于开发人员使用。Spring 库允许提取企业技术(比如 Java Transactions API),这样应用程序就写入了 Spring API,且可以在 Java SE 或 Java EE 运行时中运行,而无需更改应用程序逻辑。

Spring Framework 的基石之一是 Inversion of Control (IOC) 容器,该容器提供灵活、功能齐全的机制来构建 POJOs (Plain Old Java Objects) 和执行用于连接这些对象的依赖关系图的依赖性注入。该构造和连接逻辑通常在 XML 中提供,但是还有大量其他配置选项。Spring 等 Dependency Injection 框架的主要价值在于,通过移除从应用程序中管理对象关系和生命周期的责任,代码可以将重点放在直接实现业务逻辑上。此外,通过提供被注入对象的存根实现、改进测试覆盖率和代码质量,可轻松对编写良好的应用程序类执行单元测试。

Spring Framework 还因其 Model View Controller (MVC) Web 框架、声明性事务管理支持和 Data Access Object (DAO) 框架而得到大量采用。这些技术通常与其他开源技术(比如 Apache OpenJPA、JBoss 和 Apache Tomcat)或 IBM WebSphere Application Server 等 Java EE 应用程序服务器结合使用。经结合后,这些技术旨在提高基于 servlet 的 Web 应用程序的开发速度,简化其逻辑并在一个轻量级容器中提供企业级服务质量。

OSGi Alliance 是负责 Java 的灵活模块化技术规范的标准组织。提供一个能够并发运行同一代码的多个版本的框架,还有一个用于表达外部依赖关系的强大机制,以及外部提供的模块 API,在 OSGi 中也称为捆绑包。OSGi 定义大量接口和合约,对 Java 运行时主要实现三个方面的增强:

  • OSGi 框架的第一层称为模块层,该层定义以良好封装的方式共享类定义和资源的方法。
  • 第二个 OSGi 层是生命周期层,它定义在一个运行的系统中动态添加、删除、启动、停止和修改捆绑包的方法。这允许以无序的方式调出基于 OSGi 的系统,能够让捆绑包独立于彼此而运行。
  • OSGi 框架的第三层是服务层。服务层定义一个运行时机制,通过这种机制可以松耦合方式动态共享对象实现。服务注册表中的服务可以快速响应系统中的变化,而且捆绑包可以基于运行时中可用的服务主动改变其行为。

虽然很多开发组织重视从使用 Spring 这样的框架中获得的价值,他们常常担心将应用程序容器作为其应用程序的一部分加以维护的负担,而不是将其作为基础中间件平台的一部分予以提供。这些组织可能也担心 Spring 这样的框架给其所在的每个应用程序带来的大量磁盘和内存占用。

此外,有大量开发组织希望利用 OSGi 提供的强大的成熟模块化技术,而且希望利用由 OSGi 服务注册表实现的动态、灵活的应用程序结构。这些组织常常因难以利用 OSGi 和缺乏明确的应用程序编程模型而受到阻碍。

本文描述如何使用 WebSphere Application Server V7 Feature Pack for OSGi Applications 和 JPA 2.0(以下简称 OSGi 功能包)及其 OSGi Blueprint 容器以基于标准的方式获得使用 Spring 的优势,其中大量繁重工作由 WebSphere Application Server 容器完成。本文还就运行和迁移基于 Spring 的应用程序到 OSGi 提供了详细指导。(参见 为 WebSphere Application Server 开发企业 OSGi 应用程序 了解有关 OSGi 应用程序的更多信息。)

什么是 OSGi Blueprint Service?

Blueprint Service(通常称为 Blueprint)是一个基于 POJO 的 OSGi 组件模型。它提供一个依赖性注入容器,允许自动管理一个对象的生命周期。Blueprint 与 OSGi 服务注册表相集成,支持托管的实例透明地使用或被呈现为 OSGi 服务。

对于使用过 Spring Framework 的开发人员来说,Blueprint 看起来非常熟悉。这是因为 Blueprint 标准是与 SpringSource VMware(Spring 框架背后的公司)协力开发的,真正是 Spring Dynamic Modules 项目的一个标准化版本。因此,Blueprint 提供与 Spring 相同的优势给应用程序开发人员,支持松耦合和灵活配置。还有很多其他好的理由来使用 Blueprint(参见 参考资料 中有关 OSGi 最佳实践的文章)。

本文中描述的示例将仅利用 Blueprint 容器中的基本功能,即依赖性注入,将 beans 显示为服务,引用来自 OSGi 服务注册表的服务。但是还有更多高级功能,包括复杂的对象构造、容器调用初始化和析构方法、服务生命周期通知和服务引用列表。更多细节可在 OSGi Service Platform Enterprise Specification 和 “使用 Blueprint Container 规范构建 OSGi 应用程序” 一文中找到,两者均在 参考资料 中。

先决条件

本文面向对 OSGi 感兴趣且对 Spring Framework 依赖性注入模型以及 Java Persistence API (JPA) 或类似的对象关系型映射技术有基本了解的 Spring 和 Java EE 开发人员。您应当熟悉使用 Apache Ant 构建工具和 Java 编程语言。如果您希望执行这里描述的迁移步骤,一个现代 Java IDE(虽然在技术上不需要),比如 Eclipse、Netbeans 或 IntelliJ,会很有帮助。

要运行本文提供的示例,您需要安装通过 OSGi 功能包增强的 WebSphere Application Server V7。要构建样例,您需要 Apache Ant 版本 1.7.1 或更高版本的一份工作副本。

Blether 应用程序简介

本文主要围绕如何将一个基本的社交网络应用程序从基于 Spring 的 Java EE 应用程序迁移到基于 Blueprint 的 OSGi 应用程序。这里描述的样例应用程序 Blether 提供一个基本社交消息服务,允许用户共享状态更新以及查找和跟踪其他用户。

接下来的部分概述样例应用程序,并描述如何构建和安装它。

构建 Blether 应用程序

最初,该应用程序是使用 Spring 和 JPA 构造的一个简单 Java EE 应用程序。在本练习期间,您要将其转换为一个 OSGi 应用程序,然后逐渐消除对 Spring 的依赖性。这不是作为一次性大批量迁移进行的,您将看到如何单独迁移应用程序的各个区块,这样应用程序在迁移步骤间就仍然可用。

为易于使用,样例是作为若干 “快照” 提供的,对应于迁移过程(其中应用程序功能完善)中的检测点。您可以从初始代码开始遵循练习,一次迁移应用程序的一部分。另外,您可以加入任何其他快照。构建过程对于所有阶段都是一样的。

本文以一个 .zip 格式文件形式 提供可供下载的样例,该文件中还进一步包含 .zip 文件,每个文件对应于开发过程中的每个阶段(快照),还有一个脚本目录。脚本目录包含用于安装和卸载应用程序的 python 脚本。所有快照 .zip 文件的结构都一样:一个顶级样例目录,名为 sample,包含顶级构建文件以及持久性和 Web 组件子目录。两个子目录都包含一个 src 源文件目录以及一个构建文件和一个依赖描述符(ivy.xml)。Web 和持久性目录包含一个资源目录。需要更改的大部分配置文件都位于资源的子目录。

要构建应用程序,从 “下载” 部分将适当的代码快照(首先是 SampleInitial.zip 存档)解压到您的工作区目录。导航到样例目录,在这里通过命令行运行 ant。这将首先通过下载 Apache Ivy 然后下载构建依赖项来引导项目。这一步需要一个实用的互联网连接,因为所有依赖项都取自中央 maven 存储库。之后,将为持久性 JAR(或捆绑包)和 Web 模块构建两个子项目,最后将在 build/lib 目录中的最高一级创建 EAR 或 EBA。

尽管命令行是构建项目的一个不错的地方,在实际编辑 Java 代码时 IDE 是一个更好的选择。下面的迁移步骤假定您配有支持基本重构的 IDE,比如将类移入不同的程序包,重命名字段,以及确定导入。要在 Eclipse 中设置项目,执行以下步骤。生成的项目概要是:

  1. 创建一个新的 Java 项目,命名为 Blether
  2. 导入从上述 SampleInitial.zip 中解压出来的样例目录(通过 Import > File System)。
  3. 如上所述运行顶级 build.xml。在 Eclipse 中,这可以通过右键单击 build.xml 文件并选择 Run As > Ant Build 来完成。
  4. 从构建路径中删除 default 源文件夹,添加 persistence/srcweb/src 作为源文件夹。
  5. 将 persistence/lib 和 web/lib 中的所有库添加到构建路径。
图 1. Blether 应用程序的 Eclipse 项目结构
图 1. Blether 应用程序的 Eclipse 项目结构
图 1. Blether 应用程序的 Eclipse 项目结构

安装 Blether 应用程序

脚本化安装过程对于所有快照是一样的。在首次安装样例时,必须创建一个数据库。您可以使用 scripts/createDb.sql 完成这个任务。之后,scripts/bletherSampleUninstall.py 和 scripts/bletherSampleInstall.py 用于安装和卸载应用程序。下面的步骤针对 Windows® 平台。如果您使用 Linux®,只需将 *.bat 替换为 *.sh

  1. createDb.sql 设置样例应用程序需要的数据库。它只需要通过 ${WAS_INSTALL_ROOT}/derby/bin/embedded/ij.bat createDb.sql 运行一次。
  2. bletherSampleInstall.py 安装样例应用程序,并创建手动安装所需的数据源。要安装样例,运行 ${WAS_PROFILE_ROOT}/bin/wsadmin.bat -f bletherSampleInstall.py fullInstall <server name> <node name> <path to sample binary>,其中服务器和节点名称是指 WebSphere Application Server 和节点的名称,“path to sample binary” 是指要安装的 EAR 或 EBA 文件的完全限定名。
  3. bletherSampleUninstall.py 卸载样例应用程序,也可以选择性地卸载数据源。要运行它,像这样调用 wsadmin 脚本:${WAS_PROFILE_ROOT}/bin/wsadmin.bat -f bletherSampleUninstall.py <mode>,其中模式可以是仅限应用程序或全部。

使用 Blether 应用程序

在构建样例之后,导航到 <WAS_DEFAULT_HOST>/blether。这将向您呈现欢迎面板。首次使用应用程序时,您需要创建一个用户 ID。为此,进入注册链接并填充表单。注册您的 ID 之后,您将会被带到主应用程序面板。这个主界面显示您自己和您跟踪的那些用户的状态消息,其中有一个用于发布新消息的文本框。

要验证应用程序在运行,发布一条状态消息,然后登出并创建一个新用户。有了新用户,您可以从右边的用户选项卡跟踪第一个用户。这将列出系统知道的所有用户。单击第一个用户会将您带到一个个人资料页面,您可以在这里选择跟踪用户。您最初创建的状态消息会出现在您的主状态选项卡上。

对应用程序功能的这个最低限度的示范足以测试应用程序到一个 OSGi 应用程序的转换。

迁移阶段 1:将 Blether 打包为一个 OSGi 应用程序

一个 OSGi 应用程序包(或 EBA)的结构很类似于一个 Java EE 应用程序存档(EAR)的结构。一个 EBA 由零个或多个捆绑包组成,打包在一个带有可选应用程序清单(APPLICATION.MF)的 .zip 格式存档中,而且有一个 .eba 扩展名。相比之下,一个 EAR 文件包含一个或多个模块,打包在一个带有应用程序 XML 的 .zip 格式存档中,且有一个 .ear 扩展名。

图 2. EAR 的结构
图 2. EAR 的结构

EBA 和 EAR 之间的主要区别在于,EAR 几乎总是包含存档内的所有模块,且必须包含应用程序 XML,而 EBA 通常仅包含应用程序清单(APPLICATION.MF),且捆绑包理想情况下取自一个捆绑包存储库。(参见 Best practices for developing and working with OSGi applications, #9。)从 EBA 外引用捆绑包是通过 APPLICATION.MF 文件中的配置实现的。默认情况下,如果未指定 APPLICATION.MF 或 Application-Content 头,假定应用程序由 EBA 内存在的每个捆绑包形成。这个默认行为表示不可能有一个不包含捆绑包也不包含 APPLICATION.MF 的 EBA。如果 APPLICATION.MF 不定义一个 Application-Content,EBA 不包含捆绑包也是无效的。

图 3. EBA 结构示例,含有和不含内容的情况下
图 3. EBA 结构示例,含有和不含内容的情况下

本文中描述的前几个迁移阶段依赖于使用默认的 OSGi 应用程序行为。也就是说,尽管您会在该阶段生成一个 EBA,它不会包含应用程序清单。您还要将文件 sample/resources/META-INF/application.xml 留在 EBA 中,因为 OSGi 功能包可以使用文件中的信息预先填充一些配置选项。

在利用任何 OSGi 功能之前,您必须让 Blether 样例作为一个 OSGi 应用程序运行。这意味着 EAR 内的 Java EE 模块需要转化成 OSGi 包。对于 EBA 内的一个 WAR 模块,这个转换可以自动执行,不过最好的做法是在将 WAR 模块添加到 EBA 之前将其转化为一个 OSGi 包。必须手动转换 EAR 内的任何其他项目。

将持久性 JAR 转化成一个捆绑包

要将 Blether 应用程序转化成一个 OSGi 应用程序,首先将持久性 JAR 转化成一个 OSGi 包;更具体地说是一个持久性包,因为该包含有 JAR 内容。

将任何实用程序 JAR 转化成包的第一步是,您需要添加一个适当的捆绑包清单到 JAR。捆绑包清单根据(符号)名称和版本以及捆绑包的依赖项信息定义模块的身份,比如它提供的包和包内的类需要的包。

对于大型 JARs 来说恰当好处地做到后半部分有点棘手。但是,有一个不错的工具(Peter Kriens 的 bnd 工具)可生成或检查这个元数据。

对于 Blether 持久性 JAR,转换简单明了,因此没必要使用工具。纵观两个 JPA 类(~sample/persistence/src 目录下),很明显 javax.persistence 是除 java.* 类之外惟一需要的包。因为您需要在其他包内使用来自这个包的两个类,您将需要导出其在清单中的包。清单 1 显示 MANIFEST.MF 中需要的所有东西。

清单 1. 一个持久性包清单样例
Manifest-Version: 1
Bundle-ManifestVersion: 2
Bundle-SymbolicName: com.ibm.ws.eba.example.blether.persistence
Bundle-Version: 1.0.0
Import-Package: javax.persistence;version="[1.0.0,2.0.0)"
Export-Package: com.ibm.ws.eba.example.springmigration;version="1.0.0"
Meta-Persistence:

注意,导出和导入的程序包有其自己的版本属性。OSGi 允许将版本分配给程序包以及整个捆绑包,此外这些版本是完全独立的。程序包版本是对程序包(而非捆绑包)的用户很重要的版本,因为程序包版本用于确定类兼容性。因为它对捆绑包之间的兼容性如此重要,为一个程序包导出指定一个明确的版本是应当一直遵循的一个最佳实践。

OSGi JPA 服务规范定义 JPA 1.0 程序包的版本是 1.0.0,最佳实践版本控制策略规定,达到(但不包括)下一个主要版本的任何版本都将是兼容的。因此,您可以为程序包导入指定一个版本范围,使用标准 OSGi 语法表示您需要一个从版本 1.0.0(包括)到版本 2.0.0(不包括)的版本范围。要知道程序包的版本不总是匹配它所来自于的规范的版本。在本例中,javax.persistence 的版本 1.0.0 来自 JPA 1.0,但是 JPA 2.0 对应于 javax.persistence 1.1.0。(参见 开发和使用 OSGi 应用程序的最佳实践,#4。)注意,“1.0.0” 的版本范围不同于 “[1.0.0,1.0.0]”。没有定义最大值的版本范围可延伸到无穷远。

总之,该步骤所需的就是创建一个名为 MANIFEST.MF 的文件,在 persistence/resources/META-INF/ 目录中包含上面显示的内容。

Blether 持久性配置的更改

一个持久性 JAR(或持久性捆绑包)定义 JPA 运行时会使用的持久性类和持久性单元,以 EntityManagerFactory 的形式将持久性服务提供给应用程序。

持久性配置文件 persistence.xml 可位于一个 OSGi 包中的任何位置。默认位置是 META-INF/persistence.xml;要支持其他位置,JPA 扩展程序预期将被告知使用 Meta-Persistence 在哪里可以找到 persistence.xml 文件:捆绑包清单中的标头,如上所示。(根据 JAR 规范,要让标头有效,需要一个尾随空格。)

如样例清单所示,存在没有位置的 Meta-Persistence 头,这指示 JPA 扩展程序查找其默认位置中的持久性配置文件 META-INF/persistence.xml。可以使用逗号分隔的路径指定其他位置,包括嵌套 jars 的路径,例如,Meta-Persistence:jpa.xml, persistence.jar!/JPA/persistence.xml。

一般而言,需要对 persistence.xml 文件做少许更改来让它们在一个 OSGi 框架中工作。通常需要更改的惟一部分是对 JTA 和非 JTA 数据源的 JNDI 引用。在一个 Java EE 场景中,这些数据源通常通过一个 java:comp/env 名称空间查询,通过一个 Web 模块资源引用绑定。(这实际上是针对供应商的一个选项,不是由 JPA 规范所定义,规范只需要在这些元素中支持全局 JNDI 名称。)

在样例中,数据源 JNDI 名称不再适用于持久性捆绑包,因为它没有相关的 java:comp 名称空间(不同于 Web 模块,该模块仍然有一个用于向后兼容性的名称空间)。在该样例中,您将遵循 JPA 规范,并将数据源直接映射到全局 JNDI。当然有一个用于使用持久性捆绑包的资源引用的机制,这实际上是最佳实践,但是这将让您偏离本文主题。产生的持久性描述符如清单 2 所示。

清单 2. 一个 OSGi 兼容的 persistence.xml
<persistence xmlns="http://java.sun.com/xml/ns/persistence" 
  version="1.0">
    <persistence-unit name="sampleJPAPersistenceUnit" 
       transaction-type="JTA">
         <jta-data-source>jdbc/userdb</jta-data-source>
         <non-jta-data-source>jdbc/nonXAdb</non-jta-data-source>
         <class>com.ibm.ws.eba.example.springmigration.Post</class>
         <class>com.ibm.ws.eba.example.springmigration.UserInfo</class>
         <exclude-unlisted-classes/>
    </persistence-unit>
</persistence>

总之,第二步,也就是对 Blether 样例进行更改的步骤,就是要修改 sample/persistence/resources/META-INF/persistence.xml 文件来匹配上述样例。

Blether Web 模块的更改

可以使用与对持久性模块所做的类似的更改来将 EAR 中的 Web 模块转化成一个 OSGi 包。在本例中,我们不使用 Meta-Persistence 头来表示捆绑包是一个持久性包,而是使用一个 Web-ContextPath 头来表示捆绑包是一个 Web 应用程序包(WAB)。您将在稍后的转换中执行这一步,但是对于这个阶段,您要依赖于 WebSphere Application Server 执行的自动转换。

Blether WAR 中的 Spring 上下文通过 Web 模块的本地 JNDI 查询它使用的 EntityManagerFactory,其中它由模块的 web.xml 文件中的一个 persistence-unit-ref 所映射。由于持久性单元不再由 Java EE 定义,因此这样的名称不会由 OSGi JPA 运行时填充。相反地,JPA 运行时使用主要 OSGi 集成机制、OSGi 服务注册表发布持久性单元(即 EntityManagerFactory)。不过这不是个问题,因为 OSGi JNDI 规范允许应用程序使用 osgi:service 名称空间将 JNDI 用作 OSGi 服务注册表的一个集成层。

因此,Web 模块所需的惟一变更是更改描述符文件 sample/web/resources/WEB-INF/springapp-service.xml 中的这一行,从:

<jee:jndi-lookup id="entityManagerFactory" jndi-name="jpa/emf"/>

到:

<jee:jndi-lookup id="entityManagerFactory" lookup-on-startup="false" proxy-interface="javax.persistence.EntityManagerFactory" jndi-name="osgi:service/javax.persistence.EntityManagerFactory"/>

这如何工作?首先,新 JNDI 名称指示处理程序从在 javax.persistence.EntityManagerFactory 接口下发布的服务注册表返回服务。因此,不要根据单元名查询 JPA 服务,而是仅使用这里的接口。该查询不含糊,因为 OSGi 应用程序是孤立的且 Blether 仅定义单一持久性单元。在一个拥有多个持久性单元的场景中,可使用一个 LDAP 样式过滤器根据持久性单元限制选择。例如,JNDI 名称 osgi:service/javax.persistence.EntityManagerFactory/(osgi.unit.name=sampleJPAPersistenceUnit) 会仅选择 EntityManagerFactory 服务,其中将规范定义的属性 osgi.unit.name 设置为持久性单元的名称;在该例中为 sampleJPAPersistenceUnit。

对 Spring 应用程序上下文的更改还通过将 lookup-on-startup 设置为 false 添加延迟层到 JNDI 查询,这也需要指定 proxy-interface 属性。设置这个属性之后,Spring 容器仅会在首次需要时检索 EntityManagerFactory 服务。添加这个的目的是消除 Web 模块与持久性捆绑包之间存在的一个竞争状态。在 Java EE 中精心安排了这两个模块的启动,以便在继续后续启动任务之前创建任何资源,这以一个定义明确的方式发生。相比之下,OSGi 应用程序和 OSGi 运行时使用一组松耦合的协作捆绑包,且通常异步完成其各自的任务。这将实现更快速的启动以及更灵活和健壮的应用程序,但是通常需要额外延迟,因为对于启动顺序没有保证。

在该阶段,您还可以选择删除 web.xml 中的 persistence-unit-ref 元素,因为它们不再用作任何用途。但是它们的存在不会导致任何问题。

构建步骤 1 的样例应用程序

此时,您已经做了阶段 1 所需的所有代码更改。与此迁移阶段对应的代码可在 SampleStage1.zip 中找到。然而,由于您需要构建一个 EBA 而非 EAR,您需要对构建脚本 sample/persistence/build.xml 和 sample/build.xml 做一些小的更改。

在 sample/persistence/build.xml 脚本中:

  1. 向 jar 任务添加一个清单属性(例如 manifest="resources/META-INF/MANIFEST.MF")。
  2. 添加一个代码行,去除 jar 任务内的文件集中的 MANIFEST.MF;例如:

    <fileset dir="${basedir}/resources" includes="**/*">
    <exclude name="META-INF/MANIFEST.MF" />
    </fileset>

在 sample/build.xml 脚本中:

  1. 不创建 blether.ear,而是创建一个名为 blether.eba 的 .zip 文件;例如, <zip destfile="build/lib/blether.eba">。记住要将终止的 /ear> 替换为 /zip>
  2. 删除从 EAR 文件中排除 application.mf 的三个代码行,并添加一个代码行(<fileset dir="resources" includes="**/*" />),确保它包含在 blether.eba 中。

在这些更改之后或通过 SampleStage1.zip 快照进行重建时,将在 build/lib 中创建一个新的 OSGi 应用程序二进制文件。应用程序现在是功能齐全的一个 OSGi 应用程序。在 WebSphere Application Server 中测试它,即首先使用 scripts/bletherSampleUninstall.py 脚本删除上一版本,之后使用 scripts/bletherSampleInstall.py 脚本安装 EBA。验证您可以运行 Blether 应用程序。

迁移阶段 2. 从 Spring 到 OSGi Blueprint Container

在上一节,您将 Blether 样例应用程序转化成了一个 OSGi 应用程序。在做了这些变更之后,下一个任务是将 Spring 依赖性注入框架替换为 Blueprint 框架。您还会看到如何仅使用标准 Java EE APIs 替换常见的 Spring 实用工具,比如 JpaTemplate。

如同上一节,您将依次对持久性捆绑包和 Web 捆绑包进行更改。

重构 Blether 服务

您必须对 Blether 持久性服务进行几个更改。首先,它取决于 JpaTemplate 类。需要移除该依赖性。另外,持久性服务在属于 Web 模块一部分的 BletherImpl* 中实现。分离表示层(Web 模块)和业务(或持久性)层会更行得通。为此,您要将 Blether 服务迁移到持久性捆绑包,从这里 Web 模块可以通过 OSGi 服务注册表访问它。这还将为您提供机会来引入第一部分 Blueprint。

在 Blether 实现中不要使用 JpaTemplate

Blether 服务使用 Spring 框架提供的 JpaTemplate 实用工具类来为 JPA 添加其他方法。JpaTemplate 效仿 javax.persistence.EntityManager 接口,但不真正实现它。这表示,JpaTemplate 上的绝大多数方法在标准接口中也可用,特别是基本接口:persist、merge 和 find。

移除依赖性的第一步是要在 Blether 服务类(BletherImpl.java)的开始将 org.springframework.orm.JpaTemplate import 替换为 javax.persistence.EntityManager。另外还需要添加 import javax.persistence.PersistenceContext;

其次,您需要将注释从对应于 JpaTemplate 或 javax.persistence.EntityManagerFactory 的 @PersistenceUnit 更改为对应于 javax.persistence.EntityManager 的 @PersistenceContext。您还要将字段 jpaTemplate 重命名为误导性小的名称,比如 persistenceService

从清单 3 中所示的代码入手应用上述更改。结果应当如清单 4 所示。

清单 3. Blether 服务持久性上下文注入
  @PersistenceUnit(unitName = "sampleJPAPersistenceUnit")
  private JpaTemplate jpaTemplate;

  public void setJpaTemplate(JpaTemplate j)
  {
    jpaTemplate = j;
  }
清单 4. 修改的 Blether 服务持久性上下文注入
  @PersistenceContext(unitName = "sampleJPAPersistenceUnit")
  private EntityManager persistenceService;

  public void setPersistenceService(EntityManager em)
  {
    persistenceService = em;
  }

在应用这些更改之后,全局性地将 jpaTemplate.find 替换为 persistenceUnit.find 应当会让文件中的四个编译错误对应于使用 JpaTemplate 的额外功能的情况,EntityManager 中不提供这些功能。特别地,您会缺少 find(String, Object ...) 方法。需要更改的 4 个方法是:

  • checkEmailUniqueness
  • getPostsByUser
  • getPostsForUserProfile
  • searchUserName

例如,getPostsByUser 方法需要从清单 5 所示代码更改为清单 6 所示代码。

清单 5. 使用 JpaTemplate 的 getPostsByUser
  @SuppressWarnings("unchecked")
  public List<Post> getPostsByUser(String username)
  {
    List<Post> result = jpaTemplate.find(getMyPostsQuery, username);
    return result;
  }
清单 6. 仅使用 JPA 标准接口的 getPostsByUser
  @SuppressWarnings("unchecked")
  public List<Post> getPostsByUser(String username)
  {
    Query query = persistenceService.createQuery(getMyPostsQuery);
    query.setParameter(1, username);
    return query.getResultList();
  }

需要做相同的更改来修复其余三个编译错误。因此,checkEMailUniqueness 方法变为清单 7 中所示的代码。

清单 7. 仅使用 JPA 标准接口的 checkEMailUniqueness
  @SuppressWarnings("unchecked")
  public boolean checkEMailAddressUniqueness(String emailAddress)
  {
    boolean result = false;

    Query query = persistenceService.createQuery(uniqueEmailAddressQuery);
    query.setParameter(1, emailAddress);
    List<UserInfo> users = (List<UserInfo>) query.getResultList();

    if (users.isEmpty()) {
      result = true;
    }

getPostsForUserProfile 方法变为清单 8 中所示的代码。

清单 8. 仅使用 JPA 标准接口的 getPostsForUserProfile
  @SuppressWarnings("unchecked")
  public List<Post> getPostsForUserProfile(String userName)
  {
    Query query = persistenceService.createQuery(getPostsVisibleToUserQuery);
    query.setParameter(1, userName);
    return query.getResultList();
  }

最后,searchUsername(String usernameFilter) 变为清单 9 中所示的代码。

清单 9. 仅使用 JPA 标准接口的 searchUsername
  public List<UserInfo> searchUsername(String usernameFilter)
  {
    usernameFilter += "%";
    Query query = persistenceService.createQuery(searchUserNameQuery);
    query.setParameter(1, usernameFilter);
    return query.getResultList();
  }

此时,您完成了对 BletherImpl.java 类的更改。

将 Blether 服务迁移到持久性捆绑包中

您需要将使用 JPA 服务的应用程序逻辑从 Web 模块迁移到持久性捆绑包。正是这一阶段真正需要一个好的 Java IDE,比如 Eclipse。(描述重构 Blether 代码所需的文件更改对于本文来说没有任何价值,因为 IDE 会为您料理这一切。)

要重构:

  1. 将 BletherUserInterface.java 类迁移到持久性捆绑包中的 com.ibm.ws.example.springmigration 程序包。
  2. 将 BletherImpl.java 类迁移到持久性捆绑包中的 com.ibm.ws.example.springmigration.impl。

这些变更都不需要更新持久性捆绑包的清单,因为没有导入或导出新程序包。

重新连接 Blether 服务来使用 Blueprint

现在 Blether 服务存在于持久性捆绑包中,您需要确保它可由 Blether WAR 中的 servlets 访问。正如您所看到的,交换服务的地方(在一个 OSGi 环境中)是 OSGi 服务注册表。您可以通过 osgi:service 名称空间暂时连接到 OSGi 注册表。在 Spring 描述符(sample/web/resources/WEB-INF/springapp-service.xml)中,删除 bletherService bean、EntityManagerFactory 的 JNDI 查询和 jpatemplate bean。在其位置中,您希望为 Blether 服务执行一个查询,如清单 10 所示。

清单 10. 从服务注册表获取持久性服务
  <jee:jndi-lookup lookup-on-startup="false" id="bletherService" 
   proxy-interface="com.ibm.ws.eba.example.springmigration.BletherUserInterface" 
   jndi-name="osgi:service/com.ibm.ws.eba.example.springmigration.BletherUserInterface"/>

您还需要让 Blether 服务在服务注册表中可用。在简单 OSGi 中,实现这一点的方法是添加一个捆绑包激活器到持久性捆绑包来实例化 Blether 服务,然后使用 org.osgi.framework.BundleContext 上的 API 来将其发布到服务注册表。然而这将是不必要的框架,可通过使用 Blueprint 这样的依赖性注入框架来避免。

您要添加一个 Blueprint 描述符到持久性捆绑包。为此,使用清单 11 中所示的内容创建 sample/persistence/resources/OSGI-INF/blueprint/blueprint.xml。

清单 11. 持久性捆绑包的 Blueprint 描述符
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">

  <bean id="bletherImpl"
    class="com.ibm.ws.eba.example.springmigration.impl.BletherImpl" />

  <service ref="bletherImpl" 
    interface="com.ibm.ws.eba.example.springmigration.BletherUserInterface" />

</blueprint>

如您所料,Blueprint 描述符非常类似于 Spring 描述符。顶级元素和名称空间声明改变了,但是 bean 声明基本一样。服务元素更有趣。它将 bletherImpl bean 发布到 BletherUserInterface 接口下的服务注册表中。显式地指定接口似乎有点限制,且在重命名接口或将接口迁移到另一个程序包时需要更改 XML。一个替代方法是指定属性 auto-export=”interfaces”,它会针对 bean 实现的所有接口将 bean 注册为一个 OSGi 服务。在该示例中,两种方法产生同样的结果。

最后的区别是 JPA 服务的注入,这在 Blueprint 描述符中没有。有了 Blueprint 和 OSGi JPA,EntityManager 的注入基于 BletherImpl 中指定的 @PersistenceContext 发生在幕后。(这个注入仅针对由 Blueprint 容器管理的实例。)

这一需求可能不会总是得到满足。例如,没有方法可以匹配 JPA 注释与工厂创建的 bean。对于这种情况,有一个用于注入 JPA 服务的 Blueprint 扩展名称空间,该机制还在仅使用注释的情况下在幕后得到使用。清单 12 显示在 Blueprint 中通过工厂类构造 beans 所需的 Blueprint XML 示例。(这仅供参考,不在该迁移示例中使用。)

清单 12. 使用 JPA 扩展的持久性捆绑包的 Blueprint 描述符
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
  xmlns:jpa="http://aries.apache.org/xmlns/jpa/v1.0.0">

  <bean id="bletherImpl"
    class="com.ibm.ws.eba.example.springmigration.impl.BletherFactory"
    factory-method=”createBletherService”>
    <jpa:context property=”entityManager” unitname=”sampleJPAPersistenceUnit” />
  </bean>
  
  <service ref="bletherImpl" 
    interface="com.ibm.ws.eba.example.springmigration.BletherUserInterface" />

</blueprint>

有了这些变更,持久性捆绑包和核心应用程序逻辑就安排妥当了(OSGi)。现在您可以重新构建和重新安装应用程序来确保一切仍然运作正常。此检测点处的应用程序在 SamepleStage2.zip 文件中提供。

迁移阶段 3:更改 Blether Web 模块来使用 Blueprint

迁移持久性层和应用层之后,现在您可以将注意力转向其余 Web 层了。目标在于将 Spring 描述符和特定于 Spring 的 APIs 替换为 Blueprint 和标准 APIs。您要分两步处理这个任务。首先,消除对 Spring 的 HttpRequestHandler 机制的依赖性,然后迁移 Spring 描述符。

替换 HttpRequestHandler 机制

对 Spring Web 应用程序的一个常见顾虑是,Web 描述符中声明的 servlets 由 Web 容器而非 Spring 管理。这表示,它们不能使用 Spring 依赖性注入框架提供的优势。此外,需要为 servlets 编写桥接逻辑,以使用在 Spring 描述符中定义的 beans。这是个不太理想的事态。所幸,Spring 框架包含一个实用工具 HttpRequestHandlerServlet,它处理桥接逻辑且委托给 HttpRequestHandler。这些为 Spring 用户提供与 servlet 功能同等的功能。

作为一个简单依赖性注入框架,Blueprint 对于 Web 容器与 Blueprint 容器之间的桥接存在完全相同的问题。但是,在这种情况下没有标准化的实用工具,比如 HttpRequestHandlerServlet。不过您可以开发一个可重用机制来提供相同的功能。

创建 AppRequestHandler 接口

您需要在 /web/src/com/ibm/ws/eba/example/springmigration/web/ 中创建一个新接口来封装与 HttpRequestHandler 相同的功能。该接口定义一个处理任意 HTTP 请求的 servlet 的一个通用抽象(清单 13)。

清单 13. AppRequestHandler 接口
public interface AppRequestHandler {
  void handleRequest(HttpServletRequest arg0, HttpServletResponse arg1) 
       throws ServletException, IOException;
}

创建 ForwardServlet 类

您需要替换 HttpRequestHandlerServlet,因为这一工具是特定于 Spring 的,不适用于 Blueprint。从 Web 描述符的角度来看,HttpRequestHandlerServlet 代表 Blether 使用的惟一 servlet。它基于 servlet 名称和 bean 名称(预计会完全匹配)选择目标 HttpRequestHandler。目前,您要构建稍微简单一点的东西,有明确的名称匹配配置。在 /web/src/com/ibm/ws/eba/example/springmigration/web/ 中创建如清单 14 所示的 ForwardServlet 类。

清单 14. ForwardServlet 实现
public class ForwardServlet extends HttpServlet {

  @Override
  protected void service(HttpServletRequest req, 
                         HttpServletResponse resp)
      throws ServletException, IOException
  {
    try {
      InitialContext ic = new InitialContext();
      AppRequestHandler handler = (AppRequestHandler) 
        ic.lookup("osgi:service/" + 
          AppRequestHandler.class.getName() + "/" +
          "(servlet.name=" + getServletName() + ")");

      if (handler != null) {
        handler.handleRequest(req, resp);
      } else { 
        System.out.println("ERROR: No handler for servlet"+getServletName());
      }
    } catch (NamingException ne) {
      ne.printStackTrace();
    }
  }
}

上述代码没有什么特别或新颖之处。ForwardServlet 的惟一顾虑是查询正确的 AppRequestHandler 并将传入的请求转发给它。作为一个集成机制,您要再次使用 OSGi 服务注册表,像以前一样通过一个 JNDI 查询予以访问。另外,可通过 osgi-bundlecontext 属性从 servlet 上下文提供 Web 捆绑包的 BundleContext。为了区分不同的请求处理程序,您需要一个额外的服务属性 servlet.name,该属性预计会匹配传入请求的 servlet 名称。

将 Spring 描述符转化为 Blueprint

最后,该是时候将其余 Spring 描述符全部转化成 Blueprint 了。除了转化语法差异(比如标记名称)之外,您还需要使用适当的 servlet.name 服务属性在服务注册表中注册请求处理程序。最后,.jee、.tx 和 .aop 等 Spring 扩展名需要全部替换为适合的 Blueprint 扩展名。

创建 Blueprint XML

目前,Spring 描述符类似于清单 15(细节省略)。

清单 15. Web 模块的 Spring 描述符
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:tx="http://www.springframework.org/schema/tx"
  xmlns:aop="http://www.springframework.org/schema/aop"
  xmlns:jee="http://www.springframework.org/schema/jee">

  <tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
      <tx:method name="get*" read-only="true"/>
      <tx:method name="*"/>
    </tx:attributes>
  </tx:advice>

  <aop:config>
    <aop:pointcut id="servletRequest" expression=
      "execution(* com.ibm.ws.eba.example.springmigration.web.*.handleRequest(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="servletRequest"/>
  </aop:config>

  <bean id="RegistrationServlet"
    class="com.ibm.ws.eba.example.springmigration.web.Registration">
    <property name="bletherHandle">
      <ref bean="bletherService"/>
    </property>
  </bean>

  ... A lot more request handlers ...

  <jee:jndi-lookup lookup-on-startup="false" id="bletherService" 
    proxy-interface="com.ibm.ws.eba.example.springmigration.BletherUserInterface" 
    jndi-name="osgi:service/com.ibm.ws.eba.example.springmigration.BletherUserInterface"
  />

  <tx:jta-transaction-manager/>

</beans>

下一步是创建一个 Blueprint 描述符;即创建 sample/web/resources/OSGI-INF/blueprint.xml。您可以通过查看 sample/web/resources/WEB-INF/springapp-service.xml 开始构建该文件的内容了。

  • .jee 扩展名仅用于通过 JNDI 查询 Blether 服务。但是 Blether 服务实际上存在于 OSGi 服务注册表,且 Blueprint 定义了一种更直接的方式,通过引用元素使用来自 OSGi 服务注册表的服务。因此,不需要 .jee 功能且您不需要在 Blueprint 描述符中创建任何等同功能。
  • .aop 和 .tx 扩展名在 springapp-service.xml 中用于声明围绕所有请求处理程序的事务。实现这种声明性事务的机制是通用且正交的,基于内容导向。这意味着所有单个部分可独立用于各种用途。Blueprint 还支持声明性事务。

    具体而言,您需要使用 http://aries.apache.org/xmlns/transactions/v1.0.0 名称空间,它定义一个元素,为一个封闭的 bean 元素声明事务性行为。

    要注意这里描述的扩展名、上面使用的 .jpa 扩展名和基础扩展名机制是特定于 Apache Aries 项目的 Blueprint 实现的,该项目属于 OSGi 功能包的一部分。目前在核心 Blueprint 规范中没有定义好的扩展名机制。

除了这些变更,您需要为每个请求处理程序创建一个服务导出定义。为此,使用已经在持久性捆绑包的 Blueprint 描述符中引入的服务元素。最后,需要更改一些小的语法差异;例如,一个属性元素内的 ref 元素有一个 id 属性,而非 Spring 中使用的 bean 属性。

Blueprint 描述符文件类似于清单 16。

清单 16. Web 模块的 Blueprint 描述符
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
           xmlns:tx="http://aries.apache.org/xmlns/transactions/v1.0.0">

  <bean id="RegistrationServlet" 
    class="com.ibm.ws.eba.example.springmigration.web.Registration">
    <tx:transaction method="*" value="Required"/>
    <property name="bletherHandle" ref="bletherService" />
  </bean>

  <service ref="RegistrationServlet" 
    interface="com.ibm.ws.eba.example.springmigration.web.AppRequestHandler">
    <service-properties>
      <entry key="name" value="RegistrationServlet" />
    </service-properties>
  </service>

  ... Repeat the bean definition and service element for each resource...

  <reference id="bletherService" 
    interface="com.ibm.ws.eba.example.springmigration.BletherUserInterface" />

</blueprint>

这比它替换的 Spring 描述符略微更冗长些。这主要是由于其他服务元素,而且事务现在是以 bean 为单位声明的,而非全局性声明 — 不过如果您看一下 SampleStage3.zip 内容,您会看到一些 tx 元素实际上并不需要且已被删除。使用扩展名,还可以通过 Blueprint APIs(特别是 org.osgi.service.blueprint.container.BlueprintContainer)直接消除服务元素的开销,并根据名称查询 beans(类似于 HttpRequestHandlerServlet 所做的)。Blueprint 容器可以取自OSGi 服务注册表(这不足为奇)。我们将在本文末尾返回该话题。

清理 web.xml 文件

引用 Spring 的部分可以从 web/resources/web.xml 文件中删除了,这只表示要删除 <context-param> 和 <listener> 部分。

清理 ivy.xml 文件

在该阶段,对 Spring 框架的所有依赖性都被切断,且可以从 Web 项目的 ivy.xml 文件中删除对应的代码行。只有三个依赖项仍然保留:javax.servlet、javax.persistence 和 com.google.code.gson。可以删除 springapp-service.xml 文件。

应用程序所处阶段在 SampleStage3.zip 快照中被捕获。如果愿意,您可以使用前面部分描述的 uninstall/install 脚本测试它。

迁移阶段 4:收尾

在该阶段,完全将 Blether 应用程序从 Spring 模型转化为 OSGi 了,特别是 Blueprint 模型。但是有一点清理工作还需要做,这在前面的步骤中被省略了。最重要的是,应用程序依赖于应用程序清单的自动完成和 Web 模块的自动转换。虽然这些功能非常便于使用 OSGi 功能包快速转换要运行的应用程序,但您通常会希望对生产中的应用程序有更严格的控制。例如,自动生成的应用程序清单不允许细粒度更新。而且自动转换的 WAR 模块将所有依赖项看作是可选的,这样 OSGi 解析程序就不会确保 Web 模块的所有依赖项都存在。

将 Web 模块转化为一个 Web 应用程序包

首先,您要将 Web 模块转化成一个合适的捆绑包,这是创建一个完整应用程序清单的先决条件。正如在 OSGi 中,这一切都通常涉及到仔细制作合适的清单。根据 OSGi Web 应用程序规范(OSGi Enterprise spec rev. 4.2, 128),除了确定正确的 Import-Package 语句之外,一个 Web 模块尤其需要适当地设置捆绑包类路径,还要有一个标头来将捆绑包指示为一个 Web 捆绑包。将清单 17 中所示的内容编辑到 sample/web/resources/META_INF/MANIFEST.MF 文件中。

清单 17. Blether Web 应用程序包清单
Manifest-Version: 1.0
Bundle-ManifestVersion: 2 
Web-ContextPath: /blether
Bundle-SymbolicName: com.ibm.ws.eba.example.blether.web
Bundle-Version: 1.0.0
Bundle-Classpath: WEB-INF/classes/,WEB-INF/lib/JSON4J.jar
Import-Package: javax.servlet;version=2.5,
 javax.servlet.http;version=2.5,
 javax.el;version=2.1,
 javax.servlet.jsp;version=2.1,
 javax.servlet.jsp.el;version=2.1,
 javax.servlet.jsp.tagext;version=2.1,
 com.ibm.ws.eba.example.springmigration;version="[1.0.0,2.0.0)"

清单中的导入程序包头是相当不言自明的。您需要 servlet API 程序包,当然还有来自持久性捆绑包的持久性实体和 BletherUserInterface 类。捆绑包类路径头更有趣。该头是必需的,因为在一个 Web 应用程序(以及一个 Web 应用程序捆绑包)中,类不在捆绑包的根部,而是在 WEB-INF/classes 中。而且,嵌入的库存在于 WEB-INF/lib/*.jar 中。OSGi 通过 Bundle-Classpath 头提供一个标准的机制来定义这些非标准的类路径,这里需要用到它们是因为 Web 应用程序捆绑包不再从 Java EE Web 容器中使用的约定中受益。由于您在前面的步骤中删除了所有 Spring 依赖项,需要提供的惟一 JAR 库是 JSON。再进一步,就会很容易将该依赖项提取到其自己的捆绑包(元数据已存在)中,从而进一步将其 OSGi 化为 Web 捆绑包。这留给您稍后做练习。

最后,有一个 Web-ContextPath 头。它主要有两个功能。首先,它指定安装 Web 应用程序所在的默认上下文路径。但是在 WebSphere Application Server 上,安装应用程序时可重写该默认设置。其次,也是较为重要的一点是,它将捆绑包指示为一个 Web 捆绑包。因此与没有 Meta-Persistence 的持久性捆绑包类似,没有 Web-ContextPath 头的 Web 捆绑包不会被看作是 Web 捆绑包。

创建应用程序清单

一个 OSGi 应用程序的应用程序清单位于 .eba 存档内的 META-INF/APPLICATION.MF,它描述一个 OSGi 应用程序的身份和逻辑内容。清单 18 显示 Blether 应用程序的一个样例清单。

清单 18. Blether 应用程序清单
Manifest-Version: 1.0
Application-Name: Blether spring migration sample
Application-SymbolicName: blether.eba
Application-Version: 1.0.0
Application-Content: com.ibm.ws.eba.example.blether.persistence;version="[1.0.0,2.
 0.0)",com.ibm.ws.eba.example.blether.web;version="[1.0.0,2.0.0)"

有趣的部分主要是应用程序内容头。该头定义应用程序的逻辑内容;在本例中是 Web 捆绑包和持久性捆绑包。在以这种方式声明内容之后,捆绑包不再需要包含在 .eba 存档本身。而是可以从捆绑包的一个外部存储库或 WebSphere Application Server 内部捆绑包存储库中拉入捆绑包。要知道,对于 com.ibm.ws.eba.example.blether.persistence;[1.0.0,2.0.0) 这样一个版本范围,.eba 存档与任何已定义捆绑包存储库之间可用的持久性捆绑包的最高可用版本会被用到,不一定是 .eba 中包含的那一个。版本范围的一个最终目的是要定义可以更新应用程序中的捆绑包的界线。例如,在上述清单中,应用程序可以由 1.0.0(包括)与 2.0.0(不包括)之间的任何版本的 Web 捆绑包和持久性捆绑包组成。(参见 参考资料。)

要为 Blether 样例创建一个应用程序清单,将清单 18 中的样例代码粘贴到 sample/resource/META-INF/APPLICATION.MF 中。

ForwardServlet 的收尾工作

作为最后的改进,再次看一下阶段 3 中开发的 ForwardServlet。它有相当多的 boilerplate Blueprint XML,用于定义请求处理程序 beans,然后通过正确的服务属性将它们导出到服务注册表。如果 ForwardServlet 使用了一个类似于 Spring 的 HttpRequestHandlerServlet 的约定优于配置(convention-over-configuration)方法,这会更整洁,其中 bean 的 ID 预期匹配 web.xml 中定义的 servlet 名称。

原来仅需一次小小的重写就可以实现这一点。清单 13 显示基本实现。不要从服务注册表查询特定 AppRequestHandler,而是要从那里为 Web 捆绑包获取 BlueprintContainer。然后将其用于获取 ID 等于 servlet 名称的 bean 实例。(现在该实现取决于 Blueprint 接口,而非仅仅使用通用的服务注册表。)

清单 19. 使用约定优于配置方法的 ForwardServlet
import org.osgi.service.blueprint.container.BlueprintContainer;

public class ForwardServlet extends HttpServlet {

  @Override
  protected void service(HttpServletRequest arg0, 
                         HttpServletResponse arg1)
      throws ServletException, IOException
  {
    try {
      InitialContext ic = new InitialContext();
      BlueprintContainer container = (BlueprintContainer) ic.lookup("osgi:service/" 
        + "org.osgi.service.blueprint.container.BlueprintContainer/" 
        + "(osgi.blueprint.container.symbolicname=com.ibm.ws.eba.example.blether.web)");
      
      AppRequestHandler handler = (AppRequestHandler) 
        container.getComponentInstance(getServletName());
      
      if (handler != null) handler.handleRequest(arg0, arg1);
      else System.out.println("ERROR: No handler for servlet"+getServletName());
      
    } catch (NamingException ne) {
      ne.printStackTrace();
    }
  }
}

除了更改 Java 代码之外,您还需要在捆绑包清单中声明对 Blueprint 的新依赖性,方法就是添加以下代码到程序包导入头:

org.osgi.service.blueprint.container;version="[1.0.0,2.0.0)"

将该代码行添加到 MANIFEST.MF 文件中时,不要忘记在前一行的末尾加一个逗号。

Ivy 配置的更改

最后一次重新构建应用程序之前,还需要进行一项更改来让 Blueprint API 在编译阶段可用。为此,添加一个依赖性声明:

<dependency org="org.osgi" name="org.osgi.compendium" rev="4.2.0" conf="*->*,!sources"/>

将其添加到 Web 项目中的 ivy.xml 中,而且要确保新的 JAR 不作为 WAR 文件的一部分打包。后半部分实际上不重要,因为在 OSGi 中是为类搜索程序包导入,而非冲突情况下捆绑包的内容。

结束语

本文完成了从 Blether Spring 应用程序到 Blether OSGi 应用程序的转换。将该示例作为一个模板来转化您自己的 Spring 应用程序并利用 OSGi 的优势。最终的完整样例代码可见于 SampleSnapshotFinal.zip 下载文件


下载资源


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=WebSphere
ArticleID=659696
ArticleTitle=使用 WebSphere Application Server V7 Feature Pack for OSGi Applications 从 Spring Framework 迁移到 OSGi
publish-date=05192011