SpringSource 應用平臺清單頭部

工程 | Glyn Normington | 2008年5月8日 | ...

SpringSource 應用平臺由 OSGi Bundle 構建而成,並支援同樣由 OSGi Bundle 構建的應用。該平臺支援 OSGi 的標準特性,但也支援一些額外的清單頭部。有幾個人問過 為什麼 SpringSource 添加了專有頭部?新頭部的語義是什麼?,所以這篇博文解釋了 Import-LibraryImport-Bundle 的背景動機和語義。

標準 OSGi Bundle 支援

該平臺基於 OSGi R4.1 標準(或者如果你喜歡,也可以稱作 JSR 291)構建,並使用 Equinox 作為其 OSGi 實現。因此,你可以使用平臺的工具開發標準的 OSGi Bundle,並將這些 Bundle 部署到平臺上,自平臺釋出以來,許多使用者一直在這樣做。

因此,精通 OSGi 的開發者可以將該平臺用作標準的 OSGi 容器,並受益於平臺特性,例如:

  • 能夠使用管理控制檯部署 Bundle,或將 Bundle 放置在平臺的 pickup 目錄中進行部署,
  • 診斷功能,如解析失敗診斷、應用特定跟蹤和自動死鎖檢測,
  • 與 Spring 和 Spring Dynamic Modules 的強大整合,適用於希望使用這些框架的開發者,以及
  • 從倉庫自動配置依賴項。
然而,該平臺也致力於讓幾乎沒有接觸過 OSGi 的企業應用開發者輕鬆受益於 OSGi,這對平臺提出了一些額外的要求。

企業應用的額外要求

正如 Sam 最近關於平臺部署選項的博文所解釋的,你可以在平臺上部署現有的單體 WAR 檔案,而無需瞭解 OSGi —— 平臺會為你處理一切。但是,要受益於共享庫、共享服務以及最終的 PAR 檔案作用域,需要將單體 WAR 檔案拆分為 OSGi Bundle。這有多難呢?

嗯,這個過程中的一些步驟相對容易,特別是如果遵循了良好的軟體工程實踐並且程式碼已經組織成服務、領域和基礎設施元件。這些元件可以轉換為 Bundle,並且它們之間的依賴關係可以使用 META-INF/MANIFEST.MF 中的標準 OSGi Import-Package 和 Export-Package 頭部來表達。

更困難的一步是表達對企業框架(如 Spring 和 Hibernate)的依賴。使用標準的 OSGi Import-Package 和 Require-Bundle 頭部來表達這些依賴關係是完全可行的,如果你的目標是建立可以在其他 OSGi 容器中執行的 OSGi Bundle,這正是你應該做的,但這種方法有一些隱含成本。

首先,開發者必須精確地決定一個給定框架包含哪些包。僅僅匯入應用程式碼使用的包是不夠的,因為一些企業框架在應用載入時會將更多的依賴編織到應用的位元組碼中。開發者必須透過反覆試驗來發現需要匯入哪些額外的實現包,以確保編織後的應用行為正確。

其次,從一個框架版本遷移到下一個版本是一項繁瑣的任務,因為構成該框架的精確包集合可能已經改變。編織所需的附加包通常未由公共契約定義,因此可能會發生變化。

此外,由此產生的包匯入並不能恰當地體現設計意圖,這使得未來維護或擴充套件應用更加困難。

我們真的不想把這些負擔強加給使用者,所以我們建立了一些額外的 SpringSource 應用平臺特定的清單頭部,Import-LibraryImport-Bundle,作為表達對企業框架依賴的便捷方式。正如你將在下面看到的,這些頭部實際上只是 語法糖,它們最終會被轉換為標準的 OSGi 包匯入。

Import-Library

基本語法與其他清單頭部類似:
    Import-Library: <librarySymbolicName>;version=<versionRange>
其中 <librarySymbolicName> 是庫的 符號名,而 <versionRange> 是使用 OSGi 版本範圍表示法表示的庫的可接受版本範圍。庫定義指定了庫的符號名和版本,這兩者共同唯一標識了平臺上的庫。

如果你不熟悉 OSGi 版本範圍表示法,目前最常用的形式是最低版本範圍,例如 2,表示 版本 2 或更高,以及 半開 範圍,例如 [2.2.1,2.2.2),表示大於等於 2.2.1 且小於 2.2.2 的任何版本。如果省略了 version=<versionRange>(當然也包括分號分隔符),則預設範圍包含所有版本。

對於每個庫匯入,平臺會選擇具有給定符號名並在平臺倉庫中可用且在給定版本範圍內的最高版本庫。然後,平臺會將該庫匯入替換為一組包匯入,這些包匯入與該庫 Bundle 匯出的所有包匹配。平臺會檢測一個 Bundle 匯入兩個或多個匯出相同包的庫的情況,併發出適當的日誌訊息,然後阻止安裝該匯入 Bundle。

例如,以下頭部匯入了版本在 2.5.4(含)到 2.5.5(不含)之間的某個版本的 Spring Framework 庫

    Import-Library: org.springframework.spring;version="[2.5.4,2.5.5)"

可選庫匯入

你可以使用以下語法來指示庫匯入是可選的。注意特殊的分隔符 :=,它表示一個 指令,用於修改清單頭部的語義,與分隔符 = 不同,後者表示一個 匹配屬性,例如 version
    Import-Library: <librarySymbolicName>;version=<versionRange>;resolution:=optional

如果未指定 resolution,或者指定為 mandatory,則如果不存在具有給定符號名且版本在給定範圍內的庫,包含該匯入庫頭部的 Bundle 將無法安裝。但如果指定了 resolution:=optional,則如果找不到合適的庫,該庫匯入將被忽略。

例如,以下頭部匯入了版本從 2.5 開始的某個版本的 Spring Framework 庫,但如果沒有合適的庫可用,則會被忽略:

    Import-Library: org.springframework.spring;version="2.5";resolution:=optional

匯入多個庫

如果你需要匯入多個庫,則在單個 Import-Library 清單頭部中指定一個逗號分隔的庫匯入列表,示例如下:
    Import-Library: org.foo.p;version="[1,2)",org.bar.q;version="[2,3)"

Import-Bundle

Import-Bundle 是另一種便捷方式,適用於庫只包含單個 Bundle 且建立庫定義不方便的情況。其語法與 Import-Library 非常相似,只不過它引用的是 Bundle 的符號名和版本,而不是庫的。

正如你所預期的,對於每個匯入的 Bundle,平臺會選擇具有給定符號名並在平臺倉庫中可用且在給定版本範圍內的最高版本 Bundle。然後,平臺會將該 Bundle 匯入替換為一組與該 Bundle 匯出的包匹配的包匯入。

例如,以下頭部匯入了 Hibernate 物件關係對映 Bundle:

    Import-Bundle: com.springsource.org.hibernate;version="[3.2.6,3.2.7)"

為什麼不過載 Require-Bundle

如果你熟悉 OSGi,你可能會問自己,為什麼我們不過載 Require-Bundle,而是引入 Import-Bundle

嗯,我們希望 Require-Bundle 保留其標準語義,包括合併拆分包的能力。但我們希望 Import-LibraryImport-Bundle 具有與 Import-Package 相同的底層語義,從而避免拆分包的複雜性。

我們還預計,隨著平臺的不斷發展,我們將需要為 Import-LibraryImport-Bundle 新增更多指令,這些指令不適合新增到 Require-Bundle

接下來是什麼?

平臺 beta 專案正在進行中,我們將認真聽取所有關於平臺特性(包括新清單頭部)的反饋。

對於希望利用平臺頭部,但又需要生成能在其他 OSGi 容器上執行的 Bundle 的使用者,我們計劃提供一個工具,將 Import-LibraryImport-Bundle 語法糖替換為等效的標準包匯入。

我們還將與 OSGi Alliance 的同事討論新的清單頭部,以確定是否存在適合 OSGi 未來解決的通用需求。

訂閱 Spring 簡報

透過 Spring 簡報保持聯絡

訂閱

搶佔先機

VMware 提供培訓和認證,助你加速前進。

瞭解更多

獲取支援

Tanzu Spring 透過一項簡單的訂閱,為 OpenJDK™、Spring 和 Apache Tomcat® 提供支援和二進位制檔案。

瞭解更多

即將舉行的活動

檢視 Spring 社群所有即將舉行的活動。

檢視全部