Module
OSGi中具體實(shí)現(xiàn)Module的單位是bundle,一個(gè)bundle就是一個(gè)jar文件,其中包含所需的類文件和資源文件,同時(shí)必須包含一個(gè)描述文件;每個(gè)bundle都可以被獨(dú)立打包、部署??吹竭@里,你是否會(huì)覺(jué)得跟J2EE中的WAR定義很類似?
單從形式上來(lái)看,它們的確非常相似,而且它們的區(qū)別主要在于:
1)J2EE的WAR文件的粒度很大,是以應(yīng)用為單位的;而OSGi bundle的粒度則相對(duì)小很多,以一組服務(wù)為單位,一個(gè)OSGi應(yīng)用將包含多個(gè)bundle。
2)最重要的差別是,bundle之間可以通過(guò)共享java package、發(fā)布或者引用服務(wù)進(jìn)行協(xié)作。而Web應(yīng)用之間幾乎是不存在協(xié)作的,起碼在定義上沒(méi)有。
3)在J2EE中,可以將多個(gè)war文件打包成為一個(gè)ear文件進(jìn)行部署,而OSGi/bundle則沒(méi)有這種"Application"的概念,每個(gè)bundle都必須獨(dú)立部署。
Life Cycle
bundle擁有自己的生命周期,可以被安裝、啟動(dòng)、激活、停止等,這一點(diǎn)與J2EE中的WAR也非常相似。不過(guò)由于不存在協(xié)作關(guān)系,WAR的生命周期相對(duì)簡(jiǎn)單,只關(guān)心自己能否啟動(dòng)則可。而bundle在被激活之前,必須保證其依賴的其他bundle已經(jīng)存在。
Class Loading
在J2EEServlet規(guī)范中,對(duì)ClassLoader的著墨不多,不過(guò)目前各產(chǎn)品的實(shí)現(xiàn)都比較類似,就是每個(gè)WAR文件有一個(gè)獨(dú)立的ClassLoader。如下圖,由于WebApp1和WebApp2使用不同的ClassLoader,因此它們可以使用同一個(gè)Java Class的不同版本:
- +-----------------------------+
- | Bootstrap |
- | | |
- | System |
- | | |
- | Common |
- | / \ |
- | Catalina Shared |
- | / \ |
- | WebApp1 WebApp2 |
- +-----------------------------+
在這張圖中我們可以看到Catalina用獨(dú)立的ClassLoader,這是一個(gè)進(jìn)步。早期很多人都遇到過(guò)這樣的問(wèn)題,自己的應(yīng)用中采用了某個(gè)開(kāi)源軟件,部署的時(shí)候卻無(wú)法正常運(yùn)行。其原因是服務(wù)器已經(jīng)采用了該開(kāi)源軟件的較老版本,而自己的應(yīng)用卻依賴與新版本。新版本的Tomcat則把自己依賴的類庫(kù)放在Catalina分支,這樣這些類庫(kù)對(duì)所有WebApp都不可見(jiàn)。同時(shí)由Shared負(fù)責(zé)的類庫(kù)則是所有WebApp都能夠用到。
這樣的ClassLoader結(jié)構(gòu)對(duì)于WebApp來(lái)說(shuō)已經(jīng)相當(dāng)不錯(cuò),但是仍然有一個(gè)問(wèn)題沒(méi)有解決,那就是如果WebApp1需要用到WebApp2的類怎么辦?對(duì)于WebApp來(lái)說(shuō),這種需求的確相當(dāng)罕見(jiàn),因?yàn)閼?yīng)用與應(yīng)用之間一般不會(huì)出現(xiàn)之間的類引用。但是對(duì)于一個(gè)應(yīng)用中的多個(gè)模塊,相互引用則是再正常不過(guò)了。
論壇上正好有個(gè)帖子“有關(guān)于classloader的思考(或者說(shuō)是困惑)”整理了這方面的需求,我則無(wú)需重復(fù)。樓主edge_hh的問(wèn)題用OSGi則可以很簡(jiǎn)單解決:
1. 在模塊A的配置文件說(shuō)明"Export-Package: demo.a.httpservice"
2. 在模塊B的配置文件說(shuō)明"Import-Package: demo.a.httpservice"
3. 將所有httpservice接口需要的定義放置在模塊A的demo.a.httpservice下
這樣就可以了。這就是OSGi的特別之處,bundle的CloassLoader是平級(jí)的,但平級(jí)的CloassLoader之間可以共享Java Package。
Delcare Services
在edge_hh的帖子中,他沒(méi)有明確提出的一個(gè)問(wèn)題是,模塊B是如何調(diào)用模塊A的服務(wù)?
在Java WebApp中,是不存在跨WebApp的服務(wù)調(diào)用,forward和include操作都是局限在同一個(gè)WebApp中。EJB中用<ejb-ref>來(lái)描述對(duì)EJB的引用,不過(guò)這種引用方式就如人們對(duì)EJB2的批評(píng)一樣,復(fù)雜、不方便使用。
實(shí)際上在服務(wù)引用這方面,大家更加熟悉的應(yīng)該是Spring的方式,如下:
- <beans>
- <bean id="ModuleA" class="demo.a.httpservice.HttpService"/>
- <bean id="ModuleB" calss="demo.b.Consumer">
- <property name="httpService">
- <ref bean="ModuleA"/>
- </property>
- </beans>
不過(guò)在這種場(chǎng)合下Spring存在的問(wèn)題有:
1. Spring沒(méi)有"module"的概念,難以說(shuō)明“一個(gè)module包含多個(gè)service”這樣的情況
2. Spring的DI是靜態(tài)的,一旦建立就不會(huì)更改。而在模塊化的程序中,一個(gè)模塊在運(yùn)行時(shí)也有卸載、重新部署的時(shí)候。Spring無(wú)法處理這種情況。
讓我們來(lái)看看OSGi的做法,下面的配置代碼來(lái)自《OSGI 實(shí)戰(zhàn)》的例子:
1)聲明服務(wù):
- <?xml version="1.0" encoding="UTF-8"?>
- <component name="DBValidator">
- <implementation class="org.riawork.demo.service.user.impl.DBValidatorImpl"/>
- <service>
- <provide interface="org.riawork.demo.service.user.Validator"/>
- </service>
- </component>
2)引用服務(wù):
- <?xml version="1.0" encoding="UTF-8"?>
- <component name="LoginServlet">
- <implementation class="org.riawork.demo.web.servlet.LoginServlet"/>
- <reference name="ValidatorService" interface="org.riawork.demo.service.user.Validator" bind="setValidator" unbind="unsetValidator" policy="dynamic" cardinality="0..1"/>
- <reference name="HttpService" interface="org.osgi.service.http.HttpService" bind="setHttpService" unbind="unsetHttpService" policy="dynamic"/>
- </component>
其中特別的部分是bind和unbind的設(shè)定。通過(guò)bind和unbind,服務(wù)消費(fèi)者能夠知道所需服務(wù)對(duì)應(yīng)模塊的啟動(dòng)和停止,從而實(shí)現(xiàn)了DI的動(dòng)態(tài)綁定。
"OSGi technology is the dynamic module system for Java!"
在學(xué)習(xí)OSGi之前,盛名之下,總覺(jué)得OSGi是很復(fù)雜的技術(shù);然而在初步了解OSGi后,又覺(jué)得它非常簡(jiǎn)單,或者說(shuō)是如此的清晰明了。我初步認(rèn)識(shí)到OSGi的主要好處是:
1)明確定義了Moduel/Service的。
“我們的應(yīng)用/系統(tǒng)是模塊化的”,這是一句常常能聽(tīng)到的話語(yǔ),然而個(gè)模塊的具體實(shí)現(xiàn)方式恐怕每個(gè)應(yīng)用都不盡相同,這種情況非常不利于開(kāi)發(fā)團(tuán)隊(duì)積累可重用的模塊?,F(xiàn)在通過(guò)OSGi的嚴(yán)格定義,有望形成一個(gè)標(biāo)準(zhǔn)的模塊市場(chǎng),Eclipse的Plug-in就是一個(gè)很好的例子。即便只是在公司范圍內(nèi)形成模塊倉(cāng)庫(kù),都將對(duì)開(kāi)發(fā)效率有極大提高。當(dāng)然一個(gè)相當(dāng)規(guī)模的模塊市場(chǎng),必然是依賴于一套設(shè)計(jì)良好的Service接口的。
2)運(yùn)行時(shí)的動(dòng)態(tài)性。
服務(wù)具體由哪個(gè)模塊提供,模塊的安裝、啟動(dòng)、停止、卸載,這些都可以在運(yùn)行時(shí)指定,并且隨時(shí)更改。這樣的情況下,應(yīng)用的動(dòng)態(tài)性就取決于你的想象力了。舉一個(gè)實(shí)在的例子,我們無(wú)需重新啟動(dòng)整個(gè)應(yīng)用,就能夠?qū)?yīng)用進(jìn)行打補(bǔ)丁、升級(jí)。