1. 程式人生 > >在Spring DM中使用Annotations釋出和引用服務

在Spring DM中使用Annotations釋出和引用服務

Spring DM並不是OSGi規範的實現,也就是說它不是OSGi容器。它只是一套用於在 OSGi環境中將Spring Bean註冊釋出為OSGi服務的工具。它充分利用Spring的特性,將OSGi與Spring無縫的加以結合,實現了Spring的模組化開發。

1.  在使用Spring DM進行開發過程中發現的問題:

雖然Spring DM改進了OSGi原本的DS開發,使用Spring Bean來完成服務的註冊。但是對於“懶惰”的程式設計師來說還是太過繁瑣,首先需要在XML中註冊SpringBean,然後要使用SpringDM提供的特殊名稱空間(<osgi:service>)釋出服務,最後當需要引入一個OSGi服務時又要使用SpringDM提供的另外一個特殊名稱空間(<osgi:reference>)來引入服務。對於一個只有十幾個Bean的應用程式來說,這些配置量也無所謂,但是當應用程式的Bean發展到上百個的時候,這簡直就是配置噩夢。尤其是在多人合作開發的環境下,經常會發生遺漏配置的情況,一旦遺漏則OSGi服務將無法啟動,並且要在上百個Bean配置XML檔案中查詢到底是哪個遺漏了,的確也是一件體力活(這正是IT民工的悲慘命運)。

當然Spring DM的開發團隊也想到了這點,為我們開發人員提供了Annotation的選項,這樣可以大大減少XML的配置量(這裡我們不爭論XML和Annotation孰優孰劣)。但是不知為何,Spring DM只提供了引入服務的Annotation,並沒有提供釋出OSGi服務的Annotation,這讓我很費解?其次,由於SpringDM是在JDK1.4環境下編譯的,所以預設情況下Annotation的功能是Disabled的。這又讓我很費解?到底讓我們用不用Annotation!

2.  問題的解決方案:

針對上述的問題,我們可以通過以下方法解決;

1)  如何啟用Spring DM的Annotation功能

通過閱讀Spring DM提供的官方文件,讓我很莫名,關於Annotation的介紹只有寥寥數語,而且還是放在附錄中介紹的。可能是我的英文水平太爛,總之一句話,沒看懂!後來從解決其他問題的過程中得到了啟示,終於開啟了SpringDM的Annotation功能。

第一步,建立一個Fragment型別的Bundle,具體的目錄結構如下圖所示:


這裡主要關注2個檔案,一個是extender.xml檔案和MANIFEST.MF檔案。

第二步,建立extender.xml檔案:

在上圖所示的目錄結構中建立一個extender.xml檔案,這個檔案用於覆蓋SpringDM Extender的配置選項。因為預設情況下是不支援Annotation的,所以要將指定的屬性值設定為開啟狀態,才能使用Annotation功能。配置檔案的內容如下:


這個配置檔案其實就是一個Spring Bean的配置檔案。將process.annotations屬性設定成true,就開啟了Spring DM的Annotation功能。

第三步,建立MANIFEST.MF檔案,這個檔案是Bundle的屬性配置檔案,我們在這個檔案中指定Fragment-Host屬性,具體內容如下:


其中我們要關注的是Fragment-Host屬性,這裡指定為org.springframework.osgi.extender,表示在extender Bundle啟動之前先啟動ftps-extender-config這個Bundle,這樣就將預設的屬性值更新為我們設定的屬性值。

第四步,在Spring的XML配置檔案中註冊處理Annotation的處理器Bean,如下所示:


通過上述4個步驟,我們就開啟了Spring DM的Annotation功能。

以下例子展示如何使用Spring DM的Annotation功能:


其中@ServiceReference就是引入OSGi服務的Annotation,它等效於XML中的<osgi:reference/>配置項。

2)  編寫自己的Annotation實現OSGi服務的釋出:

勝利的喜悅還沒維持多久,新的問題又隨之而來。既然我們已經有引入OSGi服務的Annotation,為什麼釋出OSGi服務的時候不能使用Annotation呢?我們仍然在繼續編寫一大坨苦澀的OSGi配置項,這種工作即枯燥又乏味。但是尋遍SpringDM的所有官方文件,包括SpringDM提供的Annotation擴充套件包原始碼,都沒有找到釋出OSGi服務的Annotation。Google一下,大致都是提問的,沒有回答問題的。沒有辦法,只能靠自己。經過對SpringDM原始碼的閱讀以及使用BEBUG來檢視程式碼流程的走向,終於瞭解了SpringDM將Spring Bean釋出為OSGi服務的大致方法。其中最關鍵的物件就是:OsgiServiceFactoryBean。這個工廠bean就是將SpringBean釋出為OSGi服務的關鍵類。這個類並不難懂,有興趣的朋友可以閱讀一下這個類的原始碼。由於我們是解決問題的文章,不是談理論的文章,所以我們繼續看實現方案:

首先介紹一下解決方案的大致流程。1)通過編寫一個實現了BeanPostProcessor介面的處理器類,我們就可以對Bean的生命週期進行控制。2)在容器中的某個Bean被初始化之後,我們攔截這個Bean,並獲取該Bean上是否寫了註冊OSGi服務的Annotation。3)如果是,則例項化OsgiServiceFactoryBean物件,設定相關的屬性值,並呼叫OsgiServiceFactoryBean物件的afterPropertiesSet方法,進行OSGi服務的註冊和釋出。最後返回該OsgiServiceFactoryBean物件。同時將該物件的引用放入一個集合中儲存用於OSGi服務的登出。4)當處理器類被銷燬時,對集合中的所有OsgiServiceFactoryBean物件執行destroy方法,完成OSGi服務的登出操作。

先來看Annotation的程式碼,很簡單,如下所示:


ElementType.TYPE指定這個Annotation只能用於類宣告,介面宣告上。

接著是處理器的實現程式碼,先關注類簽名:


這裡需要關注的是實現的幾個介面的用途。實現BundleContextAware介面是為了在處理器中獲取Bundle上下文物件。實現BeanClassLoaderAware介面是為了在處理器中獲取ClassLoader物件,這些物件都是註冊釋出OSGi服務所必須使用的物件。而繼承InstantiationAwareBeanPostProcessorAdapter介面卡是為了在Spring Bean初始化完之後給我們一個機會進行定製的特殊處理。我們需要覆蓋的是InstantiationAwareBeanPostProcessorAdapter類的postProcessAfterInitialization方法。該方法在Spring Bean初始化之後執行。我們來看這個方法的實現,如下所示:


程式碼中的註釋已經很好的說明了各邏輯塊的實現目的,不再贅述。其中需要關注createExporter方法,這個方法是建立OsgiServiceFactoryBean物件的。如下所示:


程式碼很簡單,就是例項化物件,並呼叫註冊釋出OSGi服務的方法。其實發佈一個OSGi服務只需要三個引數:Bean名稱,Bean物件,Bean實現介面型別的陣列,這個和XML中的配置屬性一一對應:

<osgi:service ref="systemConfigureServiceBean" interface="org.storevm.ftps.biz.service.core.SystemConfigureService"/>

其中ref屬性對應Bean名稱以及一個Bean物件的引用,interface屬性對應Bean實現介面型別的一個數組。

該方法中用到的一些其他例項變數的定義如下:


最後來看一下OSGi服務的登出實現,程式碼如下:


登出服務就是將先前註冊時儲存下來的OsgiServiceFactoryBean物件集合進行destroy方法的呼叫。

3.  啟用釋出OSGi服務的Annotation:

相比較開啟SpringDM自帶的Annotation,我們這個Annotation的啟用方法要簡單的多。只要在Spring的XML中註冊這個處理器Bean就可以了,程式碼如下:


以下例項程式碼展示瞭如何使用使用Annotation釋出一個OSGi服務釋出:


其中@OsgiService就是註冊釋出OSGi服務的Anntation。到此我們終於實現了使用Annotation來發布註冊OSGi服務以及引入OSGi服務,我們不再需要在XML中配置那一大坨無聊的OSGi服務配置了。最後引用一句名言:任何發明創造源自於“懶惰”