OSGi入门篇:模块层
1 什么是模塊化
模塊層是OSGi框架中最基礎(chǔ)的一部分,其中Java的模塊化特性在這一層得到了很好的實(shí)現(xiàn)。但是這種實(shí)現(xiàn)與Java本身現(xiàn)有的一些模塊化特性又有明顯的不同。 本文介紹模塊層的一些基礎(chǔ)知識(shí),以及OSGi聯(lián)盟在設(shè)計(jì)模塊層時(shí)所做的一些考慮。OSGi標(biāo)準(zhǔn)走到今天,并不是憑空想出來(lái)的,它的產(chǎn)生恰恰是為了彌補(bǔ)之前一些技術(shù)的缺陷。
模塊化其實(shí)就是計(jì)算機(jī)科學(xué)中常見(jiàn)的一個(gè)概念: “將一個(gè)大型系統(tǒng)分解為多個(gè)較小的互相協(xié)作的邏輯單元,通過(guò)強(qiáng)制設(shè)定模塊之間的邏輯邊界來(lái)改善系統(tǒng)的維護(hù)性和封裝性”。
在OSGi中模塊的定義可以參考下圖:
也就是說(shuō)一個(gè)模塊(module)定義了一個(gè)邏輯邊界,這種模塊本身精確的控制了哪些類是完全被封裝起來(lái)的,而哪些類需要暴露出來(lái)作為外部使用。這樣我們就可以輕松的將實(shí)現(xiàn)屏蔽在模塊的內(nèi)部,而將公共API暴露在外部。
2 為什么需要模塊化
2.1 OSGi中模塊化與面向?qū)ο蟮穆?lián)系與區(qū)別
按照以上模塊化的定義,可能有的人會(huì)問(wèn):“在面向?qū)ο罄锩?#xff0c;不是也有對(duì)模塊化的支持嗎?”沒(méi)錯(cuò),面向?qū)ο蟮母拍羁梢哉f(shuō)也在一定程度上支持模塊化編程,那為什么還需要OSGi提供的模塊化特性呢?這涉及到“邏輯邊界”的不同粒度。
在用Java編寫面向?qū)ο蟪绦虻臅r(shí)候,一個(gè)了解面向?qū)ο蟾拍畹娜耸遣粫?huì)把所有功能都塞到同一個(gè)類里面去的,面向?qū)ο笞屇銖膯?wèn)題域中發(fā)現(xiàn)多個(gè)事物,并且每個(gè)事物負(fù)責(zé)不同的功能,盡量做到高內(nèi)聚和低耦合。在這里,我們可以說(shuō)面向?qū)ο蟮哪K化粒度是在“類”這個(gè)級(jí)別上。?
而OSGi的模塊化,則是通過(guò)為JAR包添加metadata來(lái)定義哪些類應(yīng)該暴露哪些類又隱藏在包中,其控制可見(jiàn)性的粒度是在bundle(JAR包)這一層面上的。
所以,它們所帶來(lái)的能力都是通過(guò)控制可見(jiàn)性和可用性來(lái)保證高內(nèi)聚和低耦合的,但是粒度不同,一個(gè)是對(duì)象層面上的,一個(gè)是模塊層面上的。 既然負(fù)責(zé)的是不同的粒度,那么兩者并不相互沖突,各有各的作用在里面。
2.2 Java在模塊化方面的局限性
2.2.1 底層代碼可見(jiàn)性控制
Java提供了private,public,protected和package private(無(wú)修飾符)這四種訪問(wèn)控制級(jí)別,不過(guò)這僅僅提供了底層的OO數(shù)據(jù)封裝特性。包這個(gè)概念確實(shí)是起到了分割代碼的作用,但是如果包中的代碼需要對(duì)包外可見(jiàn),那么必須設(shè)置為public(或者protected,如果是使用了繼承的話)。 這樣的話就可能出現(xiàn)一個(gè)問(wèn)題:
首先大家看看下面的例子,其中有三個(gè)java文件:?
org.serc.helloworld.Hello.java:定義了一個(gè)接口
org.serc.helloworld.impl.HelloImpl.java:實(shí)現(xiàn)了Hello接口
package org.serc.helloworld.impl;import org.serc.helloworld.Hello;public class HelloImpl implements Hello{ final String helloString;public HelloImpl(String helloString){this.helloString = helloString; }public void sayHello(){System.out.println(this.helloString); } }org.serc.helloworld.main.Main.java package org.serc.helloworld.main;import org.serc.helloworld.Hello; import org.serc.helloworld.HelloImpl;public class Main{ final String helloString;public static void main(String[] args){Hello hello = new HelloImpl(“Hello,SERC!”);hello.sayHello(); }}
這三個(gè)文件分別在不同的包中。按理說(shuō),HelloImpl這個(gè)實(shí)現(xiàn)細(xì)節(jié)是不應(yīng)該暴露給其他包的,但是從Main.java的main方法中我們可以明顯的看出,為了創(chuàng)建Hello 的實(shí)例,我們不得不引入HelloImpl類,但是HelloImpl作為接口的實(shí)現(xiàn)細(xì)節(jié),是不應(yīng)該暴露給使用者的,這違反了封裝的原則,顯然不太好。
但是,如果我們不想讓HelloImpl暴露出來(lái)的話,就需要做額外的工作來(lái)保證“既隱藏了實(shí)現(xiàn)細(xì)節(jié),又能簡(jiǎn)單的創(chuàng)建一個(gè)實(shí)現(xiàn)了Hello接口的實(shí)例”。達(dá)到這一目的的方法不止一種(比如工廠模式),有些至今也是很常用的,但是這增加了與應(yīng)用本身功能無(wú)關(guān)的多余工作,想想如果你每次想開(kāi)發(fā)一個(gè)應(yīng)用都要為了達(dá)到上述目的而做出多余工作,不得不說(shuō)是有點(diǎn)繁瑣的,所以這可以說(shuō)是Java的一大局限。
2.2.2 classpath的局限
我們?cè)赾lasspath中加入jar包的時(shí)候,只是簡(jiǎn)單的給出文件路徑,而這個(gè)jar包的版本和一致性,它所依賴的jar包是什么,我們都無(wú)法在classpath中明確的設(shè)置或是從classpath中看出這些屬性。?
并且classpath中的jar包是按序加載的,例如:
classpath=c:\servlet2.2\servlet.jar;c:\servlet2.3\servlet.jar,
那么在實(shí)際應(yīng)用的過(guò)程中,Java讓你使用的是servlet2.2,而不是servlet2.3。這種情況下我們還能看出來(lái)使用的是哪個(gè)版本,如果在大型系統(tǒng)中大家分開(kāi)開(kāi)發(fā)的時(shí)候各用各的servlet包,并且版本號(hào)不一樣,那么在最后將開(kāi)發(fā)結(jié)果合并的時(shí)候,到時(shí)候用的是哪個(gè)版本的servlet包就很難搞清楚了,也就說(shuō)不可控性是比較強(qiáng)的。?
即使classpath能注意到版本的問(wèn)題,也沒(méi)法精確指出依賴。試著回想你在設(shè)置classpath的過(guò)程中出現(xiàn)過(guò)情況:在你以為classpath已經(jīng)設(shè)置完畢以后,你嘗試啟動(dòng)程序,結(jié)果虛擬機(jī)拋出異常告訴你缺包,然后你再加上你覺(jué)得缺少的那些包,然后再啟動(dòng)程序,如此反復(fù)直到虛擬機(jī)不運(yùn)行到缺包異常為止。
2.3 OSGi對(duì)這些局限性的改善
對(duì)于上一小節(jié)提到的Java的局限,在OSGi中都得到了很好的解決。
- 包的可見(jiàn)性:OSGi通過(guò)引入包的可見(jiàn)性機(jī)制,能夠完全控制一個(gè)包中的代碼對(duì)哪些模塊可見(jiàn),而不僅僅局限于無(wú)差別的可見(jiàn)性,從而完善了Java的代碼訪問(wèn)控制機(jī)制。
- 包的版本: OSGi通過(guò)為包增加版本信息,可以精確控制代碼的依賴,保證代碼的版本一致性,彌補(bǔ)了classpath的缺點(diǎn)。
3 OSGi模塊層基礎(chǔ)
3.1 Bundle的概念
這是模塊層最核心的概念,也是模塊(module)這個(gè)概念在OSGi中的具象表現(xiàn)。接下來(lái)你將會(huì)在OSGi的世界中創(chuàng)建和使用數(shù)不勝數(shù)的bundle。?
什么是bundle?——bundle是以jar包形式存在的一個(gè)模塊化物理單元,里面包含了代碼,資源文件和元數(shù)據(jù)(metadata),并且jar包的物理邊界也同時(shí)是運(yùn)行時(shí)邏輯模塊的封裝邊界。
一個(gè)更為直觀的說(shuō)明:在標(biāo)準(zhǔn)的jar包的manifest文件中添加一些bundle的模塊化特征(就是前面提到的metadata)后,這個(gè)jar包就變成了一個(gè)bundle。?
那么有了上面的描述,你大概也能想到,bundle和普通jar包最大的區(qū)別就在于元數(shù)據(jù)。
3.2 使用元數(shù)據(jù)來(lái)定義bundle
Bundle元數(shù)據(jù)的目的在于準(zhǔn)確描述模塊化相關(guān)的bundle特征,這樣才能讓OSGi框架恰當(dāng)?shù)膶?duì)bundle進(jìn)行各種處理工作(比如依賴解析,強(qiáng)制封裝等),這些元數(shù)據(jù)主要有這三個(gè)部分:
- 可讀的信息(可選):幫助更好地理解和使用bundle
- bundle的標(biāo)識(shí)符(必須):唯一的標(biāo)識(shí)一個(gè)bundle
- 代碼可見(jiàn)性(必須):定義內(nèi)部與外部代碼
3.2.1 可讀的信息
這些內(nèi)容可以幫助人們直觀的了解這個(gè)bundle是做什么的,從哪里來(lái)。OSGi標(biāo)準(zhǔn)定義了幾個(gè)元數(shù)據(jù)條目來(lái)達(dá)到這個(gè)目的,但是所有的條目都不是必須的,并且也不對(duì)模塊化特性產(chǎn)生任何的影響,OSGi框架會(huì)完全無(wú)視這些內(nèi)容。?
下面就是一個(gè)這類信息的例子:
3.2.2 bundle的標(biāo)識(shí)符
在上個(gè)小節(jié)提到的可讀信息中,有些似乎也能用來(lái)唯一標(biāo)識(shí)一個(gè)bundle,比如Bundle-Name,但是事實(shí)上,他并沒(méi)有被用來(lái)標(biāo)識(shí)一個(gè)bundle。早期的OSGi標(biāo)準(zhǔn)中并沒(méi)有提供標(biāo)識(shí)一個(gè)已知bundle的方法,直到OSGi R4標(biāo)準(zhǔn)“唯一bundle標(biāo)識(shí)符”這個(gè)東西才被提出來(lái)。為了向后兼容,Bundle-Name就不能用來(lái)作為標(biāo)識(shí)符了,否則就會(huì)增加維護(hù)向后兼容的工作,所以新的manifest屬性就誕生了:Bundle-SymbolicName
Bundle-SymbolicName: org.serc.helloworld?
兩者相比,Bundle-Name是給用戶讀的,而Bundle-SymbolicName是給OSGi框架讀的,讓OSGi框架能夠唯一標(biāo)識(shí)一個(gè)bundle。
只用一個(gè)Bundle-SymbolicName肯定是可以唯一標(biāo)識(shí)一個(gè)bundle了,但是隨著時(shí)間的推移,你的bundle可能會(huì)有新版本,這時(shí)候加入版本屬性會(huì)讓你的bundle的信息更加準(zhǔn)確。
Bundle-Name: SERC Helloworld Bundle-Vendor: GR, SERC Bundle-DocURL: http://elevenframework.org Bundle-Category: example Bundle-Copyright: SERC3.2.3 代碼可見(jiàn)性
在JavaSE中的jar包如果放在了classpath里面,那么它對(duì)這個(gè)classpath下的所有程序都是可見(jiàn)的,并且這種可見(jiàn)性不能改變,而OSGi標(biāo)準(zhǔn)定義了如下的屬性用于描述代碼的可見(jiàn)性:
-
Bundle-ClassPath—它定義了形成這個(gè)bundle的所有代碼所在的位置,和Java的classath的概念相近,不同點(diǎn)在于,Java中的classpath是定義的jar包的位置,而這個(gè)屬性描述的是bundle內(nèi)部的類在bundle中的路徑。有例子如下:
Bundle-ClassPath:.,other-classes/,embedded.jar -
Export-Package—顯式暴露需要和其他bundle共享的代碼,每個(gè)包之間用逗號(hào)分隔,每個(gè)包可用修飾詞來(lái)修飾包的其他特征。
Export-Package: org.serc.hellworld; vendor=”SERC”, org.serc.hellworld.impl; vendor=”Gou Rui” -
Import-Package—定義該bundle所依賴的外部代碼,其格式和Export-Package相同,并且也可以使用修飾詞來(lái)修飾包。不過(guò)這里的修飾詞是用來(lái)限制所依賴包的范圍的,像是一個(gè)過(guò)濾器,而不像Export-Package中用來(lái)聲明包的特征。例如如下語(yǔ)句:
Import-Package: org.serc.helloworld; vendor=”SERC”
4 總結(jié)
通過(guò)這一章,希望讀者們能夠?qū)SGi模塊層有一個(gè)初步的了解,明白這一層著重解決的是一些什么問(wèn)題,并且在這之后配合著《OSGi開(kāi)發(fā)環(huán)境的建立和HelloWorld》這一章可以嘗試著自己創(chuàng)建一個(gè)bundle,通過(guò)在MANIFEST文件中填寫元數(shù)據(jù)來(lái)暴露一些包,引入一些包。在下一篇入門篇中,我們將講解OSGi的生命周期層。
from:http://osgi.com.cn/article/7289219?
總結(jié)
以上是生活随笔為你收集整理的OSGi入门篇:模块层的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 以 OSGi 包的形式开发和部署 Web
- 下一篇: OSGI动态加载删除Service bu