在接觸 OSGi 時,必須學習的第一個概念是捆綁包的概念。在本條目中,我想更仔細地了解捆綁包實際上是什麼,以及如何將普通的 jar 轉換為 OSGi 捆綁包。所以,事不宜遲,
什麼是 Bundle?
OSGi 規範將 Bundle 描述為「模組化的單元」,它「由 Java 類別和其他資源組成,這些資源共同為最終使用者提供功能」。到目前為止還不錯,但 Bundle 到底是什麼?再次引用規範:
一個 Bundle 是一個 JAR 檔案,它
- 包含 [...] 資源
- 包含一個 Manifest 檔案,描述 JAR 檔案的內容並提供關於 Bundle 的資訊
- 可以在 JAR 檔案的 OSGI-OPT 目錄或其子目錄之一中包含選擇性的文件
簡而言之,一個 Bundle = jar + OSGi 資訊 (在 JAR Manifest 檔案中指定 - META-INF/MANIFEST.MF),不需要額外的檔案或預定義的資料夾佈局。這意味著從一個 jar 檔案建立一個 Bundle,只需將一些條目新增到 JAR Manifest 即可。
OSGi 元數據
OSGi 元數據由 Manifest 條目表示,這些條目指示 OSGi 框架 Bundle 提供或/和需要什麼。 該規範指示大約 20 個 Manifest 標頭,但我們只會查看您最有可能使用的標頭。
Export-Package
顧名思義,這個標頭指示 Bundle 中可用的哪些套件 (packages) 被匯出,以便它們可以被其他 Bundle 匯入。只有這個標頭指定的套件會被匯出,其餘的將是私有的,並且不會在包含 Bundle 之外可見。
Import-Package
類似於 Export-Package,這個標頭指示 Bundle 匯入了哪些套件。同樣,只有這個標頭指定的套件會被匯入。預設情況下,匯入的套件是強制性的 - 如果匯入的套件不可用,匯入 Bundle 將無法啟動。
Bundle-SymbolicName
唯一必需的標頭,這個條目指定了 Bundle 的唯一識別碼,基於反向網域名稱慣例(也用於 Java 套件)。
Bundle-Name
為這個 Bundle 定義一個人類可讀的名稱,沒有空格。建議設定此標頭,因為它可以提供比
Bundle-SymbolicName 更短、更有意義的關於 Bundle 內容的資訊。
Bundle-Activator
BundleActivator 是一個 OSGi 特定的介面,允許在 OSGi 框架啟動或停止 Bundle 時通知 Java 程式碼。 這個標頭的值應該包含 activator 類別的完整限定名稱,該類別應該是公開的並且包含一個沒有任何引數的公開建構子。
Bundle-Classpath
當 jar 檔案包含嵌入式程式庫或各個資料夾下的類別套件時,這個標頭非常有用,它會擴充預設的 Bundle 類別路徑(預期類別直接位於 jar 檔案根目錄下)。
Bundle-ManifestVersion
這個鮮為人知的標頭指示要用於讀取此 Bundle 的 OSGi 規範。
1 表示 OSGi 版本 3,而
2 表示 OSGi 版本 4 及更高版本。由於
1 是預設版本,因此強烈建議指定此標頭,因為 OSGi 版本 4 的 Bundle 在 OSGi 版本 3 下無法按預期工作。
下面是一個範例,取自 Spring 2.5.x 核心 Bundle Manifest,它使用了上面提到的一些標頭
Bundle-Name: spring-core
Bundle-SymbolicName: org.springframework.bundle.spring.core
Bundle-ManifestVersion: 2
Export-Package:org.springframework.core.task;uses:="org.springframework.core,org.springframework.util";version=2.5.1 org.springframework.core.type;uses:=org.springframework.core.annotation;version=2.5.1[...]
Import-Package:org.apache.commons.logging,edu.emory.mathcs.backport.java.util.concurrent;resolution:=optional[...]
在 OSGi 元數據上花費的大部分時間可能都花在 Export/Import 套件條目上,因為它們描述了 Bundle 之間的關係(也就是說,模組之間的關係)。 談到套件,沒有什麼是隱含的 - 只有提到的套件會被匯入/匯出,其餘的則不會。這也適用於子套件:匯出 org.mypackage 將匯出 僅 這個套件,而不會匯出其他任何東西(例如 org.mypackage.util)。 匯入也是如此 - 即使一個套件在 OSGi 空間中可用,除非它被顯式匯入,否則它不會被某個 Bundle 看到。
總結一下,如果 Bundle A 匯出套件 org.mypackage,並且 Bundle B 想要使用它,那麼 Bundle A 的 META-INF/MANIFEST.MF 應該在其 Export-Package 標頭中指定該套件,而 Bundle B 應該將其包含在其 Import-Package 條目中。
套件考量
雖然匯出相當簡單,但匯入稍微複雜一些。 應用程式通常會透過搜尋環境中是否存在某些程式庫,然後只使用可用的程式庫來優雅地降級,或者程式庫會包含使用者不使用的程式碼。 這樣的例子包括記錄 (使用 JDK 1.4 或 Log4j)、正則表達式 (Jakarta ORO 或 JDK 1.4+) 或並發實用程式 (JDK 5 中的 java.util 或 backport-util-concurrent 適用於 JDK 1.4 的程式庫)。
在 OSGi 術語中,基於套件的可用性來依賴它會轉化為選擇性的 Package-Import。 在前面的範例中,您已經看到了這樣一個套件
```code Import-Package: [...]edu.emory.mathcs.backport.java.util.concurrent;resolution:=optional ```
由於在 OSGi 中,同一類別的多個版本可以存在,因此最好在匯出和匯入套件時都指定類別套件的版本。 這可以透過在每個套件宣告後新增 version 屬性來完成。 OSGi 支援的版本格式是 <major>.<minor>.<micro>.<qualifier>,其中 major、minor 和 micro 是數字,而 qualifier 是字母數字。
版本的含義完全取決於 Bundle 提供者,但是,建議使用流行的編號方案,例如來自 Apache APR 專案的 這個方案,其中
- <major> - 表示一個重大的更新,不保證任何相容性
- <minor> - 表示一個更新,它保留了與較舊次要版本的相容性
- <micro> - 表示從使用者角度來看的微不足道的更新,它在向前和向後方向上都是完全相容的
- <qualifier> - 是使用者定義的字串 - 它沒有被廣泛使用,可以為版本號提供額外的標籤,例如建置編號或目標平台,而沒有標準化的含義
預設版本(如果屬性遺失)是「0.0.0」。
雖然匯出的套件必須指示一個特定的版本,但匯入器可以使用數學間隔符號來指示一個範圍 - 例如
[1.0.4, 2.0) 將匹配 1.0.42 及以上的版本,直到 2.0(不包括)。請注意,指定一個版本而不是一個間隔將匹配所有大於或等於指定版本的套件,也就是說
Import-Package: com.mypackage;version="1.2.3"
等同於
Import-Package: com.mypackage;version="[1.2.3, ∞)"
作為最後一個提示,請確保在指定版本時始終使用引號,無論它是一個範圍還是一個單獨的版本。
使用 OSGi 元數據
現在我們已經有了一些關於 Bundle 的資訊,讓我們看看我們可以使用哪些工具來 osgi-fying 現有的 jar 檔案
手動
不鼓勵使用這種自己動手做的方法,因為拼寫錯誤和額外的空格很容易潛入並使 Manifest 無用。即使使用智慧型編輯器,Manifest 格式本身也會引起一些問題,因為它每行限制為 72 個空格,如果被打破,可能會導致一些難以理解的問題。手動建立或更新 jar 檔案不是一個好主意,因為 jar 格式要求 META-INF/MANIFEST.MF 條目是存檔中的第一個條目 - 如果不是,即使它存在於 jar 檔案中,Manifest 檔案也不會被讀取。手動方法確實建議用於沒有其他替代方案的情況。
但是,如果有人真的想要/需要直接使用 Manifest 檔案,那麼應該使用一個可以處理 UNIX/DOS 空格的編輯器以及一個適當的 jar 檔案建立實用程式(例如 JDK 附帶的 jar 工具)來處理所有 MANIFEST 要求。
Bnd
Bnd 代表 BuNDle 工具,是由 Peter Kriens(OSGi 技術負責人)建立的一個很好的實用程式,它可以「幫助 [...] 建立和診斷 OSGi R4 Bundle」。 Bnd 解析 Java 類別以了解可用的和匯入的套件,因此它可以建立等效的 OSGi 條目。 Bnd 提供了一系列指令和選項,可以自訂產生的工件。 Bnd.jar 本身的好處是它可以透過 命令列、透過專用的 任務由 Ant 執行,或者整合到 Eclipse 中作為一個 外掛程式。
Bnd 可以從類別路徑或 Eclipse 專案中可用的類別建立 jar 檔案,或者可以透過新增所需的 OSGi 工件來 osgi-fy 現有的 jar 檔案。 此外,它可以列印和驗證給定 jar 檔案的 OSGi 資訊,使其成為一個非常強大但易於使用的工具。
第一次使用的使用者可以使用 Bnd 來查看將哪些 OSGi Manifest 新增到普通的 jar 檔案中。 讓我們選擇一個普通的 jar 檔案,例如 c3p0(這是一個優秀的連線池程式庫)並發出列印指令
```code java -jar bnd.jar print c3p0-0.9.1.2.jar ```
輸出相當大,包含幾個部分
- 通用 Manifest 資訊
[MANIFEST c3p0-0.9.1.2.jar]
Ant…