SpringSource 應用平臺由 OSGi bundle 構建而成,並支援同樣由 OSGi bundle 構建的應用程式。該平臺支援 OSGi 的標準功能,但也支援一些額外的清單頭。有些人問過 為什麼 SpringSource 添加了專有頭?
以及 新頭的語義是什麼?
,因此本文解釋了背景動機以及 Import-Library 和 Import-Bundle 的語義。
標準 OSGi Bundle 支援
該平臺基於
OSGi R4.1 標準構建,如果您願意,也可以說是
JSR 291,並使用
Equinox 作為其 OSGi 實現。因此,您可以使用平臺的工具開發標準 OSGi bundle,並將這些 bundle 部署到平臺上,許多使用者自平臺釋出以來一直在這樣做。
因此,熟悉 OSGi 的開發者可以將平臺用作標準的 OSGi 容器,並受益於平臺的功能,例如
- 能夠使用 Admin Console 或透過將 bundle 放入平臺的 pickup 目錄來部署 bundle,
- 診斷功能,例如解析失敗診斷、應用程式特定跟蹤和自動死鎖檢測,
- 與 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-Library 和 Import-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,則在沒有合適的庫可用時,該庫匯入將被忽略。
因此,例如,以下頭匯入 Spring Framework 庫 2.5 或更高版本,但在沒有合適的庫可用時將被忽略
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 保留其標準語義,包括合併拆分包(split package)各個部分的能力。但我們希望 Import-Library 和 Import-Bundle 具有與 Import-Package 相同的底層語義,以避免拆分包帶來的複雜性。
我們還預計,隨著平臺的不斷發展,我們需要在 Import-Library 和 Import-Bundle 中新增更多指令,而這些指令不適合新增到 Require-Bundle 中。
下一步是什麼?
平臺
beta 專案 正在進行中,我們將聽取所有關於平臺功能的反饋,包括新的清單頭。
對於希望利用平臺頭部,但需要生成能在其他 OSGi 容器上執行的 bundle 的使用者,我們計劃開發一個工具來替換 Import-Library 和 Import-Bundle...