用osgi實現java的模塊化和熱插拔時要考慮好兩個問題,不同bundle間如何通信?依賴怎么處理?
OSGi的一個標准就是各個bundle之間是相互隔離的,每個bundle都有自己的classloader,並且不同的版本之間都是相互隔離的,這樣就使bundle從物理上進行了隔離,那么OSGi 的bundle之間是怎么進行通信的呢?
下面我們將要介紹3中方法。
Bundles之間通信的方法
方法 描述 1.Export-Package 根據OSGi規范,每個工程可以通過聲明Exprot-Package對外提供訪問此工程中的類和接口,可以先把bundle導出,再導入到需要調用的bundle中 2.OSGi服務 通過將要對外提供功能聲明為OSGi的服務實現面向接口、面向服務式的設計; 3.Event 基於OSGi的Event服務也是實現模塊交互的一種可選方法,模塊對外發布事件,訂閱了此事件的模塊就會相應地接收到消息,從而做出反應,以達到交互的目的。
在JAVA程序中,用到外部包中的類幾乎是必然的事情,在OSGI和MAVEN環境下,引用外部包的方法總結如下:
1.java.開頭的包,是JDK提供了,代碼中直接import。
2.org.osgi開頭的(包括core、compendium等),是osgi規范提供的,已經包含在osgi框架(Felix)中,開發時需要導入,但是發布程序中不需要包含,由Felix提供,實現方法是添加scope為provided的maven依賴,如:
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.core</artifactId>
<version>4.3.0</version>
<scope>provided</scope>
</dependency>
另外如果使用AS容器,而不是osgi嵌入http service,那么jee相關的包如servlet-api也是provided。
3.第三方jar包,可能有多個bundle共享的,直接osgi化,然后作為獨立bundle安裝到Felix中,例如commons-io-1.4.jar,Felix的WebConsole子項目依賴它,我們的很多bundle也可能用到它,開發時在maven中加入
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>1.4</version>
</dependency>
部署時從maven庫中找到這個jar包,然后直接放到Felix的bundle目錄,啟動Felix,就可以看到這個jar包作為一個獨立bundle已經啟動,其他bundle就可以通過直接Import-Package的方式來引用這些包。的之所以能夠這么做是因為這個jar已經osgi化了,查看jar包中的META-INF/MENIFEST.MF文件,只要其中有Bundle-SymbolicName: org.apache.commons.io等一系列Bundle-*的屬性值,並且有Export-Package導出內部的包就可以確定。現在大部分從maven庫中獲得的比較新的jar包,基本上都已經支持OSGI了。如果一個jar包確實不包含OSGI信息,也可以通過手動編輯它的MENIFEST.MF文件增加OSGI信息,來實現這個jar包獨立作為bundle運行。例如json-20090211.jar,直接編輯它的MENIFEST.MF如下:
Manifest-Version: 1.0
Created-By: 1.6.0_07 (Sun Microsystems Inc.)
Export-Package: org.json
Bundle-Name: JSON
Bundle-Version: 20090211
Bundle-SymbolicName: org.json
確保jar包的格式(如果解壓編輯,要重新打包,最好用解壓軟件直接打開編輯自動打包),然后就直接在Felix中運行了。4.第三方的jar包,不考慮多個bundle共享,只確保一個bundle的獨立依賴,可以把這些依賴的jar嵌入到開發的bundle中發布。如在開發com.ailk.common.log4j時,查看log4j的pom發現它依賴多個其他jar包(不同版本log4j依賴的jar包不一樣,這里是1.2.15版本的情況)
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>javax.jms</groupId>
<artifactId>jms</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>com.sun.jdmk</groupId>
<artifactId>jmxtools</artifactId>
<version>1.2.1</version>
</dependency>
<dependency>
<groupId>com.sun.jmx</groupId>
<artifactId>jmxri</artifactId>
<version>1.2.1</version>
</dependency>
maven中加入這些就可以在開發時引用了,但是發布到Felix框架時,我們要把這些jar包一起提供才行,方法是把這些jar包嵌入到我們的bundle中,使用maven-bundle-plugin,增加instructions配置<Embed-Dependency>*;scope=compile|runtime</Embed-Dependency>,這樣maven在打包是就可以自動把這些依賴的jar包嵌入。要注意這些依賴的scope和Embed-Dependency的表述方式的匹配,具體Embed-Dependency可能的寫法請參見maven-bundle-plugin在線文檔。
這種方式引用外部包,好處是自帶依賴,操作簡單,不像方法3中那樣,如果依賴jar包更新要受到影響,並且要注意多個bundle共享依賴是否有沖突。壞處是所有依賴jar包都要一起打包到bundle中,顯得非常臃腫。
5.自己開發的bundle之間,互相引用其他bundle的包,被引用者注意要Export-Package,並打包部署到maven庫,其他引用的bundle在開發階段通過maven依賴直接引用,在部署時確保幾個管理的bundle同時都在Felix中並且resolved。
6.嵌入包瘦身,針對上面第四點,在嵌入第三方包到bundle時,如果完全按照pom中指定的依賴全部打包進bundle,bundle體積會比較大,但是這些jar包是否全部都是必須的呢?不一定。例如log4j的pom指定依賴javax.mail的mail.jar,但是如果不使用mail方式的appender,這個jar基本上是用不到的。同樣mybatis的pom中也包含了5種log實現作為依賴,包括log4,slf4j,common-logging等,但是實際上這個日志功能並不是必須全部具備,嵌入全部這些log實現的jar就顯得多余。但是如果在pom中不配置這些jar包的依賴,開發編譯階段沒有問題,但是bundle在resolve的過程會包缺少需要的package。解決方法是在pom中仍然加入這些Jar包的依賴,但是增加一個<optional>true</optional>的屬性,同時修改打包instructions為<Embed-Dependency>*;scope=compile|runtime;optional=!true</Embed-Dependency>。經驗證,這做是可以的,log4j的bundle不再包含mail,jms,jmxtools,jmxri幾個jar包后,體積減少了一半以上,功能不受影響。
http://blog.csdn.net/rosen_luo/article/details/47398391