基於OSGi和Spring開發Web應用
作為一個新的事實上的工業標準,OSGi 已經受到了廣泛的關注, 其面向服務(介面)的基本思想和動態模組部署的能力, 是企業級應用長期以來一直追求的目標。Spring 是一個著名的輕量級J2EE 開發框架,其特點是面向介面程式設計和非侵入式的依賴注入。將 OSGi 和 Spring 結合能充分發揮二者各自的特長,更好地滿足企業級應用開發的需求。Spring 開發組織在 2008 年釋出了將 OSGi 和 Spring 結合的第一個版本:Spring-DM。本文通過一個簡單例項,介紹如何利用 Spring-DM 開發基於 OSGi 和 Spring 架構的 Web 應用,同時探討其中用到的關鍵技術及其基本思想。
開發一個簡單的OSGi Web 應用例項——一個簡單的 Web 應用
我們寫一個簡單的 Web 應用 compute.html :計算兩個數字的和或乘積。如下圖所示:
圖 1. 一個簡單例子
為了體現 OSGi bundle 的動態部署能力,我們寫兩個 service bundle,其中一個計算兩個數字的和(稱為 add bundle),另外一個計算兩個數字的積(稱為 multiply bundle)。 當我們點選“Compute”按鈕的時候,如果此時 add bundle 被部署,則頁面將返回兩個數字的和,否則如果此時 multiply bundle 被部署,則頁面將返回兩個數字的積。
開發環境準備
- 下載 Eclipse 3.4
- 獲取所有 OSGi, Equinox 和 Spring 的外掛, 如下圖所示:
圖 2. 相關外掛列表
- 開啟 Eclipse, 設定 target platform 為上述外掛集合
基本模組設計
該應用主要包含兩個層次: 服務層和 Web 層。Web 層基於 Spring-MVC 實現,包含處理 Web訪問相關的 bundle(本例中只有一個)。服務層包含處理數字計算的 bundle,本例中包含一個宣告服務介面的 compute interface bundle 和兩個實現該服務介面的 bundle :add bundle 和 multiply bundle。基本模組結構如下圖所示:
圖 3. 基本框架
模組程式實現
Step 1 :實現 Service Layer
服務層的三個 OSGi bundle 實現完畢之後如下圖所示 :
圖 4. 服務層
服務層.bmp
其中 com.zxn.example.service.compute 是宣告服務介面的 bundle。com.zxn.example.service.compute.add和com.zxn.example.service.compute.multiply 是實現了服務介面的兩個 bundle。
- com.zxn.example.service.compute
宣告一個 Compute 介面,其中包含一個介面方法 computeNums(),如下圖所示 :
圖 5. 服務層介面 bundle
- com.zxn.example.service.compute.add
bundle com.zxn.example.service.compute.add 的基本程式結構如下圖所示:
圖 6. 介面實現 bundle :add
在該 add bundle 中,新增一個 Add 類,實現 Compute 介面,如下圖所示:
圖 7. 介面實現程式碼 :Add 類
注意到我們在 META-INF 下建了一個 spring 目錄,並且添加了一個 computeAdd-context.xml 檔案。系統啟動時,Spring 將利用該 xml 檔案建立一個 bean 例項,並把該 bean 輸出為一個 OSGi service,如下圖所示 :
圖 8. Spring 宣告檔案 :computeAdd-context.xml
該xml檔案中,osgi : service是 Spring-DM 輸出 OSGi service 的標記,其中的 interface屬性標明瞭該 service 實現的服務介面。
- com.zxn.example.service.compute.multiply
按照與 add bundle 同樣的方法,實現 multiply bundle,如下圖所示:
圖 9. 介面實現程式碼:Multiply 類
介面實現程式碼-Multiply類.bmp
類似的,新增一個 computeMultiply-context.xml 輸出 OSGi service,如下圖所示 :
圖 10. Spring 宣告檔案:computeMultiply-context.xml
Step 2 :實現 Web Layer
Web 層只包含一個 bundle:com.zxn.example.web,採用 Spring-MVC 和 OSGi 構建,基本程式結構如下圖所示:
圖 11. Web Layer 程式結構
- ComputeControler.java
該JAVA類實現了 org.springframework.web.servlet.mvc.Controller
,
是本 web應用中核心的 servlet,負責接受並處理 web 請求。該類呼叫 ComputeServiceRef 的方法實現業務邏輯。該類關鍵的方法是 handleRequest(…), 如下圖所示 :
圖 12. 核心 servlet 類
- ComputeServiceRef.java
該 JAVA 類負責引用部署的 service bundle 完成最終計算,其中的 computeService 由 Spring 根據 OSGi 中實際部署的 service 進行注入。本例中,實際部署的 service 可能是 add bundle 或者 multiply bundle。
需要特別注意的是,此處體現了 Spring-DM 的動態特性。OSGi 的動態部署能力使得 Spring 的動態服務注入成為可能。
圖 13. 服務消費類
- HTTPContextResgistry.java
該 JAVA 類負責在 OSGi 環境中配置和註冊 HTTP 服務,其關鍵方法為 bean 初始化時呼叫的 init( ) 方法。
圖 14. 在 OSGi 環境中註冊 HTTP 服務
該 init 方法中,第六行的 getHTTPService(…) 呼叫 OSGi 的 ServiceTracker 來獲取 OSGi環境中註冊的 HTTP 服務的引用,如下圖所示:
圖 15. 使用 ServiceTracker 獲取 HTTP 服務
- computeWeb-context.xml
該 xml 檔案主要用於配置 HTTPContextResgistry bean 類,以及匯入對 Compute 服務介面的引用。標記 osgi : reference用於宣告要匯入的服務介面,其 interface 屬性標明瞭該介面的定義,本例中為 com.zxn.example.service.compute.Compute 介面。
圖 16. Spring 宣告檔案:匯入服務介面
- computeWeb-Dispatcher.xml
該 xml 檔案用於配置 ComputeControler bean類。
圖 17. Spring 宣告檔案:配置核心 servlet 類
執行程式
以往開發 J2EE 應用通常需要將應用伺服器的 runtime 整合到開發環境中才能進行程式除錯,非常麻煩。基於 OSGi 的應用完全可以脫離應用伺服器執行,這使得程式開發和除錯變得非常容易,直接在 Eclipse 中除錯執行就可以。我們在 Eclipse 中將程式執行起來,如下圖所示:
圖18. 執行 OSGi 程式
從上圖中看到,我們同時選擇部署了 add bundle 和 multiply bundle,利用 OSGi console 察看如下:
圖 19. 察看部署的 OSGi bundle
當 OSGi 環境中同時部署有多個服務介面的實現 bundle 時,OSGi 會選擇一個預設的 bundle提供服務。本例中,Spring 會預設注入 add bundle。我們通過 web 訪問 compute.html 頁面:
圖 20. 訪問頁面
點 Compute 按鈕之後,結果頁面如下:
圖 21. 訪問結果
可以看出,是 add bundle 提供了計算服務。下面我們通過命令 <stop 76> 來停止 add bundle的服務:
圖 22. 停止 add bundle
圖 23. add bundle 狀態變為 RESOLVED
重新訪問 compute.html 頁面,結果得到的是兩個數字的乘積。可以看出,是 multiply bundl
提供了計算服務。如下圖所示:
圖 24. 再次訪問頁面
小結
作為當前頗具生命力的兩個標準和框架,OSGi 和 Spring 已經初步融合在一起。二者的結合,為開發企業級的 Web 應用同時提供了巨大的靈活性和動態部署能力。本文通過一個簡單的例子,描述如何開發一個基於 OSGi 和 Spring 的 Web 應用,並說明了開發過程中涉及到的技術關鍵點。