MANIFEST.MF 可能出現在任何包括主類信息的 Jar 包中,一般位於 META-INF 目錄中,所以此文件並不是一個 OSGi 特有的東西,而僅僅是增加了一些屬性,這樣也正好保持了 OSGi 環境和普通 Java 環境的一致性,便於在老的系統中部署。表 2 列出此文件中的重要屬性及其含義:
表 2. MANIFEST.MF 文件屬性
屬性名字 | 含義 |
Bundle-Activator |
Bundle 的啓動器 |
Bundle-SymbolicName |
名稱,一般使用類似於 JAVA 包路徑的名字命名 |
Bundle-Version |
版本,注意不同版本的同名 bundle 可以同時上線部署 |
Export-Package |
導出的 package 聲明,其它的 bundle 可以直接引用 |
Import-Package |
導入的 package |
Eclipse-LazyStart |
是否只有當被引用了才啓動 |
Require-Bundle |
全依賴的 bundle,不推薦 |
Bundle-ClassPath |
本 bundle 的 class path,可以包含其它一些資源路徑 |
Bundle-RequiredExecutionEnvironment |
本 bundle 必須的執行環境,例如 jdk 版本聲明 |
我們已經看到,編寫一個很普通的 Hello world 應用,必須首先創建一個 plug-in 工程,然後編輯其 Activator 類的 start
方法,實際我們這樣做的本質是爲 OSGi 運行環境添加了一個 bundle,那麼一個 bundle 必須的構成元素是哪些呢?
- MANIFEST.MF:描述了 bundle 的所有特徵,包括名字、輸出的類或者包,導入的類或者包,版本號等等,具體可以參考 表 2. MANIFEST.MF 文件屬性。
- 代碼:包括 Activator 類和其它一些接口以及實現,這個和普通的 Java 應用程序沒有什麼特殊的區別。
- 資源:當然,一個應用程序不可能沒有資源文件,比如圖片、properties 文件、XML 文件等等,這些資源可以隨 bundle 一起存在,也可以以 fragment bundle 的方式加入。
- 啓動級別的定義:可以在啓動前使用命令行參數指定,也可以在運行中指定,具體的 start level 的解釋,請參考 後面的說
好了,我們已經明白 bundle 是什麼了,也知道如何開發一個基本的 bundle 了,那麼我們還必須要明白,我的 bundle 放在 Equinox 框架中,它對我們的 bundle 做了些什麼?
圖 11. Equinox 框架架構
實際上,目標平臺已經爲我們準備了 N 個 bundle,它們提供各種各樣的服務,OSGi 中,這些 bundle 的名字叫 system bundle,就好比精裝修的房子,您只需要拎包入住,不再需要自己鋪地板,裝吊頂了。
我們的 bundle 進入 Equinox 環境後,OSGi 框架對其做的事情如下:
- 讀入 bundle 的 headers 信息,即 MANIFEST.MF 文件;
- 裝載相關的類和資源;
- 解析依賴的包;
-
調用其 Activator 的
start
方法,啓動它; - 爲其提供框架事件、服務事件等服務;
-
調用其 Activator 的
stop
方法,停止它;
OK,到現在爲止,似乎一切都是新鮮的,但是您似乎在考慮,OSGi 到底有什麼優勢,下面介紹一下其中的一個特點,幾乎所有的面向組件的框架都需要這一點來實現其目的: 面向服務、封裝實現 。這一點在普通的 Java 應用是很難做到的,所有的類都暴露在 classpath 中,人們可以隨意的查看您的實現,甚至變更您的實現。這一點,對於希望發佈組件的公司來說是致命的。
圖 13. OSGi bundle 原理
OSGi 很好的解決了這個問題,就像上面的圖顯示的,每個 bundle 都可以有自己公共的部分和隱藏的部分,每個 bundle 也只能看見自己的公共部分、隱藏部分和其它 bundle 的公共部分。
bundle 的 MANIFEST.MF 文件提供了 EXPORT/IMPORT package 的關鍵字,這樣您可以僅僅 export 出您希望別人看到的包,而隱藏實現的包。並且您可以爲它們編上版本號,這樣可以同時發佈不同版本的包。
這一點比較難理解,一般情況下您不需要關心這個事情,除非事情出現了問題,您發現明明這個類就在這裏,怎麼就是報告 ClassNotFoundException/NoClassDefExcpetion 呢?在您垂頭喪氣、準備砸掉電腦顯示器之前,請看一下 bundle 中的類是如何查找的:
-
首先,它會找 JRE,這個很明顯,這個實際是通過系統環境的
JAVA_HOME
中找到的,路徑一般是 JAVA_HOME/lib/rt.jar、tools.jar 和 ext 目錄,endorsed 目錄。 - 其次,它會找 system bundle 導出的包。
- 然後,它會找您的 import 的包,這個實際包含兩種:一種是直接通過 require-bundle 的方式全部導入的,還有一種就是前面講的通過 import package 方式導入的包。
- 查找它的 fragment bundle,如果有的話。
- 如果還沒有找到,則會找自己的 classpath 路徑(每個 bundle 都有自己的類路徑)。
- 最後它會嘗試根據 DynamicImport-Package 屬性查找的引用。