新聞中心
本文是IBMDW的王志強(qiáng)撰寫(xiě)的文章,原文名稱(chēng)為《實(shí)現(xiàn) WebSphere Application Server 上應(yīng)用程序?qū)?OSGi 的支持》。

目前創(chuàng)新互聯(lián)已為近千家的企業(yè)提供了網(wǎng)站建設(shè)、域名、虛擬空間、網(wǎng)站運(yùn)營(yíng)、企業(yè)網(wǎng)站設(shè)計(jì)、宜陽(yáng)網(wǎng)站維護(hù)等服務(wù),公司將堅(jiān)持客戶(hù)導(dǎo)向、應(yīng)用為本的策略,正道將秉承"和諧、參與、激情"的文化,與客戶(hù)和合作伙伴齊心協(xié)力一起成長(zhǎng),共同發(fā)展。
編輯推薦:OSGi入門(mén)與實(shí)踐全攻略
為了解決現(xiàn)實(shí)工程中遇到的版本沖突問(wèn)題,我研究了 OSGi 技術(shù)。為此,我在網(wǎng)上搜索了很多的有關(guān)于 OSGi 的文章,但是最后發(fā)現(xiàn)很少有人能清楚的闡述 OSGi 的由來(lái)和 OSGi 最本質(zhì)的特性,直到我發(fā)現(xiàn) Neil Bartlett 的 OSGi In Practice。它讓理解了 OSGi 的本質(zhì),從而清楚的明白:解決 Java 工程中的版本沖突問(wèn)題,OSGi 是最好的選擇。這是由 OSGi 的本質(zhì)決定的,這也是為什么大的 Java 軟件產(chǎn)品紛紛開(kāi)始采用 OSGi 框架的原因。Java 有天生的缺陷,而 OSGi 彌補(bǔ)了它。然后我又花了大量的時(shí)間來(lái)研究 WebSphere Application Server 是如何支持 OSGi 的,最后才能成功的使我的應(yīng)用支持 OSGi,從而解決了版本沖突問(wèn)題。但這一整個(gè)過(guò)程耗費(fèi)了我大量的時(shí)間和精力,為了能讓遇到同樣問(wèn)題的人少走彎路,我寫(xiě)了這篇文章,希望能夠有所幫助。
OSGi 的由來(lái)
隨著科技和需求的發(fā)展和變化,現(xiàn)在的軟件變得越來(lái)越龐大。這樣,隨之而來(lái)的最大挑戰(zhàn)就是軟件在設(shè)計(jì)上的越來(lái)越復(fù)雜和維護(hù)上的越來(lái)越困難。為了解決這個(gè)問(wèn)題,軟件架構(gòu)師將軟件切分成比較小的并且易于理解的多個(gè)模塊。那么軟件模塊化會(huì)給我們帶來(lái)什么樣的好處呢?
- 拆分人力:將軟件模塊化后,我們就可以分配獨(dú)立的團(tuán)隊(duì)去處理獨(dú)立的模塊,從而將人力拆分開(kāi)來(lái)。這樣既便于管理,又會(huì)降低整個(gè)軟件的設(shè)計(jì)的復(fù)雜性。因?yàn)槊總€(gè)獨(dú)立的團(tuán)隊(duì)可以專(zhuān)心去設(shè)計(jì)和實(shí)現(xiàn)其模塊,而不用通盤(pán)考慮整個(gè)軟件的復(fù)雜性。
- 抽象化:將軟件模塊化后,我們將整個(gè)軟件抽象化成多層、多 模塊的一個(gè)集成。這樣使整個(gè)軟件易于理解,便于管理。
- 重用:將軟件模塊化后,每個(gè)模塊有其獨(dú)立的功能和封裝。這樣這個(gè)模塊就可以在多處(甚至是將來(lái)其他的軟件中)重用,從而節(jié)省人力。
- 易于維護(hù):將軟件模塊化后,當(dāng)軟件出現(xiàn)問(wèn)題后,我們可以容易地定位問(wèn)題出在那個(gè)模塊,而每個(gè)模塊又相對(duì)較小和易于理解,從而降低了軟件維護(hù)的難度。
基于上述的 4 個(gè)優(yōu)點(diǎn),在當(dāng)前的軟件設(shè)計(jì)中,軟件模塊化是軟件架構(gòu)師的主流思想。為了實(shí)現(xiàn)軟件模塊化,應(yīng)運(yùn)而生的就是面向?qū)ο蟮母呒?jí)編程語(yǔ)言,Java 是其中的典型代表。Java 用其獨(dú)有的 Jar 格式文件去包裝 Java 類(lèi)和其他的資源文件,從而可以將軟件組件封裝成獨(dú)立的 Jar 文件。這些 Jar 文件可以相互依賴(lài)并共同完成同一個(gè)工作,從而實(shí)現(xiàn)了軟件的模塊化。但是 Java 卻不能真正的帶給我們軟件模塊化的那 4 個(gè)優(yōu)點(diǎn),為什么呢?為了解釋這個(gè)問(wèn)題,我們需要知道模塊的定義。
什么是一個(gè)模塊?
一個(gè)模塊應(yīng)該有以下 3 個(gè)特性:
- 自包含:一個(gè)模塊應(yīng)該是一個(gè)業(yè)務(wù)邏輯的整體。它應(yīng)該可以作為一個(gè)獨(dú)立的整體被移動(dòng)、安裝和卸載。模塊不是一個(gè)原子體,它可以包含多個(gè)更小的部分,但這些部分不能獨(dú)立存在。
- 高內(nèi)聚:一個(gè)模塊不應(yīng)該做很多不相關(guān)的事情,它應(yīng)該專(zhuān)注于 1 個(gè)業(yè)務(wù)邏輯的目標(biāo)并盡全力實(shí)現(xiàn)這個(gè)目標(biāo)。
- 低耦合:一個(gè)模塊不應(yīng)該關(guān)注其他模塊的內(nèi)部實(shí)現(xiàn),松散的聯(lián)系允許我們?nèi)ジ哪硞€(gè)特定的模塊,而不會(huì)影響到其他的模塊。
而 Java 語(yǔ)言的 Jar 文件并不能完美的實(shí)現(xiàn)一個(gè)模塊這 3 個(gè)特性,它主要會(huì)遇到以下的 3 個(gè)問(wèn)題:
- 針對(duì)一個(gè) Jar 文件,沒(méi)有對(duì)應(yīng)的 Java 運(yùn)行時(shí)的概念。Jar 文件只有在開(kāi)發(fā)和部署的時(shí)候有意義,而在 JVM 中,所有的 Jar 文件中的內(nèi)容被簡(jiǎn)單地聯(lián)系在一起作為一個(gè)單獨(dú)的全局的列表,這就是所謂的“Classpath”。這種類(lèi)加載模式,使 Jar 文件在運(yùn)行時(shí)是不可見(jiàn)的。
- Jar 文件沒(méi)有標(biāo)準(zhǔn)的元數(shù)據(jù)信息去指明該 Jar 文件所需要的外部依賴(lài)文件列表,這樣我們就不能清楚的知道,該 Jar 文件需要和其他的那些 Jar 文件一起工作。另外,現(xiàn)在的 Jar 文件沒(méi)有版本信息,這樣,同一 Jar 文件的多個(gè)版本就不能同時(shí)被加載。
- Java 沒(méi)有機(jī)制在不同的 Jar 文件中隱藏信息。
這 3 個(gè)問(wèn)題,使 Jar 文件在模塊的“自包含”和“低耦合”這兩個(gè)特性上做的不好,從而使 Java 在模塊的“拆分人力”和“易于維護(hù)”這兩個(gè)優(yōu)點(diǎn)上沒(méi)有好的表現(xiàn),而更嚴(yán)重的是第 2 個(gè)問(wèn)題,這使 Java 應(yīng)用軟件存在難以處理的版本沖突問(wèn)題。
Java 語(yǔ)言為了安全的考慮,它的類(lèi)加載器(注意不是類(lèi)加載)是多層立體的并且對(duì)其類(lèi)加載采用了父委托機(jī)制。并且,當(dāng)同一個(gè)類(lèi)加載器加載類(lèi)文件時(shí),JVM 會(huì)加載最先發(fā)現(xiàn)類(lèi)。所以當(dāng)同一個(gè)類(lèi)的新舊兩個(gè)版本被分別封裝在兩個(gè)不同的 jar 文件中(比如:ClassVersion2.jar 和 ClassVersion1.jar),而這兩個(gè) jar 文件又都在 JVM 的類(lèi)加載路徑里的話,最先被加載的 Jar 文件中的類(lèi)才會(huì)被使用到。而現(xiàn)在的 Java 應(yīng)用軟件因?yàn)殚L(zhǎng)時(shí)間的更新維護(hù),同一個(gè)模塊的多個(gè)版本共存的情況比比皆是,這就會(huì)產(chǎn)生版本沖突問(wèn)題。比如說(shuō),在 Java 應(yīng)用軟件中存在一個(gè)模塊 ThirdComponent,這個(gè)模塊依賴(lài) ClassVersion 模塊而且必須同老版本的 ClassVersion 模塊(ClassVersion1.jar)一起工作,當(dāng)新 ClassVersion 模塊(ClassVersion2.jar)被升級(jí)到軟件中后,ThirdComponent 模塊很有可能就不會(huì)工作,因?yàn)轭?lèi)加載器很有可能會(huì)先加載的是 ClassVersion2.jar,而不是 ClassVersion1.jar。其實(shí)產(chǎn)生這個(gè)問(wèn)題的根本原因是,Java 的 Jar 文件在自包含上做的不好。Jar 文件只是在軟件的封裝上起到了作用,而在 JVM 運(yùn)行時(shí)中,Java 只關(guān)心類(lèi)而忽略了 Jar,從而使 Java 的類(lèi)加載變成了平面的線性的而非立體的網(wǎng)狀的。
為了解決 Java 在模塊化中存在的問(wèn)題,OSGi 模塊系統(tǒng)出現(xiàn)了。OSGi 是基于 Java 之上開(kāi)發(fā)的,它提供了一種建立模塊化的 Java 應(yīng)用程序的方法并定義了這些模塊在運(yùn)行時(shí)中如何相互交互。
OSGi 最本質(zhì)的特性
OSGi 是一個(gè)由大概 40 個(gè)公司組成的聯(lián)盟來(lái)共同定義的一個(gè)標(biāo)準(zhǔn)。依照這個(gè)標(biāo)準(zhǔn),目前,有 4 種獨(dú)立實(shí)現(xiàn)了的 OSGi 框架,他們分別是:
- Equinox: 這個(gè) OSGi 框架是目前應(yīng)用最廣泛的 OSGi 框架。它是由 IBM 開(kāi)發(fā)的,目前已經(jīng)被應(yīng)用到 Eclipse,Lotus Notes,IBM WebSphere Application Server 等等。Equinox 實(shí)現(xiàn)了版本為 4.1 的 OSGi 規(guī)范。
- Felix:這個(gè) OSGi 框架實(shí)現(xiàn)了版本 4.x 的 OSGi 規(guī)范,它是由 Apache 開(kāi)發(fā)和維護(hù)的。
- Knopflerfish:這是一個(gè)流行并成熟的實(shí)現(xiàn)了版本 3 和 4.1 的 OSGi 規(guī)范的 OSGi 框架。它是由 Makewave AB 開(kāi)發(fā)和維護(hù)的。
- Concierge:這個(gè) OSGi 框架實(shí)現(xiàn)了版本 3 的 OSGi 規(guī)范。
關(guān)于 OSGi 這個(gè)名字,2 個(gè)最常被問(wèn)到的問(wèn)題是 OSGi 代表什么?為什么 i 是小寫(xiě)的?
關(guān)于這兩個(gè)問(wèn)題,權(quán)威性的回答是:官方上 OSGi 不代表任何東西,然而,通常上說(shuō) OSGi 代表“Open Service Gateway initiative. 而小寫(xiě)字母“i”來(lái)自單詞“initiative”。
至于 OSGi 的中心思想,它非常地簡(jiǎn)單。傳統(tǒng) Java 軟件問(wèn)題的根源就是全局的扁平的類(lèi)加載路徑(Classpath),所以 OSGi 采用了一種完全不同的類(lèi)加載機(jī)制,那就是每個(gè)模塊都有其獨(dú)立的類(lèi)加載路徑。這幾乎解決了傳統(tǒng) Java 在模塊化中遇到的所有問(wèn)題,然而一個(gè)新的問(wèn)題又產(chǎn)生了,軟件中的模塊是要在一起工作的,這就意味著不同的模塊之間存在類(lèi)共享(不然的話,一個(gè)模塊如何能夠調(diào)用到另外一個(gè)模塊呢),如果每個(gè)模塊有一個(gè)類(lèi)加載路徑,模塊間的類(lèi)共享如何解決?為了解決這個(gè)問(wèn)題,OSGi 定義了一個(gè)特殊的并完善的類(lèi)共享機(jī)制。OSGi 將會(huì)采用顯示的導(dǎo)入和導(dǎo)出機(jī)制來(lái)控制模塊間的類(lèi)共享。
在 OSGi 中,模塊被起了另外一個(gè)名字,叫做 bundle。實(shí)際上 OSGi 的 bundle 就是一個(gè) Java 的 Jar 文件。OSGi 并沒(méi)有定義一個(gè)新的標(biāo)準(zhǔn)去封裝 Java 類(lèi)和其他的資源文件,標(biāo)準(zhǔn) Jar 文件可以很好地工作在 Java 應(yīng)用軟件中。在 OSGi 體系里,只是一些新的元數(shù)據(jù)信息被加入到 Jar 文件中,這些元數(shù)據(jù)信息使的 Jar 文件變成一個(gè) OSGi 體系中的 bundle。那么什么樣的元數(shù)據(jù)信息被加入進(jìn)來(lái)了呢?
- Bundle 的名字。OSGi 提供了一個(gè)“symbolic”名字作為這個(gè) bundle 的唯一標(biāo)識(shí)符。
- Bundle 的版本信息。
- Import 和 export 列表。從這個(gè)列表,我們可以清楚地知道這個(gè) OSGi bundle 需要導(dǎo)入和導(dǎo)出那些包的類(lèi)。導(dǎo)入的包是本 bundle 需要用到的外部資源,導(dǎo)出的包是其他 bundle 可以用的本 bundle 中的資源。
- Bundle 需要運(yùn)行的最小的 Java 版本。這個(gè)信息是可選的。
- 種類(lèi)繁雜的人類(lèi)可讀的其他信息,比如說(shuō):本 bundle 的提供者,版權(quán)陳述,聯(lián)系地址等等。
這些元數(shù)據(jù)信息被放到 Jar 文件的 MANIFEST.MF 文件中,而這個(gè)文件是每個(gè)標(biāo)準(zhǔn) Jar 文件的一部分。用一個(gè)標(biāo)準(zhǔn)的 Jar 文件作為 OSGi bundle 的一個(gè)好處是 bundle 可以被用在 Jar 文件可以出現(xiàn)的任何一個(gè)地方,因?yàn)?bundle 就是一個(gè)純粹的 Jar 文件。當(dāng)一個(gè) bundle 用在 OSGi 的運(yùn)行時(shí)之外的時(shí)候,這些額外多出來(lái)的元數(shù)據(jù)信息會(huì)被 Java 運(yùn)行時(shí)簡(jiǎn)單地忽略掉,所以說(shuō),bundle 是向前兼容的。那么除了這個(gè),OSGi 的 bundle 還給我們帶來(lái)了什么樣的好處呢?
為每一個(gè) bundle 提供一個(gè)類(lèi)加載路徑意味著什么呢?簡(jiǎn)單地說(shuō)我們?yōu)槊恳粋€(gè) bundle 提供了一個(gè)類(lèi)加載器,這個(gè)類(lèi)加載器能夠看到這個(gè) bundle 文件里的類(lèi)和其他資源文件。但是為了達(dá)到多個(gè) bundle 共同工作的目的,在 OSGi 的類(lèi)加載器之間,類(lèi)加載請(qǐng)求可以從一個(gè) bundle 的類(lèi)加載器被委托到另外一個(gè) bundle 的類(lèi)加載器?;叵胍幌略跇?biāo)準(zhǔn) Java 和 J2EE 中,類(lèi)加載器是一個(gè)樹(shù)形結(jié)構(gòu)的,類(lèi)加載請(qǐng)求總是被向上委托給每一個(gè)類(lèi)加載器的父親。這中類(lèi)加載機(jī)制不允許在水平的樹(shù)節(jié)點(diǎn)之間進(jìn)行類(lèi)加載委托。為了讓一個(gè)類(lèi)庫(kù)可以被類(lèi)加載器樹(shù)的多個(gè)樹(shù)枝共同所見(jiàn),我們就需要將這個(gè)類(lèi)庫(kù)推到這些樹(shù)枝共同的祖先節(jié)點(diǎn)。這樣這個(gè)版本的類(lèi)庫(kù)就會(huì)被這些樹(shù)枝上的所有節(jié)點(diǎn)所見(jiàn),而不管是否這些節(jié)點(diǎn)都想看到這個(gè)版本的類(lèi)庫(kù)。圖 1 是一個(gè)典型的 J2EE 類(lèi)加載器層次結(jié)構(gòu),它展示了為什么類(lèi)庫(kù)會(huì)不斷推高的原因。
圖 1. 典型的 J2EE 類(lèi)加載器層次結(jié)構(gòu)
而樹(shù)型結(jié)構(gòu)并不是我們需要的,我們真正需要的是網(wǎng)狀結(jié)構(gòu)。兩個(gè)組件之間的依賴(lài)關(guān)系不是簡(jiǎn)單的上下級(jí)的關(guān)系,而應(yīng)該是一種提供者和使用者的網(wǎng)絡(luò)關(guān)系。類(lèi)的加載請(qǐng)求被從一個(gè) bundle 的類(lèi)加載器委托到另外一個(gè) bundle 的類(lèi)加載器,而這種委托是基于 bundle 之間的這種網(wǎng)狀的依賴(lài)關(guān)系。圖 2 給了我們一個(gè) OSGi 中 bundle 之間的網(wǎng)狀的依賴(lài)關(guān)系的例子。
圖 2. OSGi 中 bundle 之間的網(wǎng)狀的依賴(lài)關(guān)系
在 OSGi 中,bundle 之間的依賴(lài)關(guān)系是通過(guò)顯示的 import 和 export 類(lèi)包列表來(lái)決定的。比如說(shuō),在圖 2 的 bundle B 中包含一個(gè)類(lèi)包,名字為 com.ibm.bundle.b.somePackage.。Bundle b 就可以選擇在它的 MANIFEST.MF 文件去 export 這個(gè)類(lèi)包。而 bundle A 也可以選擇去在它的 MANIFEST.MF 文件中 import 類(lèi)包 com.ibm.bundle.b.somePackage。然后在 OSGi 運(yùn)行時(shí)中,OSGi 框架將負(fù)責(zé)匹配不同 bundle 的 import 和 export 列表。而這個(gè)匹配過(guò)程被稱(chēng)為 OSGi 的解決過(guò)程(resolution process)。OSGi 的解決過(guò)程是相當(dāng)復(fù)雜的,但是這個(gè)過(guò)程是被 OSGi 框架實(shí)現(xiàn)了的,不需要每個(gè) bundle 自己來(lái)關(guān)心它。每個(gè) bundle 只需要寫(xiě)一些很簡(jiǎn)單的 import 和 export 聲明語(yǔ)句在各自的 MANIFEST.MF 文件里。一旦 OSGi 框架匹配了 bundle A 的 import 列表里的類(lèi)包 com.ibm.bundle.b.somePackage 與 bundle B 的 export 列表里的類(lèi)包 com.ibm.bundle.b.somePackage,那么這兩個(gè) bundle 就會(huì)被連接在一起,而這就意味著當(dāng) bundle A 需要載入任何類(lèi)包 com.ibm.bundle.b.somePackage 里的類(lèi)時(shí),這個(gè)類(lèi)載入請(qǐng)求就會(huì)被委托給 bundle B 的類(lèi)載入器,而 bundle B 的類(lèi)載入器將會(huì)載入這個(gè)類(lèi),并將類(lèi)實(shí)例傳給 bundle A。而因?yàn)?bundle A 依賴(lài) bundle B 和 bundle C, 如果 bundle A import 列表里的所有類(lèi)包都能在 bundle B 和 bundle C 的 export 列表里發(fā)現(xiàn),那么 bundle A 將會(huì)被稱(chēng)為解決成功,它會(huì)進(jìn)入解決了狀態(tài)(resolved)。如果 bundle A 的 import 列表里需要的某些類(lèi)包沒(méi)有在 bundle B 和 bundle C 的 export 列表里發(fā)現(xiàn),那么 bundle A 的解決就沒(méi)有成功,bundle A 將不能被使用和啟動(dòng)。由此可見(jiàn),OSGi 中,bundle 之間的依賴(lài)關(guān)系是通過(guò)顯示的 import 和 export 列表來(lái)決定的。
- 隱藏了 bundle 中的信息。
因?yàn)樵?OSGi 中,bundle 之間的依賴(lài)關(guān)系是通過(guò)顯示的 import 和 export 列表來(lái)決定的,所以我們沒(méi)有必要 export 一個(gè) bundle 中的所有的類(lèi)包,進(jìn)而能起到隱藏 bundle 中信息的作用。在 OSGi 中,只有那些被顯示的 export 出來(lái)的類(lèi)包才能被其他的 bundle import。
- 增加了版本控制并允許多版本并存。
OSGi 不僅僅使 bundle 之間通過(guò)類(lèi)包名相互依賴(lài),它還可以為類(lèi)包加入版本信息。這是我們能夠應(yīng)付 bundle 的版本變化問(wèn)題。Export 的類(lèi)包也可以攜帶一個(gè)版本信息,而 import 卻可以引用一個(gè)版本范圍內(nèi)的所有類(lèi)包,這讓我們的 bundle 可以依賴(lài)一個(gè)版本范圍內(nèi)的所有類(lèi)包。于是說(shuō),在 OSGi 中,同一 bundle 的多個(gè)版本就可以同時(shí)存在。在本文的第三章,我將詳細(xì)介紹,OSGi 是如何允許同一個(gè) bundle 的多個(gè)版本并行存在,進(jìn)而客服了 Java 應(yīng)用軟件中組件的版本沖突問(wèn)題
解釋 OSGi 是如果克服版本沖突問(wèn)題的
OSGi 之所以能夠解決 Java 應(yīng)用軟件中組件的版本沖突問(wèn)題,原因就是 OSGi 的網(wǎng)狀類(lèi)加載器和 OSGi bundle 的版本信息控制。為什么這么說(shuō)呢?
- OSGi 的網(wǎng)狀類(lèi)加載器架構(gòu)使每個(gè) OSGi bundle 都擁有一個(gè)獨(dú)立的類(lèi)加載器,而 bundle 只是一個(gè)標(biāo)準(zhǔn) Jar 文件。這樣,對(duì)同一個(gè) bundle 的不同版本,我們就可以創(chuàng)建多個(gè)不同的 Jar 文件。這些 Jar 文件的實(shí)際內(nèi)容可以完全一樣,而只是文件名不同(甚至文件名都可以相同,因?yàn)樵?OSGi 框架中,bundle 名和版本的組合才是唯一標(biāo)識(shí)符)。因此這些 Jar 文件,在 OSGi 框架看來(lái),是不同的 bundle,于是同一個(gè)組件的不同版本可以被同時(shí)載入到 JVM 中,這就解決了 Java 應(yīng)用軟件中同一組件不同版本的并存問(wèn)題,接下來(lái)只要解決版本辨識(shí)問(wèn)題,那么 Java 應(yīng)用軟件中組件的版本沖突問(wèn)題就會(huì)被客服掉了。
- OSGi 添加了版本控制信息來(lái)區(qū)分同一個(gè) bundle 的不同版本,而且在 OSGi 框架中 bundle 名和 bundle 版本的組合才是這個(gè) bundle 的唯一標(biāo)識(shí)符,于是通過(guò) bundle 的版本控制,同一個(gè) bundle 的不同版本就可以得到區(qū)分,而 bundle 直接的依賴(lài)關(guān)系也可以通過(guò)版本來(lái)加以限制,從而就能完美的解決 Java 應(yīng)用軟件中組件的版本沖突問(wèn)題了。
那么 OSGi 是如何進(jìn)行版本控制的呢?
OSGi 通過(guò)在 MANIFEST.MF 文件中添加 Bundle-Version 屬性來(lái)個(gè)為每一個(gè) bundle 添加一個(gè)版本信息,而且這個(gè)版本信息必須嚴(yán)格遵循:3 數(shù)字段 +1 字符段的格式,6.2.0.beta_3 是一個(gè)典型有效的 bundle 版本信息。這前面的 3 個(gè)數(shù)字段就是大家都知道的主版本,小版本和微版本,而那個(gè)最后的字母段則是校正段。當(dāng)前面的 3 個(gè)任意一個(gè)數(shù)字段沒(méi)有值時(shí),OSGi 將會(huì)隱式地將 0 付給這個(gè)字段,所以版本 1 是和 1.0、1.0.0 相同的。而如果沒(méi)給 bundle 指定任意一個(gè)版本,那么 0.0.0 將被認(rèn)為是這個(gè) bundle 的版本信息。
另外 OSGi 中,版本的比較是采用從前到后的比較方式。如果在版本比較時(shí),第一個(gè)數(shù)字段就不同,那么后面的 3 個(gè)字段就不用比較了,因?yàn)?OSGi 的前一個(gè)版本段是后面所有字段值的總和,所以大版本就不相同的時(shí)候,后面的小版本就不需要比較了,比如說(shuō):2.0.0 是大于 1.999.999。而如果兩個(gè) bundle 的版本信息,在前面的 3 個(gè)數(shù)字段都相同的時(shí)候,OSGi 就會(huì)對(duì)最后的字母段進(jìn)行比較。而最后的字母段可以包含大寫(xiě)或小寫(xiě)的字母 A 到 Z、數(shù)字、連接線和下劃線,所以它的比較比較復(fù)雜。OSGi 采用了標(biāo)準(zhǔn) Java String 類(lèi)的 compareTo() 方法的算法來(lái)進(jìn)行比較,而標(biāo)準(zhǔn) Java 的 String 類(lèi)的 compareTo() 方法會(huì)對(duì)校正段的每一個(gè)字母按順序進(jìn)行比較,直到出現(xiàn)差異。另外如果字母相同,那么短的那個(gè)校正段的值將被認(rèn)為小于長(zhǎng)的校正段,beta_01 將會(huì)比 beta_010 小。
最后需要提的是 OSGi 不但可以為 bundle 指定一個(gè)版本信息,還可以為每一個(gè)類(lèi)包指定一個(gè)版本信息,即 bundle 的版本控制是可以做到類(lèi)包級(jí)別的(而且這是推薦的 OSGi 版本控制方式)。當(dāng) bundle 在 export 類(lèi)包時(shí),用戶(hù)可以為每個(gè)類(lèi)包指定一個(gè)版本信息。而當(dāng) bundle 需要 import 某特定版本的類(lèi)包時(shí),用戶(hù)除了可以指定一個(gè)特定的版本信息外,還可以指定一個(gè)版本信息范圍。而這個(gè)范圍可以用方括號(hào)“【”和圓括號(hào)“(”來(lái)作為邊界,方括號(hào)“【”表示邊界值也在范圍之內(nèi),而圓括號(hào)“(”則相反 . 比如說(shuō)【 1.0.0,2.0.0)表示從版本 1.0.0 開(kāi)始到 2.0.0 之間的所有的小版本,2.0.0 不在這個(gè)范圍只內(nèi)。下面是一些進(jìn)一步的范圍列表的例子,在下面的表 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 |
解釋 WAS 在引入 OSGi 之后的類(lèi)載入機(jī)制
OSGi 是如此優(yōu)秀的一個(gè)框架,因此很多 Java 應(yīng)用軟件開(kāi)始采用它,WebSphere Application Server(WAS)作為 IBM 最中要的 J2EE 服務(wù)器從版本 6.1 開(kāi)始采用 OSGi 框架,因此當(dāng)你的應(yīng)用是基于 WAS 的并遇到了版本沖突問(wèn)題,你就可以將你的應(yīng)用轉(zhuǎn)換成 OSGi 的 bundle,從而解決版本沖突問(wèn)題。在將你的 WAS 的應(yīng)用轉(zhuǎn)變成 OSGi 的 bundle 之前,我們需要了解 WAS 是如何支持 OSGi 的,進(jìn)而采取相應(yīng)的行動(dòng)。
圖 3. WAS 從 6.1 以后的類(lèi)加載器層次結(jié)構(gòu)
圖 3 是 WAS 從版本 6.1 以后的類(lèi)加載器層次結(jié)構(gòu),從此圖,我們可以知道 WAS 并不是完全采用 OSGi 框架(可能是出于向前兼容等因素考慮),IBM 只是將 WAS 的一部分變成了 OSGi 框架的類(lèi)加載模式,而其他的部分繼續(xù)延續(xù)了以前版本的類(lèi)加載器層 次結(jié)構(gòu)。而 WAS 中 OSGi 的那部分類(lèi)加載器,以網(wǎng)關(guān)的形式與 WAS 的擴(kuò)展類(lèi)加載器連接在一起。
接下來(lái),大家可能奇怪,WAS 原有的類(lèi)加載器是如何同 WAS 中的 OSGi 部分的類(lèi)加載器一起工作的呢? WAS 將如何進(jìn)行類(lèi)加載呢?
為了解釋這個(gè)問(wèn)題,我們首先要清楚,WAS 中的那一部分采用了 OSGi 的框架? WAS 將其常用的重要的插件部分做成了 OSGi,而所有的這些插件被放置在 WAS 的 plugins 目錄中,因此 plugins 目錄下的所有的插件是以 OSGi bundle 的形式被載入到 WAS 中來(lái)的。接下來(lái),我將解釋從版本 6.1 以后 WAS 將如何進(jìn)行類(lèi)加載。從圖 3,我們可以得知,WAS 的 OSGi 框架是與 Ext 類(lèi)加載器連接在一起的,這就是說(shuō) WAS 的 OSGi 框架在 WAS 整體的類(lèi)加載器中處于一個(gè)很高的層次。而 Java 的類(lèi)加載是采用父委托機(jī)制的,這就使普通的 WAS 上的應(yīng)用程序會(huì)一層層的向上請(qǐng)求加載類(lèi),這樣當(dāng)某個(gè)特定的類(lèi)在 WAS 的 OSGi 部分中被發(fā)現(xiàn),那么 WAS 的類(lèi)加載委托就會(huì)進(jìn)入到 WAS 的 OSGi 運(yùn)行時(shí)中。這樣當(dāng)這個(gè)類(lèi)加載以后,而它又需要加載其他類(lèi)時(shí),這之后的類(lèi)加載委托請(qǐng)求就會(huì)在 WAS 的 OSGi 運(yùn)行時(shí)中相互傳遞。
由此可見(jiàn),如果我們需要將自己的 WAS 上的應(yīng)用程序轉(zhuǎn)變成支持 OSGi,我們則需要將這個(gè)應(yīng)用程序的模塊轉(zhuǎn)變成 OSGi bundle,然后將其放置在 WAS 的 plugins 目錄下。
介紹一個(gè)具有版本沖突問(wèn)題的 WAS 上的樣例程序
在本章,我會(huì)模擬一個(gè) WAS 上版本沖突的問(wèn)題,讓大家能清楚地看到這種版本沖突問(wèn)題是如何出現(xiàn)的。然后在第六章,我會(huì)告訴大家如何將這個(gè)樣例程序的某些組件轉(zhuǎn)變成 bundle,從而解決了版本沖突問(wèn)題。
為了說(shuō)明問(wèn)題,我構(gòu)建如下一個(gè)郵件系統(tǒng),這個(gè)系統(tǒng)可以閱讀各種各樣類(lèi)型的郵件。因?yàn)猷]件的類(lèi)型可以存在很多種,而且每一種的郵件可以使用獨(dú)特的瀏覽方式,為了設(shè)計(jì)和維護(hù)的簡(jiǎn)單,我們將它拆分成多個(gè)模塊。我們將各種郵件類(lèi)型共用的接口和基礎(chǔ)類(lèi)拆分成一個(gè)模塊,而將每種不同的郵件類(lèi)型拆分成各自獨(dú)立的模塊。而為了便于描述將來(lái)會(huì)出現(xiàn)的版本沖突問(wèn)題,我們只是用兩種簡(jiǎn)單的硬編碼的郵件類(lèi)型(FixedMailbox 和 XMLMailbox)來(lái)說(shuō)明這個(gè)系統(tǒng)的整體結(jié)構(gòu),請(qǐng)看圖 4。
圖 4. 郵件系統(tǒng)結(jié)構(gòu)圖
這個(gè)結(jié)構(gòu)圖能夠清晰得告訴我們,整個(gè)系統(tǒng)的通用接口和基礎(chǔ)類(lèi)被封裝在 MailboxAPI 模塊中,而每個(gè)具體的郵件類(lèi)型將分別是一個(gè)模塊(FixedMailbox 模塊和 XMLMailbox 模塊),這些模塊將實(shí)現(xiàn)這些通用接口和每種郵件類(lèi)型各自的業(yè)務(wù)邏輯,然后將郵件展示給用戶(hù)瀏覽。所以在這個(gè)圖中將最少有 3 個(gè)模塊,他們會(huì)由不同的團(tuán)隊(duì)來(lái)分別負(fù)責(zé)。當(dāng)整個(gè)系統(tǒng)開(kāi)發(fā)完畢之后,各個(gè)團(tuán)隊(duì)需要將各自的組件發(fā)布到運(yùn)行環(huán)境當(dāng)中。因?yàn)?MailboxAPI 模塊(MailboxAPI.jar)是被所有的其他組件所依賴(lài)的,為了將來(lái)維護(hù)和發(fā)布的方便,一般 MailboxAPI.jar 會(huì)被放置到 WAS 的 lib 目錄下,這樣,其他的每一個(gè)組件都能輕松的找到它,而 MailboxAPI 模塊團(tuán)隊(duì)也可以輕松地發(fā)布和維護(hù) MailboxAPI,因?yàn)榘l(fā)布位置的唯一,MailboxAPI 模塊的每一次發(fā)布和維護(hù)將不需要通知到所有其他的團(tuán)隊(duì)。而其他的模塊,因?yàn)榘瑯I(yè)務(wù)邏輯和展示層,他們一般都是一個(gè) ear 文件,所以他們的發(fā)布位置明顯是與 MailboxAPI.jar 不同的。圖 5 和圖 6 展示給我們 FixedMailbox 和 XMLMailbox 正常運(yùn)行后的樣子。
圖 5. FixedMailbox 的運(yùn)行情況
圖 6. XMLMailbox 的運(yùn)行情況
#T#為了方便大家的學(xué)習(xí),我將已經(jīng)開(kāi)發(fā)好的這 3 個(gè)模塊以附件的形式附在本文上,它們?cè)趬嚎s文件 Version1.zip 里。你只需要解壓縮它,然后將 MailboxAPI.jar 放置在 WAS 的 lib 目錄下,然后啟動(dòng)你的 WAS 系統(tǒng),接著你需要安裝 FixedMailboxReaderEAR.ear 和 XMLMailboxReaderEAR.ear(他們分別代表 FixedMailbox 模塊和 XMLMailbox 模塊)。在 Version1.zip 你還會(huì)發(fā)現(xiàn) FixedMailbox.jar 和 XMLMailbox.jar,它們分別是模塊 FixedMailbox 和 XMLMailbox 的業(yè)務(wù)邏輯,而 FixedMailboxReaderEAR.ear 和 XMLMailboxReaderEAR.ear 是這兩個(gè)模塊的最后封裝。
注意:附件里的每一個(gè)文件里,都含有源碼,以方便感興趣的讀者深入研究。
在整個(gè)郵件系統(tǒng)運(yùn)行一段時(shí)間后,某些用戶(hù)較多的個(gè)別郵件模塊就可能面臨著升級(jí)。因?yàn)榭蛻?hù)多,需求就多,而用戶(hù)的需求又是千奇百怪的,必然會(huì)出現(xiàn)某個(gè)特定的需求波及到了基礎(chǔ)模塊 MailboxAPI。這個(gè)時(shí)候,就可能會(huì)出現(xiàn)如下的情況:模塊 MailboxAPI 和 XMLMailbox 被要求升級(jí),而模塊 FixedMailbox 為了穩(wěn)定,要求不產(chǎn)生任何變化。然而當(dāng) XMLMailbox 的需求很大時(shí),可能使 MailboxAPI 也產(chǎn)生了很大的變化,從而影響了 FixedMailbox,使之不能正常工作,于是版本沖突問(wèn)題產(chǎn)生了。
為了演示這個(gè)問(wèn)題,我改變了模塊 MailboxAPI 和 XMLMailbox 的代碼,產(chǎn)生了版本 2.0 的 MailboxAPI 和 XMLMailbox 模塊,他們被放置在附件 2(Version2.zip)中。為了看到真實(shí)的版本沖突問(wèn)題,請(qǐng)解壓縮 Version2.zip,然后停止 WAS 并刪除 WAS 的 lib 目錄下的 MailboxAPI.jar,接著將 MailboxAPI2.jar 放置在 WAS 的 lib 目錄下,然后重啟你的 WAS 系統(tǒng),接著你需要用 XMLMailboxReaderEAR2.ear 去升級(jí)已經(jīng)存在的 XMLMailboxReader 應(yīng)用,然后分別訪問(wèn)兩個(gè)模塊的 Servlet,你就會(huì)看到 FixedMailbox 模塊將不能正常工作,而 XMLMailbox 則一切正常,如圖 7 或圖 8 所示。
圖 7. FixedMailbox 不能正常運(yùn)行
圖 8. XMLMailbox 可以運(yùn)行
注意:如果你不刪除 MailboxAPI.jar,而是讓 MailboxAPI.jar 和 MailboxAPI2.jar 并存在 WAS 的 lib 目錄下,那么就有可能是 FixedMailbox 能正常工作,而 XMLMailbox 不能正常工作,如圖 9 和圖 10 所示,原因是 MailboxAPI.jar 可能在 MailboxAPI2.jar 之前被載入到類(lèi)加載器中了??傊現(xiàn)ixedMailbox 和 XMLMailbox 將不能同時(shí)工作,這就是版本沖突問(wèn)題。
圖 9. 當(dāng) MailboxAPI.jar 和 MailboxAPI2.jar 并存時(shí),F(xiàn)ixedMailbox 可以正常運(yùn)行
圖 10. 當(dāng) MailboxAPI.jar 和 MailboxAPI2.jar 并存時(shí),XMLMailbox 不能運(yùn)行
講述如何將這個(gè)樣例程序中的某些組件轉(zhuǎn)變成 WAS 上的 OSGi bundle,從而解決了版本沖突問(wèn)題
為了解決在第五章中的版本沖突問(wèn)題,我們只需要將模塊 FixedMailbox 和 XMLMailbox 的業(yè)務(wù)邏輯部分(FixedMailbox.jar 和 XMLMailbox.jar)轉(zhuǎn)變成 OSGi 的 bundle,然后將 MailboxAPI 的版本 1 和版本 2(MailboxAPI.jar 和 MailboxAPI2.jar)也都轉(zhuǎn)變成 bundle,并使 FixedMailbox.jar 依賴(lài)于 MailboxAPI.jar,使 XMLMailbox.jar 依賴(lài)于 MailboxAPI2.jar。接下來(lái),將 WAS 的 lib 目錄下的 MailboxAPI 模塊移除掉,然后將 FixedMailbox.jar、XMLMailbox.jar、MailboxAPI.jar 和 MailboxAPI2.jar 同時(shí)放入 WAS 的 plugins 目錄下,最后重新啟動(dòng) WAS,你會(huì)發(fā)現(xiàn)模塊 FixedMailbox 和 XMLMailbox 能同時(shí)工作,如圖 11 和圖 12 所示。
圖 11. 在將 FixedMailbox.jar 轉(zhuǎn)變?yōu)?bundle 后,F(xiàn)ixedMailbox 可以正常運(yùn)行
圖 12. 在將 XMLMailbox.jar 轉(zhuǎn)變?yōu)?bundle 后,XMLMailbox 可以正常運(yùn)行
在將整個(gè)郵件系統(tǒng)轉(zhuǎn)變成支持 OSGi bundle 后,會(huì)使郵件系統(tǒng)的各個(gè)模塊之間的依賴(lài)關(guān)系從圖 4 編程圖 13 的情況,因?yàn)樵?OSGi 中,同一模塊的多個(gè)版本可以并存,并且模塊之間的依賴(lài)關(guān)系可以通過(guò)版本進(jìn)行限制,從而是 Java 應(yīng)用軟件中的版本沖突問(wèn)題得到了完美的解決。
圖 13. 最后的郵件系統(tǒng)的模塊之間的依賴(lài)關(guān)系
將已有的 Jar 文件轉(zhuǎn)換成 bundle 非常簡(jiǎn)單,我們不需要改變?nèi)魏?Java 代碼,只需要將必要的 OSGi 的 bundle 信息加入 Jar 文件的 MANIFEST.MF。為了方便大家的查看,我將已經(jīng)轉(zhuǎn)好的 bundle 文件 FixedMailbox.jar、XMLMailbox.jar、MailboxAPI.jar 和 MailboxAPI2.jar 做成了附件 3。
總結(jié)
OSGi 框架是實(shí)現(xiàn) Java 應(yīng)用軟件模塊化的重要手段。當(dāng)前,OSGi 已經(jīng)變的非常流行,很多的著名的 Java 應(yīng)用在底層已經(jīng)開(kāi)始采用 OSGi 框架,比如說(shuō) Spring,IBM 的 Eclipse 和 WebSphere Application Server。所以將你自己的 Java 應(yīng)用轉(zhuǎn)變成支持 OSGi 是必要的趨勢(shì),而本文將給你一個(gè)范例,告訴你如何在 WebSphere Application Server 中將你的應(yīng)用轉(zhuǎn)變成支持 OSGi,從而就可以避免版本沖突問(wèn)題的發(fā)生。
本文標(biāo)題:OSGi全面總結(jié)與WebSphere應(yīng)用范例
分享URL:http://www.dlmjj.cn/article/dhcejgg.html


咨詢(xún)
建站咨詢(xún)
