转自:http://www.ibm.com/developerworks/cn/websphere/library/techarticles/0912_wangzq_wasosgi/index.html
为了解决现实工程中遇到的版本冲突问题,我研究了 OSGi 技术。为此,我在网上搜索了很多的有关于 OSGi 的文章,但是最后发现很少有人能清楚的阐述 OSGi 的由来和 OSGi 最本质的特性,直到我发现 Neil Bartlett 的 OSGi In Practice。它让理解了 OSGi 的本质,从而清楚的明白:解决 Java 工程中的版本冲突问题,OSGi 是最好的选择。这是由 OSGi 的本质决定的,这也是为什么大的 Java 软件产品纷纷开始采用 OSGi 框架的原因。Java 有天生的缺陷,而 OSGi 弥补了它。然后我又花了大量的时间来研究 WebSphere Application Server 是如何支持 OSGi 的,最后才能成功的使我的应用支持 OSGi,从而解决了版本冲突问题。但这一整个过程耗费了我大量的时间和精力,为了能让遇到同样问题的人少走弯路,我写了这篇文章,希望能够有所帮助。
随着科技和需求的发展和变化,现在的软件变得越来越庞大。这样,随之而来的最大挑战就是软件在设计上的越来越复杂和维护上的越来越困难。为了解决这个问题,软件架构师将软件切分成比较小的并且易于理解的多个模块。那么软件模块化会给我们带来什么样的好处呢?
- 拆分人力:将软件模块化后,我们就可以分配独立的团队去处理独立的模块,从而将人力拆分开来。这样既便于管理,又会降低整个软件的设计的复杂性。因为每个独立的团队可以专心去设计和实现其模块,而不用通盘考虑整个软件的复杂性。
- 抽象化:将软件模块化后,我们将整个软件抽象化成多层、多 模块的一个集成。这样使整个软件易于理解,便于管理。
- 重用:将软件模块化后,每个模块有其独立的功能和封装。这样这个模块就可以在多处(甚至是将来其他的软件中)重用,从而节省人力。
- 易于维护:将软件模块化后,当软件出现问题后,我们可以容易地定位问题出在那个模块,而每个模块又相对较小和易于理解,从而降低了软件维护的难度。
基于上述的 4 个优点,在当前的软件设计中,软件模块化是软件架构师的主流思想。为了实现软件模块化,应运而生的就是面向对象的高级编程语言,Java 是其中的典型代表。Java 用其独有的 Jar 格式文件去包装 Java 类和其他的资源文件,从而可以将软件组件封装成独立的 Jar 文件。这些 Jar 文件可以相互依赖并共同完成同一个工作,从而实现了软件的模块化。但是 Java 却不能真正的带给我们软件模块化的那 4 个优点,为什么呢?为了解释这个问题,我们需要知道模块的定义。
什么是一个模块?
一个模块应该有以下 3 个特性:
- 自包含:一个模块应该是一个业务逻辑的整体。它应该可以作为一个独立的整体被移动、安装和卸载。模块不是一个原子体,它可以包含多个更小的部分,但这些部分不能独立存在。
- 高内聚:一个模块不应该做很多不相关的事情,它应该专注于 1 个业务逻辑的目标并尽全力实现这个目标。
- 低耦合:一个模块不应该关注其他模块的内部实现,松散的联系允许我们去更改某个特定的模块,而不会影响到其他的模块。
而 Java 语言的 Jar 文件并不能完美的实现一个模块这 3 个特性,它主要会遇到以下的 3 个问题:
- 针对一个 Jar 文件,没有对应的 Java 运行时的概念。Jar 文件只有在开发和部署的时候有意义,而在 JVM 中,所有的 Jar 文件中的内容被简单地联系在一起作为一个单独的全局的列表,这就是所谓的“Classpath”。这种类加载模式,使 Jar 文件在运行时是不可见的。
- Jar 文件没有标准的元数据信息去指明该 Jar 文件所需要的外部依赖文件列表,这样我们就不能清楚的知道,该 Jar 文件需要和其他的那些 Jar 文件一起工作。另外,现在的 Jar 文件没有版本信息,这样,同一 Jar 文件的多个版本就不能同时被加载。
- Java 没有机制在不同的 Jar 文件中隐藏信息。
这 3 个问题,使 Jar 文件在模块的“自包含”和“低耦合”这两个特性上做的不好,从而使 Java 在模块的“拆分人力”和“易于维护”这两个优点上没有好的表现,而更严重的是第 2 个问题,这使 Java 应用软件存在难以处理的版本冲突问题。
Java 语言为了安全的考虑,它的类加载器(注意不是类加载)是多层立体的并且对其类加载采用了父委托机制。并且,当同一个类加载器加载类文件时,JVM 会加载最先发现类。所以当同一个类的新旧两个版本被分别封装在两个不同的 jar 文件中(比如:ClassVersion2.jar 和 ClassVersion1.jar),而这两个 jar 文件又都在 JVM 的类加载路径里的话,最先被加载的 Jar 文件中的类才会被使用到。而现在的 Java 应用软件因为长时间的更新维护,同一个模块的多个版本共存的情况比比皆是,这就会产生版本冲突问题。比如说,在 Java 应用软件中存在一个模块 ThirdComponent,这个模块依赖 ClassVersion 模块而且必须同老版本的 ClassVersion 模块(ClassVersion1.jar)一起工作,当新 ClassVersion 模块(ClassVersion2.jar)被升级到软件中后,ThirdComponent 模块很有可能就不会工作,因为类加载器很有可能会先加载的是 ClassVersion2.jar,而不是 ClassVersion1.jar。其实产生这个问题的根本原因是,Java 的 Jar 文件在自包含上做的不好。Jar 文件只是在软件的封装上起到了作用,而在 JVM 运行时中,Java 只关心类而忽略了 Jar,从而使 Java 的类加载变成了平面的线性的而非立体的网状的。
为了解决 Java 在模块化中存在的问题,OSGi 模块系统出现了。OSGi 是基于 Java 之上开发的,它提供了一种建立模块化的 Java 应用程序的方法并定义了这些模块在运行时中如何相互交互。
OSGi 是一个由大概 40 个公司组成的联盟来共同定义的一个标准。依照这个标准,目前,有 4 种独立实现了的 OSGi 框架,他们分别是:
- Equinox: 这个 OSGi 框架是目前应用最广泛的 OSGi 框架。它是由 IBM 开发的,目前已经被应用到 Eclipse,Lotus Notes,IBM WebSphere Application Server 等等。Equinox 实现了版本为 4.1 的 OSGi 规范。
- Felix:这个 OSGi 框架实现了版本 4.x 的 OSGi 规范,它是由 Apache 开发和维护的。
- Knopflerfish:这是一个流行并成熟的实现了版本 3 和 4.1 的 OSGi 规范的 OSGi 框架。它是由 Makewave AB 开发和维护的。
- Concierge:这个 OSGi 框架实现了版本 3 的 OSGi 规范。
关于 OSGi 这个名字,2 个最常被问到的问题是 OSGi 代表什么?为什么 i 是小写的?
关于这两个问题,权威性的回答是:官方上 OSGi 不代表任何东西,然而,通常上说 OSGi 代表“Open Service Gateway initiative. 而小写字母“i”来自单词“initiative”。
至于 OSGi 的中心思想,它非常地简单。传统 Java 软件问题的根源就是全局的扁平的类加载路径(Classpath),所以 OSGi 采用了一种完全不同的类加载机制,那就是每个模块都有其独立的类加载路径。这几乎解决了传统 Java 在模块化中遇到的所有问题,然而一个新的问题又产生了,软件中的模块是要在一起工作的,这就意味着不同的模块之间存在类共享(不然的话,一个模块如何能够调用到另外一个模块呢),如果每个模块有一个类加载路径,模块间的类共享如何解决?为了解决这个问题,OSGi 定义了一个特殊的并完善的类共享机制。OSGi 将会采用显示的导入和导出机制来控制模块间的类共享。
在 OSGi 中,模块被起了另外一个名字,叫做 bundle。实际上 OSGi 的 bundle 就是一个 Java 的 Jar 文件。OSGi 并没有定义一个新的标准去封装 Java 类和其他的资源文件,标准 Jar 文件可以很好地工作在 Java 应用软件中。在 OSGi 体系里,只是一些新的元数据信息被加入到 Jar 文件中,这些元数据信息使的 Jar 文件变成一个 OSGi 体系中的 bundle。那么什么样的元数据信息被加入进来了呢?
- Bundle 的名字。OSGi 提供了一个“symbolic”名字作为这个 bundle 的唯一标识符。
- Bundle 的版本信息。
- Import 和 export 列表。从这个列表,我们可以清楚地知道这个 OSGi bundle 需要导入和导出那些包的类。导入的包是本 bundle 需要用到的外部资源,导出的包是其他 bundle 可以用的本 bundle 中的资源。
- Bundle 需要运行的最小的 Java 版本。这个信息是可选的。
- 种类繁杂的人类可读的其他信息,比如说:本 bundle 的提供者,版权陈述,联系地址等等。
这些元数据信息被放到 Jar 文件的 MANIFEST.MF 文件中,而这个文件是每个标准 Jar 文件的一部分。用一个标准的 Jar 文件作为 OSGi bundle 的一个好处是 bundle 可以被用在 Jar 文件可以出现的任何一个地方,因为 bundle 就是一个纯粹的 Jar 文件。当一个 bundle 用在 OSGi 的运行时之外的时候,这些额外多出来的元数据信息会被 Java 运行时简单地忽略掉,所以说,bundle 是向前兼容的。那么除了这个,OSGi 的 bundle 还给我们带来了什么样的好处呢?
为每一个 bundle 提供一个类加载路径意味着什么呢?简单地说我们为每一个 bundle 提供了一个类加载器,这个类加载器能够看到这个 bundle 文件里的类和其他资源文件。但是为了达到多个 bundle 共同工作的目的,在 OSGi 的类加载器之间,类加载请求可以从一个 bundle 的类加载器被委托到另外一个 bundle 的类加载器。回想一下在标准 Java 和 J2EE 中,类加载器是一个树形结构的,类加载请求总是被向上委托给每一个类加载器的父亲。这中类加载机制不允许在水平的树节点之间进行类加载委托。为了让一个类库可以被类加载器树的多个树枝共同所见,我们就需要将这个类库推到这些树枝共同的祖先节点。这样这个版本的类库就会被这些树枝上的所有节点所见,而不管是否这些节点都想看到这个版本的类库。图 1 是一个典型的 J2EE 类加载器层次结构,它展示了为什么类库会不断推高的原因。
而树型结构并不是我们需要的,我们真正需要的是网状结构。两个组件之间的依赖关系不是简单的上下级的关系,而应该是一种提供者和使用者的网络关系。类的加载请求被从一个 bundle 的类加载器委托到另外一个 bundle 的类加载器,而这种委托是基于 bundle 之间的这种网状的依赖关系。图 2 给了我们一个 OSGi 中 bundle 之间的网状的依赖关系的例子。
在 OSGi 中,bundle 之间的依赖关系是通过显示的 import 和 export 类包列表来决定的。比如说,在图 2 的 bundle B 中包含一个类包,名字为 com.ibm.bundle.b.somePackage.。Bundle b 就可以选择在它的 MANIFEST.MF 文件去 export 这个类包。而 bundle A 也可以选择去在它的 MANIFEST.MF 文件中 import 类包 com.ibm.bundle.b.somePackage。然后在 OSGi 运行时中,OSGi 框架将负责匹配不同 bundle 的 import 和 export 列表。而这个匹配过程被称为 OSGi 的解决过程(resolution process)。OSGi 的解决过程是相当复杂的,但是这个过程是被 OSGi 框架实现了的,不需要每个 bundle 自己来关心它。每个 bundle 只需要写一些很简单的 import 和 export 声明语句在各自的 MANIFEST.MF 文件里。一旦 OSGi 框架匹配了 bundle A 的 import 列表里的类包 com.ibm.bundle.b.somePackage 与 bundle B 的 export 列表里的类包 com.ibm.bundle.b.somePackage,那么这两个 bundle 就会被连接在一起,而这就意味着当 bundle A 需要载入任何类包 com.ibm.bundle.b.somePackage 里的类时,这个类载入请求就会被委托给 bundle B 的类载入器,而 bundle B 的类载入器将会载入这个类,并将类实例传给 bundle A。而因为 bundle A 依赖 bundle B 和 bundle C, 如果 bundle A import 列表里的所有类包都能在 bundle B 和 bundle C 的 export 列表里发现,那么 bundle A 将会被称为解决成功,它会进入解决了状态(resolved)。如果 bundle A 的 import 列表里需要的某些类包没有在 bundle B 和 bundle C 的 export 列表里发现,那么 bundle A 的解决就没有成功,bundle A 将不能被使用和启动。由此可见,OSGi 中,bundle 之间的依赖关系是通过显示的 import 和 export 列表来决定的。
- 隐藏了 bundle 中的信息。
因为在 OSGi 中,bundle 之间的依赖关系是通过显示的 import 和 export 列表来决定的,所以我们没有必要 export 一个 bundle 中的所有的类包,进而能起到隐藏 bundle 中信息的作用。在 OSGi 中,只有那些被显示的 export 出来的类包才能被其他的 bundle import。 - 增加了版本控制并允许多版本并存。
OSGi 不仅仅使 bundle 之间通过类包名相互依赖,它还可以为类包加入版本信息。这是我们能够应付 bundle 的版本变化问题。Export 的类包也可以携带一个版本信息,而 import 却可以引用一个版本范围内的所有类包,这让我们的 bundle 可以依赖一个版本范围内的所有类包。于是说,在 OSGi 中,同一 bundle 的多个版本就可以同时存在。在本文的第三章,我将详细介绍,OSGi 是如何允许同一个 bundle 的多个版本并行存在,进而客服了 Java 应用软件中组件的版本冲突问题OSGi 之所以能够解决 Java 应用软件中组件的版本冲突问题,原因就是 OSGi 的网状类加载器和 OSGi bundle 的版本信息控制。为什么这么说呢?
- OSGi 的网状类加载器架构使每个 OSGi bundle 都拥有一个独立的类加载器,而 bundle 只是一个标准 Jar 文件。这样,对同一个 bundle 的不同版本,我们就可以创建多个不同的 Jar 文件。这些 Jar 文件的实际内容可以完全一样,而只是文件名不同(甚至文件名都可以相同,因为在 OSGi 框架中,bundle 名和版本的组合才是唯一标识符)。因此这些 Jar 文件,在 OSGi 框架看来,是不同的 bundle,于是同一个组件的不同版本可以被同时载入到 JVM 中,这就解决了 Java 应用软件中同一组件不同版本的并存问题,接下来只要解决版本辨识问题,那么 Java 应用软件中组件的版本冲突问题就会被客服掉了。
- OSGi 添加了版本控制信息来区分同一个 bundle 的不同版本,而且在 OSGi 框架中 bundle 名和 bundle 版本的组合才是这个 bundle 的唯一标识符,于是通过 bundle 的版本控制,同一个 bundle 的不同版本就可以得到区分,而 bundle 直接的依赖关系也可以通过版本来加以限制,从而就能完美的解决 Java 应用软件中组件的版本冲突问题了。
OSGi 通过在 MANIFEST.MF 文件中添加 Bundle-Version 属性来个为每一个 bundle 添加一个版本信息,而且这个版本信息必须严格遵循:3 数字段 +1 字符段的格式,6.2.0.beta_3 是一个典型有效的 bundle 版本信息。这前面的 3 个数字段就是大家都知道的主版本,小版本和微版本,而那个最后的字母段则是校正段。当前面的 3 个任意一个数字段没有值时,OSGi 将会隐式地将 0 付给这个字段,所以版本 1 是和 1.0、1.0.0 相同的。而如果没给 bundle 指定任意一个版本,那么 0.0.0 将被认为是这个 bundle 的版本信息。
另外 OSGi 中,版本的比较是采用从前到后的比较方式。如果在版本比较时,第一个数字段就不同,那么后面的 3 个字段就不用比较了,因为 OSGi 的前一个版本段是后面所有字段值的总和,所以大版本就不相同的时候,后面的小版本就不需要比较了,比如说:2.0.0 是大于 1.999.999。而如果两个 bundle 的版本信息,在前面的 3 个数字段都相同的时候,OSGi 就会对最后的字母段进行比较。而最后的字母段可以包含大写或小写的字母 A 到 Z、数字、连接线和下划线,所以它的比较比较复杂。OSGi 采用了标准 Java String 类的 compareTo() 方法的算法来进行比较,而标准 Java 的 String 类的 compareTo() 方法会对校正段的每一个字母按顺序进行比较,直到出现差异。另外如果字母相同,那么短的那个校正段的值将被认为小于长的校正段,beta_01 将会比 beta_010 小。
最后需要提的是 OSGi 不但可以为 bundle 指定一个版本信息,还可以为每一个类包指定一个版本信息,即 bundle 的版本控制是可以做到类包级别的(而且这是推荐的 OSGi 版本控制方式)。当 bundle 在 export 类包时,用户可以为每个类包指定一个版本信息。而当 bundle 需要 import 某特定版本的类包时,用户除了可以指定一个特定的版本信息外,还可以指定一个版本信息范围。而这个范围可以用方括号“【”和圆括号“(”来作为边界,方括号“【”表示边界值也在范围之内,而圆括号“(”则相反 . 比如说【 1.0.0,2.0.0)表示从版本 1.0.0 开始到 2.0.0 之间的所有的小版本,2.0.0 不在这个范围只内。下面是一些进一步的范围列表的例子,在下面的表 1 中 x 代表有效的范围列表:
表 1. 版本范围举例
样例 版本范围 [1.2.3,4.5.6) 1.2.3<=x<4.5.6 [1.2.3,4.5.6] 1.2.3<=x<=4.5.6 (1.2.3,4.5.6) 1.2.3 (1.2.3,4.5.6] 1.2.3 [1.2.3,1.2.3] 1.2.3 1.2.3 1.2.3<=x 0.0.0<=x OSGi 是如此优秀的一个框架,因此很多 Java 应用软件开始采用它,WebSphere Application Server(WAS)作为 IBM 最中要的 J2EE 服务器从版本 6.1 开始采用 OSGi 框架,因此当你的应用是基于 WAS 的并遇到了版本冲突问题,你就可以将你的应用转换成 OSGi 的 bundle,从而解决版本冲突问题。在将你的 WAS 的应用转变成 OSGi 的 bundle 之前,我们需要了解 WAS 是如何支持 OSGi 的,进而采取相应的行动。
图 3. WAS 从 6.1 以后的类加载器层次结构图 3 是 WAS 从版本 6.1 以后的类加载器层次结构,从此图,我们可以知道 WAS 并不是完全采用 OSGi 框架(可能是出于向前兼容等因素考虑),IBM 只是将 WAS 的一部分变成了 OSGi 框架的类加载模式,而其他的部分继续延续了以前版本的类加载器层 次结构。而 WAS 中 OSGi 的那部分类加载器,以网关的形式与 WAS 的扩展类加载器连接在一起。
接下来,大家可能奇怪,WAS 原有的类加载器是如何同 WAS 中的 OSGi 部分的类加载器一起工作的呢? WAS 将如何进行类加载呢?
为了解释这个问题,我们首先要清楚,WAS 中的那一部分采用了 OSGi 的框架? WAS 将其常用的重要的插件部分做成了 OSGi,而所有的这些插件被放置在 WAS 的 plugins 目录中,因此 plugins 目录下的所有的插件是以 OSGi bundle 的形式被载入到 WAS 中来的。接下来,我将解释从版本 6.1 以后 WAS 将如何进行类加载。从图 3,我们可以得知,WAS 的 OSGi 框架是与 Ext 类加载器连接在一起的,这就是说 WAS 的 OSGi 框架在 WAS 整体的类加载器中处于一个很高的层次。而 Java 的类加载是采用父委托机制的,这就使普通的 WAS 上的应用程序会一层层的向上请求加载类,这样当某个特定的类在 WAS 的 OSGi 部分中被发现,那么 WAS 的类加载委托就会进入到 WAS 的 OSGi 运行时中。这样当这个类加载以后,而它又需要加载其他类时,这之后的类加载委托请求就会在 WAS 的 OSGi 运行时中相互传递。
由此可见,如果我们需要将自己的 WAS 上的应用程序转变成支持 OSGi,我们则需要将这个应用程序的模块转变成 OSGi bundle,然后将其放置在 WAS 的 plugins 目录下。
介绍一个具有版本冲突问题的 WAS 上的样例程序
在本章,我会模拟一个 WAS 上版本冲突的问题,让大家能清楚地看到这种版本冲突问题是如何出现的。然后在第六章,我会告诉大家如何将这个样例程序的某些组件转变成 bundle,从而解决了版本冲突问题。
为了说明问题,我构建如下一个邮件系统,这个系统可以阅读各种各样类型的邮件。因为邮件的类型可以存在很多种,而且每一种的邮件可以使用独特的浏览方式,为了设计和维护的简单,我们将它拆分成多个模块。我们将各种邮件类型共用的接口和基础类拆分成一个模块,而将每种不同的邮件类型拆分成各自独立的模块。而为了便于描述将来会出现的版本冲突问题,我们只是用两种简单的硬编码的邮件类型(FixedMailbox 和 XMLMailbox)来说明这个系统的整体结构,请看图 4。
图 4. 邮件系统结构图这个结构图能够清晰得告诉我们,整个系统的通用接口和基础类被封装在 MailboxAPI 模块中,而每个具体的邮件类型将分别是一个模块(FixedMailbox 模块和 XMLMailbox 模块),这些模块将实现这些通用接口和每种邮件类型各自的业务逻辑,然后将邮件展示给用户浏览。所以在这个图中将最少有 3 个模块,他们会由不同的团队来分别负责。当整个系统开发完毕之后,各个团队需要将各自的组件发布到运行环境当中。因为 MailboxAPI 模块(MailboxAPI.jar)是被所有的其他组件所依赖的,为了将来维护和发布的方便,一般 MailboxAPI.jar 会被放置到 WAS 的 lib 目录下,这样,其他的每一个组件都能轻松的找到它,而 MailboxAPI 模块团队也可以轻松地发布和维护 MailboxAPI,因为发布位置的唯一,MailboxAPI 模块的每一次发布和维护将不需要通知到所有其他的团队。而其他的模块,因为包含业务逻辑和展示层,他们一般都是一个 ear 文件,所以他们的发布位置明显是与 MailboxAPI.jar 不同的。图 5 和图 6 展示给我们 FixedMailbox 和 XMLMailbox 正常运行后的样子。
图 5. FixedMailbox 的运行情况
图 6. XMLMailbox 的运行情况为了方便大家的学习,我将已经开发好的这 3 个模块以附件的形式附在本文上,它们在压缩文件 Version1.zip 里。你只需要解压缩它,然后将 MailboxAPI.jar 放置在 WAS 的 lib 目录下,然后启动你的 WAS 系统,接着你需要安装 FixedMailboxReaderEAR.ear 和 XMLMailboxReaderEAR.ear(他们分别代表 FixedMailbox 模块和 XMLMailbox 模块)。在 Version1.zip 你还会发现 FixedMailbox.jar 和 XMLMailbox.jar,它们分别是模块 FixedMailbox 和 XMLMailbox 的业务逻辑,而 FixedMailboxReaderEAR.ear 和 XMLMailboxReaderEAR.ear 是这两个模块的最后封装。
注意:附件里的每一个文件里,都含有源码,以方便感兴趣的读者深入研究。
在整个邮件系统运行一段时间后,某些用户较多的个别邮件模块就可能面临着升级。因为客户多,需求就多,而用户的需求又是千奇百怪的,必然会出现某个特定的需求波及到了基础模块 MailboxAPI。这个时候,就可能会出现如下的情况:模块 MailboxAPI 和 XMLMailbox 被要求升级,而模块 FixedMailbox 为了稳定,要求不产生任何变化。然而当 XMLMailbox 的需求很大时,可能使 MailboxAPI 也产生了很大的变化,从而影响了 FixedMailbox,使之不能正常工作,于是版本冲突问题产生了。
为了演示这个问题,我改变了模块 MailboxAPI 和 XMLMailbox 的代码,产生了版本 2.0 的 MailboxAPI 和 XMLMailbox 模块,他们被放置在附件 2(Version2.zip)中。为了看到真实的版本冲突问题,请解压缩 Version2.zip,然后停止 WAS 并删除 WAS 的 lib 目录下的 MailboxAPI.jar,接着将 MailboxAPI2.jar 放置在 WAS 的 lib 目录下,然后重启你的 WAS 系统,接着你需要用 XMLMailboxReaderEAR2.ear 去升级已经存在的 XMLMailboxReader 应用,然后分别访问两个模块的 Servlet,你就会看到 FixedMailbox 模块将不能正常工作,而 XMLMailbox 则一切正常,如图 7 或图 8 所示。
图 7. FixedMailbox 不能正常运行
图 8. XMLMailbox 可以运行注意:如果你不删除 MailboxAPI.jar,而是让 MailboxAPI.jar 和 MailboxAPI2.jar 并存在 WAS 的 lib 目录下,那么就有可能是 FixedMailbox 能正常工作,而 XMLMailbox 不能正常工作,如图 9 和图 10 所示,原因是 MailboxAPI.jar 可能在 MailboxAPI2.jar 之前被载入到类加载器中了。总之,FixedMailbox 和 XMLMailbox 将不能同时工作,这就是版本冲突问题。
图 9. 当 MailboxAPI.jar 和 MailboxAPI2.jar 并存时,FixedMailbox 可以正常运行
图 10. 当 MailboxAPI.jar 和 MailboxAPI2.jar 并存时,XMLMailbox 不能运行讲述如何将这个样例程序中的某些组件转变成 WAS 上的 OSGi bundle,从而解决了版本冲突问题
为了解决在第五章中的版本冲突问题,我们只需要将模块 FixedMailbox 和 XMLMailbox 的业务逻辑部分(FixedMailbox.jar 和 XMLMailbox.jar)转变成 OSGi 的 bundle,然后将 MailboxAPI 的版本 1 和版本 2(MailboxAPI.jar 和 MailboxAPI2.jar)也都转变成 bundle,并使 FixedMailbox.jar 依赖于 MailboxAPI.jar,使 XMLMailbox.jar 依赖于 MailboxAPI2.jar。接下来,将 WAS 的 lib 目录下的 MailboxAPI 模块移除掉,然后将 FixedMailbox.jar、XMLMailbox.jar、MailboxAPI.jar 和 MailboxAPI2.jar 同时放入 WAS 的 plugins 目录下,最后重新启动 WAS,你会发现模块 FixedMailbox 和 XMLMailbox 能同时工作,如图 11 和图 12 所示。
图 11. 在将 FixedMailbox.jar 转变为 bundle 后,FixedMailbox 可以正常运行
图 12. 在将 XMLMailbox.jar 转变为 bundle 后,XMLMailbox 可以正常运行在将整个邮件系统转变成支持 OSGi bundle 后,会使邮件系统的各个模块之间的依赖关系从图 4 编程图 13 的情况,因为在 OSGi 中,同一模块的多个版本可以并存,并且模块之间的依赖关系可以通过版本进行限制,从而是 Java 应用软件中的版本冲突问题得到了完美的解决。
图 13. 最后的邮件系统的模块之间的依赖关系将已有的 Jar 文件转换成 bundle 非常简单,我们不需要改变任何 Java 代码,只需要将必要的 OSGi 的 bundle 信息加入 Jar 文件的 MANIFEST.MF。为了方便大家的查看,我将已经转好的 bundle 文件 FixedMailbox.jar、XMLMailbox.jar、MailboxAPI.jar 和 MailboxAPI2.jar 做成了附件 3。
OSGi 框架是实现 Java 应用软件模块化的重要手段。当前,OSGi 已经变的非常流行,很多的著名的 Java 应用在底层已经开始采用 OSGi 框架,比如说 Spring,IBM 的 Eclipse 和 WebSphere Application Server。所以将你自己的 Java 应用转变成支持 OSGi 是必要的趋势,而本文将给你一个范例,告诉你如何在 WebSphere Application Server 中将你的应用转变成支持 OSGi,从而就可以避免版本冲突问题的发生。
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/14789789/viewspace-623410/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/14789789/viewspace-623410/