SpringSource Application Platform 由 OSGi 捆綁包構建,並支援同樣由 OSGi 捆綁包構建的應用程式。該平臺支援 OSGi 的標準特性,但也支援一些額外的 manifest 頭資訊。有些人問過為什麼 SpringSource 添加了專有頭資訊?
和新頭資訊的語義是什麼?
,所以這篇博文解釋了 Import-Library 和 Import-Bundle 的背景動機和語義。
標準 OSGi 捆綁包支援
該平臺基於
OSGi R4.1 標準構建,或者如果您願意,也可以說是基於
JSR 291,並使用
Equinox 作為其 OSGi 實現。因此,您可以使用平臺的工具開發標準 OSGi 捆綁包,並將這些捆綁包部署到平臺上,許多使用者自平臺釋出以來一直在這樣做。
因此,熟悉 OSGi 的開發者可以將平臺用作標準的 OSGi 容器,並受益於平臺的以下特性:
- 能夠使用 Admin Console 或將捆綁包放入平臺的 pickup 目錄來部署捆綁包,
- 診斷,例如解析失敗診斷、應用程式特定跟蹤和自動死鎖檢測,
- 與 Spring 和 Spring Dynamic Modules 的強大整合,適用於希望使用這些框架的開發者,以及
- 從倉庫自動 Provisioning 依賴項。
然而,平臺還旨在讓幾乎或完全沒有接觸過 OSGi 的企業應用程式開發者也能輕鬆受益於 OSGi,這給平臺帶來了一些額外要求。
企業應用程式的額外要求
正如 Sam 最近關於平臺部署選項的
部落格所解釋的,您可以在平臺部署現有的單一 WAR 檔案,無需瞭解 OSGi——平臺會為您處理一切。但要受益於共享庫、共享服務以及最終的 PAR 檔案作用域,有必要將單一的 WAR 檔案拆分成 OSGi 捆綁包。這有多難呢?
嗯,過程中的一些步驟相對容易,特別是如果遵循了良好的軟體工程實踐並且程式碼已組織成服務、領域和基礎設施元件。這些元件可以轉換為捆綁包,它們之間的依賴關係可以使用 META-INF/MANIFEST.MF 中的標準 OSGi Import-Package 和 Export-Package 頭資訊來表達。
更困難的一步是表達對 Spring 和 Hibernate 等企業框架的依賴。使用標準的 OSGi Import-Package 和 Require-Bundle 頭資訊完全可以表達這些依賴關係,如果您的目標是建立可以在其他 OSGi 容器中執行的 OSGi 捆綁包,這正是您應該做的,但這種方法有一些隱藏的成本。
首先,開發者必須精確決定給定框架包含哪些包。僅僅匯入應用程式程式碼使用的包是不夠的,因為當應用程式載入時,一些企業框架會將更多依賴項織入應用程式的位元組碼中。開發者必須透過嘗試和錯誤來發現需要匯入哪些額外的實現包,以確保織入的應用程式能夠正確執行。
然後是框架版本升級的繁瑣工作,因為構成框架的精確包集合可能會發生變化。織入所需的額外包通常不是由公共契約定義的,因此容易發生變化。
此外,由此產生的包匯入並不能正確體現設計意圖,這使得將來維護或擴充套件應用程式更加困難。
我們真的不想將這些負擔強加給使用者,因此我們建立了一些額外的 SpringSource Application Platform 特定的 manifest 頭資訊,Import-Library 和 Import-Bundle,作為表達對企業框架依賴關係的便捷方式。正如您將在下面看到的,這些頭資訊實際上只是語法糖
,它們最終被轉換為標準的 OSGi 包匯入。
Import-Library
基本語法與其他 manifest 頭資訊類似
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>(當然也包括分號分隔符),則預設範圍包含所有版本。
對於每個庫匯入,平臺會選擇具有給定符號名稱且在給定版本範圍內、並且在平臺倉庫中可用的最高版本庫。然後,平臺將庫匯入替換為一組包匯入,這些包匯入與該庫的捆綁包匯出的所有包匹配。平臺會檢測到一個捆綁包匯入了兩個或多個匯出共同包的庫的情況,併發出相應的日誌訊息,然後導致匯入該捆綁包失敗。
所以,例如,以下頭資訊匯入了 Spring Framework 庫 的某個版本,該版本在 2.5.4(包含)到 2.5.5(不包含)之間
Import-Library: org.springframework.spring;version="[2.5.4,2.5.5)"
可選的庫匯入
您可以使用以下語法指示庫匯入是可選的。請注意特殊的
:=
分隔符,它表示一個修改 manifest 頭資訊語義的
指令
,與表示
匹配屬性
(如
version)的
=
分隔符相對。
Import-Library: <librarySymbolicName>;version=<versionRange>;resolution:=optional
如果未指定 resolution,或指定為 mandatory,如果不存在具有給定符號名稱且在給定版本範圍內的庫,則包含 import library 頭資訊的捆綁包將無法安裝。但如果指定了 resolution:=optional,則在沒有合適的庫可用時,該庫匯入將被忽略。
所以,例如,以下頭資訊匯入了 Spring Framework 庫的 2.5 版本或更高版本,但在沒有合適的庫可用時將被忽略
Import-Library: org.springframework.spring;version="2.5";resolution:=optional
匯入多個庫
如果您需要匯入多個庫,請在單個
Import-Library manifest 頭資訊中指定一個逗號分隔的庫匯入列表,如下例所示
Import-Library: org.foo.p;version="[1,2)",org.bar.q;version="[2,3)"
Import-Bundle
Import-Bundle 是一個額外的便利,適用於庫只包含單個捆綁包且建立庫定義不方便的情況。其語法與
Import-Library 非常相似,只是它引用的是捆綁包的符號名稱和版本,而不是庫的。
正如您所預料的,對於每個匯入的捆綁包,平臺會選擇具有給定符號名稱且在給定版本範圍內、並且在平臺倉庫中可用的最高版本捆綁包。然後,平臺將捆綁包匯入替換為一組包匯入,這些包匯入與該捆綁包匯出的包匹配。
所以,例如,以下頭資訊匯入了 Hibernate 物件關係對映器 捆綁包
Import-Bundle: com.springsource.org.hibernate;version="[3.2.6,3.2.7)"
為什麼不過載 Require-Bundle?
如果您熟悉 OSGi,您可能會問自己,為什麼我們不過載
Require-Bundle,而是引入
Import-Bundle。
嗯,我們希望 Require-Bundle 保留其標準語義,包括將拆分包的不同部分合並的能力。但我們希望 Import-Library 和 Import-Bundle 具有與 Import-Package 相同的底層語義,從而避免拆分包的複雜性。
我們還預計,隨著平臺的不斷發展,我們需要向 Import-Library 和 Import-Bundle 新增更多指令,這些指令不適合新增到 Require-Bundle 中。
下一步是什麼?
平臺的
beta 專案正在進行中,我們將聽取關於平臺所有功能(包括新的 manifest 頭資訊)的反饋意見。
對於希望利用平臺頭資訊,但需要生成能在其他 OSGi 容器上執行的捆綁包的使用者,我們計劃開發一個工具來替換 Import-Library 和 Import-Bundle…