用 OSGi 應用程式開發和工作的最佳實踐

CloudSpace發表於2010-09-29
Graham Charters, 高階技術人員, IBM
Jeremy Hughes, OSGi 應用程式架構師, IBM
Tim Mitchell, 軟體工程師, IBM
Alasdair Nottingham, OSGi 應用程式開發主管, IBM
Mark Nuttall, 軟體工程師, IBM
Zoe Slattery, OSGi 技術推廣者, IBM
Tim Ward, OSGi 應用程式 JPA 主管, IBM

簡介: 十多年來,OSGi 技術已經解決了圍繞複雜性、可擴充套件性和可維護性的應用程式開發模組性挑戰。隨著 IBM® WebSphere® Application Server Feature Pack for OSGi Applications and JPA 2.0 的引入,OSGi bundles 構成的企業級 Java™ 應用程式現在可以被開發和部署到 WebSphere Application Server V7。本文介紹了開發構造良好的 OSGi 應用程式的最佳實踐,來幫助您從這個新功能獲取最大效益。 本文來自於 IBM WebSphere Developer Technical Journal 中文版

簡介

OSGi 模組性提供了標準機制來以 Java 應用程式應對共同挑戰。在 2007 年,OSGi Alliance Enterprise Expert Group (EEG) 成立,以一個業務 Java 程式設計模型的形式向業務應用程式開發人員引入 OSGi 基礎設施。OSGi 應用程式和 IBM WebSphere Application Server 企業級服務質量共同為模組化 Web 應用程式提供最完整和最健壯的業務伺服器。您可以使用 WebSphere Application Server Feature Pack for OSGi Applications and JPA 2.0 來部署和管理 Web 應用程式,作為一組版本 OSGi bundle。您也可以配置一個或多個 bundle 儲存庫,作為供應技術設施部分,來承載多個應用程式使用的公共 bundle 和簡化使用這些公共 bundle 的應用程式部署。WebSphere Application Server V7 Feature Pack for SCA V1.0.1.5 升級版新增了對由異構資產組成的 OSGi 應用程式的支援,以支援面向服務體系結構(SOA)概念。(見 參考資料

對於任何新技術都有一些該做的和不該做的建議,對架構師、開發人員和部署人員來說這也稱為最佳實踐。OSGi 技術已經使用了十多年,並且在那時出現了許多最佳實踐。本文介紹了為 OSGi Applications feature of WebSphere Application Server 編寫 OSGi 應用程式和整合 Service Component Architecture (SCA) 相關的主要最佳實踐。其中一些是常用 OSGi 最佳實踐,一些是專用於 WebSphere Application Server 中提供支援的;為了清晰起見,屬於後者的最佳實踐已指明。

本文介紹以下最佳實踐:

  1. 使用 Blueprint
  2. 使用 Blueprint 來啟用基於服務的供應
  3. 使用 Blueprint 來啟用 SCA 整合
  4. 版本是可控的
  5. 從實現中分離 API
  6. 共享服務而不是實現
  7. 好的 bundle 就像構造良好的類:鬆耦合、高聚合
  8. 避免包和 Require-Bundle 分離
  9. 列出 Application-Content 頭中包含的內容
  10. 使用 WAB 而不是 WAR
  11. 必要時只使用 Use-Bundle
  12. 使用持久 bundle 來共享您的永續性單元
  13. 充分利用提供的元件模型
  14. 讓容器擔起重任

每個最佳實踐在後續部分逐一詳細介紹。

1. 使用 Blueprint

一般而言,使用 Blueprint 是一個最佳實踐。Blueprint 提供的一些 Blueprint 支援一個簡易的 POJO 開發,並支援測試模型、簡易元件組裝和基於開發標準的這一事實。在 WebSphere Application Server OSGi 應用程式中使用 Blueprint 是額外推薦的,因為它增強了對容器整合、Service Component Architecture (SCA) 整合和基於服務的供應的支援。

原因如下

Blueprint 是一個基於 Spring Framework 的簡易元件裝配模型,它是被 SGi Alliance 在 Enterprise OSGi R4 V4.2 規範中由 SpringSource 進行標準化的。作為標準化的 Spring,它支援相同的依賴注入模式,使簡單的 Java 元件能夠不使用框架 API 而進行開發,易於進行單元測試。

為 WebSphere Application Server 開發企業級 OSGi 應用程式 一文列出了 WebSphere Application Server 支援 Blueprint 元件模型的原因,這是由客戶需求所激發的。WebSphere Application Serve 中支援的 OSGi 應用程式,其中大多數依賴於 Blueprint 的使用來啟用某一個功能,例如基於服務的配置和 SCA 整合。儘管通常情況下使用 Blueprint 是一個好主意,當開發 WebSphere Application Server OSGi 應用程式時,甚至還有更多的理由這樣做。

2. 使用 Blueprint 啟用基於服務的供應

(專用於WebSphere Application Server)

使用 Blueprint 開發和使用 OSGi 服務時,Blueprint 定義將被用來確保在應用程式配置到 WebSphere Application Server 時滿足服務依賴性。

原因如下

一個 OSGi bundle 清單描述了一個 bundle 從另一個 bundle 匯入的包以及匯出給其他 bundle 使用的包。因此,對於給定一個具體 bundle,為了滿足所有包匯入(包括傳遞依賴),決定一組必須的 bundle 很有可能。一個最佳實踐是將介面和實現分到不同的 bundle(見 將 API 從實現分離)。另一個最佳實踐是使用 OSGi 服務實現依賴(見 共享服務而不是實現))。這兩個最佳實踐的結果就是配置包依賴項僅滿足介面需求,而不能提供實現 bundle。WebSphere Application Server OSGi 應用程式特性通過使用 Blueprint 定義決定一個 bundle 提供和需要的服務來解決這一問題。然後,在應用程式部署期間使用該資訊從應用程式歸檔檔案(.eba)或內部 bundle 儲存庫(一個配置和管理 WebSphere Application Server 管理的 bundle 儲存庫)提供實現。

示例


圖 1. 基於供應示例的服務
圖 1. 基於供應示例的服務

圖 1 顯示了一個 API bundle、一個提供服務的實現和一個使用來自實現 bundle 的客戶端 bundle。客戶端 bundle 和實現 bundle 在 API bundle 上都有一個包依賴項(package dependency),其中含有服務 Java 介面。客戶端 bundle 是 OSGi 應用程式(在內容中列出)的一部分,API 和實現 bundle 是共享的,都是來自於內部 bundle 儲存庫。API bundle 將配置到 WebSphere Application Server 作為包依賴的結果,如果客戶端 bundle 和實現 bundle 都是使用 Blueprint 實現的,那麼實現 bundle 也將被配置。如果這兩個 bundle 中有一個不使用 Blueprint,那麼將不配置實現 bundle 。

如果,實現 bundle 使用 Blueprint,而客戶端不使用,那麼也提供應用程式,但是沒有實現 bundle 。這是因為供應程式沒有意識到客戶端 bundle 缺失服務依賴。如果客戶端 bundle 使用 Blueprint 但是實現 bundle 沒有使用,那麼應用程式將不能部署,因為供應程式不能滿足客戶端 bundle 服務依賴。

3. 使用 Blueprint 來啟用 SCA 整合

(專用於 WebSphere Application Server)

開發整合其他技術的 OSGi 應用程式時,一個最佳實踐(實際上是必不可少的)是使用 Blueprint 來定義 OSGi 應用程式的服務實現。

原因如下

在 WebSphere Application Server 中,SCA 用於聚合 OSGi 應用程式和其他應用程式型別(例如,Java EE)。它也用來通過各種傳輸和協議(例如 JMS、Web services、Atom)公開 OSGi 應用程式服務,也可使服務依賴通過這些傳輸和協議進行呼叫。要做到這一點,SCA 需要一個由 OSGi 應用程式提供的服務描述和應用程式需要的服務。SCA 重新使用 Blueprint XML 中描述的資訊來推出 SCA 所需的資訊。因此將 OSGi 應用程式公開到 SCA 時,必須使用 Blueprint。如果一個 OSGi 應用程式包含使用 Blueprint 時沒有定義的服務,那麼必須建立向 Blueprint 描述這些服務的 Blueprint 虛包,然後將請求轉發給非 Blueprint 服務實現。


圖 2. Blueprint 支援 SCA 整合
圖 2.  Blueprint 支援 SCA 整合

圖 2 顯示了一個 OSGi 應用程式和一個 bundle,提供了一個由 SCA 從應用程式外部呼叫的服務。這個呼叫來自另一個元件(似乎是另一個 OSGi 應用程式或一個 Java EE 應用程式),或者來自一個特定的繫結,例如一個 Web 服務或 JMS 呼叫。同一個 bundle 也需要一個由 SCA 提供的外部應用程式服務。這可能再一次呼叫另一個 SCA 元件,或在一個特定的繫結上進行一次呼叫。在圖 2 的第一個圖表中,Bundle A 是使用 Blueprint 實現的。SCA 使用 Blueprint 服務定義來了解如何分辨和呼叫目標 OSGi 服務。SCA 不使用 Blueprint 服務參考定義,因為在應用程式清單(用於定義一個 OSGi 應用程式的構件)中有充足資訊來確定什麼樣的服務是有效目標。在第二個圖表中,服務和參考資料在 Bundle B 中使用其他元件模型實現,例如,宣告服務(DB)。SCA 不能理解 DS,因此這是無效的。在第 3 個圖表中,一個 Blueprint 虛包 bundle(Bundle C )用於向 SCA 描述服務,然後將呼叫轉發到 Bundle B 中的 DS 服務實現。

4. 版本是可控制的

為 bundle 和包提供語義版本來啟用 API 客戶端和提供商之間的鬆耦合。

原因如下

在 OSGi 中,包和 bundle 是版本可控制的,如果沒有指定版本,則預設為 0。包和 bundle 應該是從語義上進行版本庫控制的,便於客戶端進行自我保護,以防 API 更改而導致中斷。

語義版本控制(semantic versioning)定義了一個版本控制模式,使其可以向客戶端傳遞版本相容資訊。語義版本控制 使用主版本號.次版本號.微版本號(major.minor.micro)的編號模式。主版本號、次版本號和微版本號都是自然數(0,1,2,等等)。

  • 在主版本中的改變表示一個二進位制不相容 API 改成客戶端 API;這就是說,在版本 2 中,API 中進行的客戶端編譯需要在版本 3 中重編譯才能生效。一個二進位制不相容改變的示例是刪除一個介面或方法。
  • 在次版本中的改變表示 API 已被增強,但是現有客戶端不需要被重新編譯。這類改變的一個例子就是新增一個介面或一個方法。
  • 在微版本中的改變表示已進行了一個改變但是不需要改變 API。這類改變的示例是一個刪除 NullPointerException 的 bug 修復。

API 以這種方式控制版本,使客戶端可以將 API 版本限制到其可用的範圍。當匯入一個包或需要一個 bundle 時,客戶端可以要求他們想要的版本。例如(1,2)允許客戶端使用版本 1 的包或 bundle 進行工作,而不能使用版本 2 的包或 bundle。這是因為客戶明白,將來的變更可能是一種使用主版本的二進位制相容變更。

如果語義版本控制用於 bundle 和包,那麼在執行時可能立刻會有 API 的多個版本,且和 bundle 同時使用。

到目前為止,最佳實踐已經介紹了關於 API 客戶端的相容性。當遵守以下 從實現中分離 API 最佳實踐時,API 的實現將在一個獨立的 bundle 中,也可從版本控制中獲益。對於一個實現者(implementor),主版本或次版本中的改變表示一個二進位制不相容改變,例如,它們可能需要實現一個新介面或方法。(使用語義版本控制的額外優勢和注意事項,見 OSGi Alliance Semantic Versioning Technical Whitepaper。)

示例

圖 3 顯示了一個 API 的兩個提供商,其中 API 是同一版本。在 OSGi 中,這兩個提供商被認為是相同的。客戶端(左邊)和實現者(右邊)都表達了一種依賴,將匹配各自 API 提供商。


圖 3. 客戶端和實現者可以任意使用兩個相同版本的包
圖 3. 客戶端和實現者可以任意使用兩個相同版本的包

圖 4 顯示了不同版本中的兩個 API 提供商。也顯示了次版本中的一個改變如何分別影響客戶端和實現者。既然這樣,您可以提供 API 的1.0 版本和 1.1 版本。客戶 A 可以使用任何一個 API 提供程式;客戶 B 使用新 API 方法,可使用最高版本 1.1。Implementation A 使用 bundle API A 提供的 API,而 Implementation B 只使用 bundle API B 提供的 API 。

服務登錄檔將對請求者可見的服務限制在執行與包繫結的 API 版本的那些請求者範圍內,知道這一點很重要。


圖 4. 一個次要 API 版本改變如何不同程度地影響客戶端和實現
圖 4. 一個次要 API 版本改變如何不同程度地影響客戶端和實

圖 5 從客戶和實現者角度顯示了一個不相容的 API 改變。既然這樣,Implementation A 和 Client A 被繫結到 API A,Implementation B 和 Client B 繫結到 API B。


圖 5. 一個主要 API 版本改變如何同樣影響客戶端和實現
圖5.  一個主要 API 版本改變如何同樣影響客戶端和實

5. 從實現中分離 API

構建 OSGi bundle,一個最佳實踐是將任何 API 類從實現類放入單獨的 bundle。

原因如下

從實現類分離 API 類比較靈活。一個單獨的 API bundle 使一個客戶端可以使用任何實現提供商;此外,也可使多個提供商同時使用。如果沒有這個分離,客戶端 bundle 需要重啟來連線一個新提供程式 bundle。

該最佳實踐也可以減少 API bundle 包依賴性,因而減少不同 API 之間的迴圈依賴性。當兩個 API 實現內部彼此使用 API 時,這很容易發生。

一旦 API 類放入一個獨立的 bundle,確保沒有實現包被匯出是很重要的,API 實現是使用公共 API 作為 OSGi 服務釋出的。

示例

圖 6 中的場景展示了一個客戶端 bundle,從一個沒有分離這些包的提供商 bundle 匯入 API 類和實現類,這個提供商 bundle 也匯出實現類和 API 類。這是一個糟糕的實踐,因為沒有分離,也因為實現類被匯出且不能作為服務公開。


圖6. 設計糟糕的提供商 bundle ,其中 API 類和實現類是在同一 bundle 中
圖 6. 設計糟糕的提供商 bundle ,其中 API 類和實現類是在同一 bundle 中

另一個場景如圖 7 所示,其中顯示了一個客戶端,從一個 bundle 中匯入 API 包,從另一箇中匯入實現包。儘管 API 包和實現包已被分離,提供商仍然匯出實現包,而不是作為服務公開該類。


圖 7. 設計糟糕的提供商 bundle,其中 API 類和實現類已被分離
圖 7.  設計糟糕的提供商 bundle,其中 API 類和實現類已被分離

最後一個場景如圖 8 所示,顯示了最佳實踐。提供商 bundle 已經分離了實現包和 API 包,此外,實現類也被作為 OSGi 服務公開。


圖 8. 設計良好的提供商 bundle,其中 API 類和實現類已被分離
圖 8. 設計良好的提供商 bundle,其中 API 類和實現類已被分離

6. 共享服務而是不實現

開發 OSGi bundle 時,一個最佳實踐是使用 OSGi 服務登錄檔來實現工廠模式,並用其他 bundle 構造匯出類。除了 從實現中分離 API 最佳實踐之外,還實現了一個客戶端、API 和 API 實現的鬆耦合。

原因如下

分離 API 和實現的實踐從消耗資源的客戶端抽象了多個實現。這使得一個供應商的 API 實現可以被另一個替換,且不需要變更使用 API 的客戶端。

想要行之有效,對於 API 和實現的分離,客戶端需要一個機制來獲取一個實現例項,而無需知道將要使用哪個實現。在非 OSGi 系統中,這通常是由一個 API 類中的靜態工廠方法來完成的。靜態工廠方法從類路徑上的實現中選擇要例項化哪個實現。由於平面類空間的所有這些實現都是可視的。用於決定哪個類應該被例項化的演算法是特定於 API 的。

這個機制在 OSGi 中不能正常工作,因為含有 API 類的 bundle 不能看到任何含有 API 實現的 bundle。

OSGi 以 OSGi 服務登錄檔的形式提供一個更為整潔的解決方案。Bundle 含有以下 API 實現之一:

  • 在 OSGi 服務登錄檔中註冊一個 API 介面例項。這可以產生這種效果,即所有客戶端 bundle 僅使用一個實現例項。
  • 在 OSGi 服務登錄檔中註冊一個 OSGi ServiceFactory 介面。ServiceFactory 介面在請求一個服務例項時為 OSGi 所用。在這種情況下,ServiceFactory 可以選擇例項來返回到每個請求客戶端 bundle。例如,可以向所有的請求客戶端 bundle 返回同一個例項,或可以返回不同的例項,或兩者兼而有之。

客戶端是完全從實現中分離出來了,正如在靜態工廠方法模式中那樣。此外:

  • API 是完全從所有實現中分離出來了,正如 OSGi 服務層與實現合作應對例項建立。
  • 用於決定哪個實現將要返回的演算法是貫穿所有 API 的而不是特定於某一個 API,就像在非 OSGi 系統中使用的靜態工廠方法模式那樣。

OSGi 服務登錄檔是一個共享服務的動態機制,其中服務可以在執行時自由來往。在服務可用時,Blueprint 通過將一個請求服務注入客戶端 bundle 的 bean 中,幫助客戶處理這種動態性。服務不可用時,Blueprint 可以注入另一個實現(如果有一個已註冊的)。

7. 好的 bundle 就像構造良好的類:鬆耦合、高聚合

編寫 bundle 時,將功能限制到一個特定的任務。將一個 bundlle 執行的任務數量降到最低,嘗試儘量少的依賴。

原因如下

編寫一個 OSGi 應用程式時,總想向一個已有 bundle 新增更多的功能,而不是去編寫一個新的 bundle 。然而,這很快就會產生一個大且聚合性低的 bundle,通常會導致 bundle 匯入和匯出大量的包。使用 Require-Bundle 頭部隱藏大量包匯入並不是一個好的實踐,因為這只不過是在 bundle 之間新增緊耦合。隨著 bundle 依賴圖的增長,必須下載和安裝的 bundle 數量不斷增多,通常成指數增長。這稱之為 “hairball 效應” 。一個設計不佳的 bundles 需要在您的應用程式中下載並安裝數十甚至上百兆,而僅僅能訪問一到兩個簡單的功能,而一個高聚合的 bundle 只有極少的依賴性,一個很小的依賴樹,而且更容易在其他應用程式中重用。需要注意該最佳實踐為面向類設計物件映象最佳實踐,這是因為同樣的理由。一個 OSGi bundle 應該是封裝良好、高度聚合和鬆耦合的 — 就像一個格式良好的類 — 防止其依賴特定版本的其他 bundle 或類,並增加重用機會。

示例

一個設計不佳的示例是一個登入實現,使使用者可以登入檔案系統、通過 JPA 登入資料庫,或登入一個訊息佇列(圖 9)。為了使用檔案系統 logger,一個應用程式需要載入訊息 API、JAP API,等等。因此,應用程式含有 logger 實現類,但並不使用。一個比較好的解決方案是登入介面和各個實現相互分離(圖 10)。通過這種方式,應用程式可以選擇包含一個它使用的登入 bundle ,而不包括額外實現類和任何進一步實現,這些都出現在登入 bundle 中。


圖 9. 一個不良設計系統,其中一個 bundle 提供一個 API 的多個實現
圖 9.  一個不良設計系統,其中一個 bundle 提供一個 API 的多個實現

圖 10. 一個設計良好的系統,其中一個 API 的每個實現都由一個獨立的 bundle 提供
圖 10. 一個設計良好的系統,其中一個 API 的每個實現都由一個獨立的 bundle 提供

8. 避免拆分包和 Require-Bundle

開發 OSGi bundle 時,一個最佳實踐是將一個包的所有類放在一個 bundle 中。將一個包分成多個 bundle 會導致使用 Require-Bundle,從而損害使用這些 bundle 的系統的可擴充套件和維護程度。

原因如下

模組化系統由鬆耦合、高聚合模組組成,每個模組執行一個相關的邏輯功能,它們之間的界限有明確的定義。該設計目標將生成易於維護和擴充套件的系統。OSGi bundle 是包上的模組化。

OSGi 通過每個 bundle 宣告其提供鬆耦合的模組,這在 OSGi 術語中稱為 bundle 。這是滿足執行時 bundle 提供的包的。一個 bundle 不能直接依存於一個提供相關性的 bundle;至於哪個 bundle 提供相關性由 OSGi 執行時決定,根據部署到框架的 bundle 而定。結果是提供一個特殊包的 bundle 能夠被提供相同包的不同 bundle 所取代,而不改變依賴那個包的 bundle。

當在同版本中一個包被兩個 bundle 匯出時,將出現一個拆分,每個 bundle 提供的類集合都不相同。類可能被複制,或者更常見的是,一部分包在一個 bundle 中,而另一部分在另一個 bundle 中。


圖 11. 拆分包的使用者需要使用 Require-Bundle 頭部
圖 11.  拆分包的使用者需要使用 Require-Bundle 頭部

圖 12. 從一個 bundle 中匯出的包保持高 bundle 聚合
圖 12. 從一個 bundle 中匯出的包保持高 bundle 聚合

在執行時,OSGi 將用另一個 bundle 的匯出滿足 Import-Package 頭部指定的 bundle 的依賴性。即使這不只一個 bundle 匯出包,也只有一個用於滿足這種依賴性。Bundles 是 “連線” 在一起的。僅存於其他匯出相同包的 bundle 中的類(匯入 bundle 的包沒有被連線在一起)對於匯入 bundle 的類載入器是不可見的。如果匯入 bundle 在執行時試圖使用那些類其中之一,將丟擲一個 ClassNotFoundException。因為在 OSGi 中的匯入/匯出 bundle 在每個包基礎上只提供一種方法將一個 bundle 連線到另一個 bundle,另一個機制則需要將一個 bundle 連線到多個 bundle, 在這種拆分包的情況下工作,這可以通過使用 Require-Bundle 頭部和指定輸出所需包的 bundle 符號名來完成。

然而,這加強了 bundle 之間的耦合:

  • 客戶端現在依賴兩個(或更多)bundle 提供包。
  • 所有通過提供 bundle 匯出的包現在對客戶端是可見的,並且當客戶端 bundle 開發一段時間以後,它將開始依賴其他包。
  • 提供包的 bundle 不再被另一個包替換,只是使用了一個不同的符號名,對所有需要他的客戶端 bundle 沒有更改。

將包分離成多個 bundle 從而強迫使用 Require-Bundle 頭部使應用程式更緊密的耦合,維護和擴充套件的費用也因此更為昂貴。如果您有一個包可以分成兩個 bundle,而您有不需要這兩部分有高聚合性,那麼您應該使用兩個不同的包。

9. 在 Application-Content 頭部列出包含的內容

(專用於 WebSphere Application Server)

在 Enterprise Bundle Archive (EBA) 檔案中包含的 Bundle 應該被列在 EBA 的 META-INF/APPLICATION.MF 的 Application-Content 頭部。相關 bundle 最好儲存在 bundle 儲存庫中,它們可用於所有的應用程式。

原因如下

在 EBA 中的 bundle 能影響應用程式的配置方法,但是隻要配置正在執行,那麼 bundle 對其他的應用程式是不可見的。如果 EBA 含有一個或多個依賴項 ,而在 Application-Content 中沒有列出,那麼這些最終可能載入到一個或多個的伺服器共享 bundle 空間,作為配置的結果。這會引起潛在的問題,因為其他應用程式最終被連線到這些依賴,即使它們不依靠這些依賴來配置。在執行時這將導致意外的改變,而且很難檢測出。

除了包含的內容應該被列在 Application-Content 頭部這個事實外,您也應該充分意識到包含內容的功能主要為了方便您的開發而提供的 — 不推薦用於生產環境中,這個由應用程式內容組成的 bundle 可以包含在 EAB 中,也可以包含在一個 bundle 儲存庫中。生產環境鼓勵 — 並經常強調 — 應用程式從一個 bundle 庫中獲取所有 bundle。這使得管理適用於生產 bundle 儲存庫,作為應用程式程式碼的唯一來源。

對於 EBA 將自動生成 APPLICATION.MF,假如沒有的話。Application-Content 將被解釋為在 EAB 根目錄下包含的每個 bundle。每個 bundle 的版本範圍將被鎖定到其精確版本,在這種情況下,沒有 bundle 能被其他應用程式共享、或部署之後升級。不能訪問整合開發環境的 IBM Rational® Application Developer(它提供更為直接的整合)的開發人員可能不會發現,它在沒有 APPLICATION.MF 的情況下包裝應用程式和匯入一個含有升級內容的新應用程式資產是很方便的,而不用將每個新 bundle 安裝到內部 bundle 儲存庫。當然,APPLICATION.MF 應該在開發週期結束之前建立。通常情況下,當您從開發通過測試移到生產環境中時,包含的內容不是很常見。在測試環境中,建立一個包含所有依賴項的 EBA 是很方便的,因此它可被安裝而不用考慮目標目標伺服器 bundle 儲存庫的狀態。而對於生產環境中依據上述這些理由進行操作是不明智的。

10. 使用 WAB 而不是 WAR

開發企業級 OSGi 應用程式時,建立一個 Web 應用程式 bundle (WAB),不依賴 WAR 進行 WAB 轉換。

原因如下

WebSphere Application Server 的 OSGi 應用程式功能自動將一個 WAR 檔案轉換成一個 OSGi bundle,就是所謂的 Web 應用程式 bundle。在剛開始學習 OSGi 時,這個功能是很有用的,但是自動轉換阻礙了某個功能的使用。因而,在開發過程中,WAR 檔案應該被轉換成一個 bundle 部署到生產系統中。

使用 OSGi bundle 時,應用程式可能顯示一系列支援的 bundle。這樣一來,不需要重新部署整個應用程式單獨的 bundle 也可以更新。該功能取決於模組所擁有的一個已知特性,並不是自動轉換,其中特性是在部署時生成的。

當一個 WAR 被轉換成一個 WAB 時,包匯入生成不可控的版本。結果,包可能撿起所需版本的二進位制不相容版本。如果一個包的版本 2 和版本 3 都可用,不可能阻止 WAR 使用版本 3。如果WAR 需要版本 2,而對版本 3 是不相容的,這將導致執行時錯誤。

11. 必要時只使用 Use-Bundle

(專用於 WebSphere Application Server)

建立一個 WebSphere Application Server OSGi 應用程式時,一個最佳實踐是僅在共享場景 bundle 的一個特定子集中指定 Use-Bundle 應用程式清單頭部。

原因如下

WebSphere Application Server Use-Bundle 應用程式清單頭部提供一種方法,可以引導應用程式部署程式在匯出相同包的其他 bundle 上執行一個 bundle。

在執行時,OSGi 應用程式彼此隔離,但是他們的依賴項是共享的。在 OSGi 應用程式的 Application-Content 頭部指定的 bundle 執行在它們自己獨立的 OSGi 框架中。依賴項執行在伺服器的共享 bundle 空間。

在 OSGi 應用程式部署過程中依賴項被確定。在部署過程中,應用程式 bundle 匯入的包匹配應用程式或配置的 bundle 儲存庫中的 bundle 匯出的包。如果 bundle 儲存庫中需要一個 bundle,將它提供給伺服器。由於包的版本約束,兩個應用程式被部署和配置來執行是可能的,其中不同 bundle 在共享 bundle 空間中提供相同的包。如果兩個應用程式需要使用類例項(來自所涉及的包)互相互動,這就變成了一個問題。如果是的話,那麼 Use-Bundle 應用程式清單頭部應該用於確保兩個應用程式是連線到同一個 bundle 的。

指定 Use-Bundle 來實現應用程式之間的依賴項共享不是必須的。只需要在兩個應用程式需要共享一個類的同一版本時,確保它們被連線到同一個提供的 bundle。

示例

例如,一個含有 Bundle X 的應用程式被部署,從 1.0 到 2.0(不包括 2.0)版本中匯入一個 包 org.hal.a(圖 13)。根據 OSGi 語義版本控制,這意味著 X 可以連線到任何匯出 org.hal.a 的 bundle,而不會中斷 API 更改,從使用者角度使用 API。此時,只有一個 bundle API.A 匯出 org.hal.a,在 1.0 版本中也是這樣的。因此 Bundle X 被連線到 API.A。Bundle X 因此可以看得見 org.hal.a 版本 1.0 的實現。Implementation.A 是這類實現之一。


圖 13. OSGi 將 Bundle X 連線到 API.A 來在可接收 Bundle X 的版本上提供 org.hal.a
圖 13. OSGi 將 Bundle X 連線到 API.A 來在可接收 Bundle X 的版本上提供 org.hal.a

然後,部署一個含有 Bundle Y 的應用程式(圖 14)。Bundle Y 從 1.5 到 2.0 版本(但不包括 2.0)匯入 org.hal.a 包。API.B 也被部署,在版本 1.5 中匯出包 org.hal.a。Bundle Y 不能連線到 API.A ,因為它匯入一個新的 org.hal.a 版本,但它 可以 被連線到 API.B。Implementation.B bundle 提供了一個 org.hal.a 1.5 版本的實現,從 1.5 到 1.6 版本(但不包括 1.6)匯入。OSGi Alliance Semantic Versioning Technical Whitepaper 對這一原因進行詳細的討論。


圖 14. OSGi 將 Bundle Y 連線到 API.B 來在可接收 Bundle Y 的版本上提供 org.hal.a
圖14. OSGi 將 Bundle Y 連線到 API.B 來在可接收 Bundle Y 的版本上提供 org.hal.a

然後,部署第 3 個應用程式,其中含有 Bundle Z。Bundle Z 從與 Bundle X 相同的版本匯入 org.hal.a 包:從版本 1.0 到版本 2.0 但不包括 2.0(圖 15)。Bundle Z 的編寫者想要它使用 Implementation.A,但是由於 API.B 是在版本 1.5 上匯出 org.hal.a 的(這處在 Bundle Z 的匯入範圍中),Bundle Z 能被連線到 API.B,隨後只能看到 Implementation.B。


圖 15. Bundle Z 被連線到 API.B,因此不能看到 Implementation.A
圖 15.  Bundle Z 被連線到 API.B,因此不能看到 Implementation.A

為了確保 Bundle Z 連線到 API.A,Bundle Z 應該指定一個 Use-Bundle 應用程式清單頭部:API.A,然後,Bundle Z 將獲得對 Implementation.A 的可見性,而不是 Implementation.B。這就是作者想要的(圖16)。


圖 16. Bundle Z 被連線到 API.A,現在可以看到 Implementation.A
圖 16. Bundle Z 被連線到 API.A,現在可以看到 Implementation.A

12. 使用持久 bundle 來共享您的永續性單元

在應用程式中使用 JPA 時,將 bundle 包裝成一個 OSGi 永續性 bundle。如果您的客戶不直接使用 EntityManagerFactory,在 OSGi 服務儲存庫中公開一個資料訪問服務,而不是從永續性 bundle 匯出實體類。

原因如下

WebSphere Application Server OSGi 應用程式特性支援符合OSGi JPA 服務規範的 OSGi 永續性 bundle。通過在一個永續性 bundle 中包裝 JPA 管理的類和 persistence.xml,用易共享的 JPA 資源(在 WebSphere Application Server OSGi 執行時使用的)和不可管理的 JPA Service 實現建立一個可重用模組是有可能的。持久 bundle 也可被大量基於永續性客戶端的 OSGi 共享,但不包括永續性客戶端程式碼,這兩個都是高度聚合和易於重用的。記得指定 Meta-Persistence bundle 清單頭部,永續性描述符可以在包內給出任意路徑。

如果一個永續性單元被包裝在一個遺留 WAR 中,它應該被開啟作為一個單獨的 OSGi bundle。在 WAR 中的永續性單元由 Java EE 容器管理,並且在 OSGi 框架內不能共享。同樣的,不能被 OSGi 應用程式中的其他 OSGi bundle 所用。從 WAR 中刪除永續性單元不是可能的,記住它是不能通過 Blueprint 容器或 OSGi 服務儲存庫訪問的。

從其他應用程式程式碼中分離永續性 bundle 的另一個原因由 前面的一個最佳實踐 來解釋。如果幾個應用程式需要在一個資料庫中訪問資料,重用同一個實體對映是有必要的。這有助於防止資料庫中的資料損壞,因為兩個應用程式共享同一資料對映,而只有一個 bundle 需要針對未來的模式更改而更新。如果永續性單元不能包裝在一個較大的 bundle 中,那麼實體將不再易於多個應用程式共享,需要程式碼副本和維護成本。

13. 充分利用提供的元件模型

(專用於 WebSphere Application Server)

在 OSGi、WebSphere Application Server OSGi Applications 功能和 SCA 之間提供一系列不斷增長的粗粒度元件模型:

  • 使用 Blueprint 在 OSGi bundle 中定義細粒度元件。
  • 將 OSGi 應用程式定義為 bundle 組合,這些 bundle 提供一個特定應用程式功能。
  • 使用 SCA 公開服務,從一個 OSGi 應用程式到另一個,並向常規 Java EE 應用程式提供服務。

原因如下

使用 Blueprint 來管理細粒度元件以及與其他 OSGi Bundle 的互動。保持您 bundle 的聚合性(通常很小)並使 您的依賴項保持鬆耦合性。高聚合和鬆耦合的原則繼續適用,因為 bundle 被分組到業務 bundle 應用程式。

OSGi 應用程式可以認為同傳統 Java EE 企業級應用程式大致相同。在組裝應用程式時,記住 WebSphere Application Serverkeep 將 Application-Content 列出的 bundle 部署到自己的獨立的框架。所有應用程式的依賴 bundle 被部署到一個共享 bundle 空間。因此鼓勵將通用程式碼庫和服務分解到共享的依賴 bundle 。這節省了記憶體,增加了可重用性,並有助於系統的管理。在一個給定 OSGi 應用程式 Application-Content 中列出的 bundle 僅由該應用程式中獨有的永續性、業務和顯示邏輯組成。

有兩個方法可以將工作深入到 OSGi 應用程式,第一個是通過 HTTP 深入到 WAR,第二個是呼叫一個 Application-ExportService 頭部列出的服務;這麼做需要使用 SCA。

SCA 是最粗粒度的可用元件模型,它提供一個分散式程式設計模型並強迫使用值傳遞語義。使用 SCA 從一個 OSGi 應用程式向另一個公開服務,並在常規 Java EE 程式之間提供服務。Rational Application Developer 8 提供工具支援構建和整合 OSGi 應用程式作為 SAC 元件。

14. 讓容器擔起重任

(專用於 WebSphere Application Server)

使用容器服務來構建應用程式並提企業品質服務而不是編寫程式碼來管理服務。

原因如下

應用程式程式設計師經常希望以一種健壯可信的方式解決複雜的問題,並希望信任業務服務,例如事務和託管的資源,來為他們提供他們想要的服務質量。然而,向容器提供這些服務的完全控制權通常有點勉強,這無疑有損靈活性並失去控制。結果是許多應用程式選擇管理自己的資源和週期。

控制反轉,特別是依賴注入,是極為強大的簡化應用程式開發工具,但它們依賴一個事實:容器能負責週期和資源管理。向 Blueprint bean 中的所有方法應用宣告事務需要一行 XML,然而在每個方法中需要多行程式碼來在應用程式中完成同樣的結果。類似地,對於資源管理,使用一個 Blueprint 資源引用可以將資源直接注入應用程式,也可以使其可以使用安全證照進行配置,以至於資源沒有必要在應用程式中提供。如果一個開發人員選擇定位自己的資源,容器就沒有機會驗證應用程式,因此證照被儲存在應用程式中,而且,如果證照過期應用程式必須被更改。

示例

bundle 提供一個基於 JPA 的資料訪問服務,但是不使用任何容器服務。它必須手工管理事務、EntityManager 週期和服務儲存庫。這將需要大約 100 行程式碼。需要多個 try/finally 塊整理資源。客戶端必須使用 OSGi API 來檢視服務,這需要更多的週期管理和 try/finally 塊。由於不使用 Blueprint 註冊和使用服務,應用程式也必須手工處理服務不可用的錯誤案例。這也不是自動基於服務配置的。

通過使用宣告性事務、Blueprint 和 管理 JPA,應用程式在大小上有所減少,減少了數百行復雜的週期和錯誤管理程式碼。這也減少了應用程式潛在缺陷的數量。這個應用程式,以及將來所有使用資料訪問服務的應用程式,都可以利用基於服務的依賴配置優勢。

本文描述了構建 OSGi bundle 和 WebSphere Application Server OSGi 應用程式來實現 bundle 之間的高聚合和鬆耦合的最佳實踐。通過採用這些最佳實踐,您可以使用 Feature Pack for OSGi 應用程式和 JPA 2.0 來建立更加可維護和可擴充套件的應用程式,這些應用程式所用的是執行在 WebSphere Application Server V7 中的技術。

原文連結:http://www.ibm.com/developerworks/cn/websphere/techjournal/1007_charters/1007_charters.html

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/14789789/viewspace-675103/,如需轉載,請註明出處,否則將追究法律責任。

相關文章