熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java開源技術 >> 正文

基於Eclipse開發輕量級Spring插件

2013-11-23 20:43:19  來源: Java開源技術 

  摘要 本文介紹如何在Eclipse中利用Spring框架作為一個平台來創建輕量級的能夠與你的現有JEE應用程序無縫集成的插件

   引言

  一般地企業軟件產品都要求在客戶端具有定制能力而且當客戶必須修改核心產品的配置來引入他們自己的定制時一般都要求進行更新操作借助於易於擴展和可升級的高度模塊化的軟件插件技術能夠提供針對這種典型場所下的完美解決方案

  注釋什麼是插件呢?一個插件是使用什麼樣的代碼構成的?

  在眾多的定義當中我認為最好的定義當屬Eclipse工程中所定義的插件是一種代碼貢獻它能夠把代碼添加到一個系統中的眾所周知的擴展點處也就是說一個插件是一個良好定義的代碼包(例如一個jar文件或目錄)它提供足夠的配置能力來實現在系統中的一個特定的眾所周知的位置插入和激活自身

  插件本身還可以定義另外的其它插件能夠擴展的擴展點一個擴展點定義了一個語言接口(該插件將提供它的一個實現)和使用該被發現的插件的組件一個擴展點能夠接受被動態地發現和在運行時刻配置的插件

  借助於一種擁有清晰定義的擴展點的插件環境核心產品可以自由升級而且插件本身可以根據獨立的計劃發行和升級例如借助於我的開源Classpath助理工程(基於Eclipse的插件框架)我可以按常規來升級我的Eclipse而且還可以輕松地發行我自己的插件的更新版本

  特別對於Java開發者來說與現有JEE組件(參考注釋JEE組件不是插件嗎?)相比插件提供了一種更好的升級技術可以設想你的許多EJB是由不同的開發小組構建的然後在了解它們能夠良好工作的情況下就可以把它們整合到一個應用程序中一個插件架構應該是允許進行這種級別的組件化的

  注釋JEE組件不是插件嗎?

  是的JEE組件例如EJB和Servlet都不是插件盡管它們都具有一定程度的可插入性(這是指你能夠交換一個EJB或Servlet實現)但是配置它們並不那麼清晰明快而且它們缺乏一個插件所具有的容易的升級能力例如Servlet無法把代碼與配置結合到一起因此盡管你能夠在其自己的jar文件中打包一個servlet實現但是此時你往往需要修改webxml以便servlet容器能夠識別它

  乍看上去EJB似乎更象插件它們包含提供有關自己信息的發布描述符然而EJB也不是插件因為典型情況下它們都要求外部配置(一種在EAR的applicationxml中的引用)並且典型地它們在其各自的發布描述符中進行彼此參考這兩種特征都使一個EJB無法成為插件式可發布的

  借助於流行的Spring框架的BeanFactoryPostProcessor接口開發者可以輕松地創建一個輕量級插件框架本文正是想討論如何實現這一點同時還要向你展示一個使用輕量級插件的工作示例

  二 准備你的插件平台

  在你的平台能夠支持可插入的組件前它需要滿足下列兩個標准

  · 組件必須是自發現的你已經了解到JEE組件不能成為真正插件的准確理由典型情況下你應該找到一個需要升級的外部配置文件以便該平台能夠感知新的代碼

  · 組件必須包含足夠信息以便在應用程序內部集成或配置其本身

  如果你僅是添加一些不需要與系統進行協作的代碼(也就是說松耦合的)那麼自動發現就是很簡單的真正的挑戰是結合有緊密集成的自發現

   Spring中的自發現功能

  事實證明Spring實際上為支持插件開發作了比較好的准備Spring已經能夠在若干種bean上下文文件中存儲配置並且它使得自發現配置文件非常簡單例如下面的Spring語句自動發現以ctxxml結尾的存在於classpath的METAINF/services目錄下的任何文件

<import resource=classpath*:METAINF/services/*ctxxml />

  這種現成的功能正是當構建輕量級插件框架時你要利用的一個特色

  注意Spring並不關心它自己的代碼自動發現功能這通常不是一個問題因為大多數JEE容器都提供一個lib目錄存放於這個目錄下的任何jar文件將被自動地添加到classpath中這意味著如果你想以jar文件形式捐獻你的代碼的話那麼在任何一種JEE容器中實現自發現都會是相當容易的事情

  在一個應用程序服務器外使用例如ant這樣的工具來實現jar文件的自發現也是非常容易的下列的Apache Ant XML以一種與一個應用程序服務器類似的方式檢測所有的存在於lib目錄下的jar文件

<path id=classpath
<fileset dir=${basedir}/lib
<include name=**/*jar/>
</fileset>
</path>
<target name=startserver description=launches the server process
<java classname=platformbootstrapServer
<classpath refid=classpath />
</java>
</target>
 

  因此盡管Spring並不直接支持自發現功能但是通過使用標准技術你仍然可以使你的代碼容易地實現自發現這一點與Spring的能夠自動檢測配置的能力相結合就可以使你既能夠實現代碼捐獻的目的也能夠使你的代碼在系統中被發現和激活

   在Spring中實現自配置

  你需要進一步實現的是使插件具有自配置能力盡管Spring並不直接支持這種功能但是借助於它提供的一些工具實現這一目標也是相當直接的實現自配置的關鍵部分是BeanFactoryPostProcessor這是一個Spring調用的接口(該調用應該是在所有配置被發現和加載到一個內存描述之後但在創建實際的對象之前發生)

  通過使用BeanFactoryPostProcessor你可以動態地把所有的bean組合到一起而不必修改原始的文件系統配置下列代碼是我的BeanFactoryPostProcessor實現的核心部分PluginBeanFactoryPostProcessor(下載源碼中提供了完整的類)

private String extensionBeanName;//經由spring設置(在此沒有顯示setter)
private String propertyName;//經由spring設置(在此沒有顯示setter)
private String pluginBeanName;//經由spring設置(在此沒有顯示setter)
/*
*(非Javadoc)
*@請參考BeanFactoryPostProcessor#postProcessBeanFactory(ConfigurableListableBeanFactory)
*/
public void postProcessBeanFactory(
ConfigurableListableBeanFactory beanFactory)
throws BeansException {
//找到我們希望修改的bean定義
BeanDefinition beanDef =
beanFactorygetBeanDefinition(extensionBeanName);

//在該bean定義中查找它的屬性並且發現我們將修改的具體屬性
MutablePropertyValues propValues = beanDefgetPropertyValues();
if ( !propValuescontains(propertyName))
throw new IllegalArgumentException(Cannot find property +
propertyName + in bean + extensionBeanName);
PropertyValue pv = propValuesgetPropertyValue(propertyName);

//取出值定義(在我們的情況下我們僅支持列表風格屬性的更新)
Object prop = pvgetValue();
if ( !(prop instanceof List))
throw new IllegalArgumentException(Property + propertyName +
in extension bean +
extensionBeanName +
is not an instanceof List);
//把我們的bean參考添加到列表中當Spring創建對象
// 並且把它們綁定到一起時我們的bean現在准備好了
List l = (List) pvgetValue();
ladd(new RuntimeBeanReference(pluginBeanName));
}
 

  下面展示了配置在Spring中看上去的樣子首先在你的核心工程中定義擴展點它是examplecrapsTable的一個實例其中它的兩個屬性(diceplayers)配置以空列表這是標准的Spring用法

<beans>
<bean id=extensionpointcrapstable
class=examplecrapsTable
initmethod=init
<property name=dice
<list>
</list>
</property>
<property name=players
<list>
</list>
</property>
</bean>
</beans>

  現在你可以使用插件類連同它的Spring上下文(這將是自發現的)打包一個jar文件並且你可以擁有一個類似如下的配置

<beans>
<bean id=realdice class=examplecrapsRealDice />
<bean class=platformspringPluginBeanFactoryPostProcessor
<property name=extensionBeanName
value=extensionpointcrapstable />
<property name=propertyName value=dice />
<property name=pluginBeanName value=realdice />
</bean>
</beans>

  在這個Spring配置中定義了一個examplecrapsRealDice的實例然後它定義你的PluginBeanFactoryPostProcessor(它被配置以找到extensionpointcrapstable bean)這一實例還會把你的realdice bean添加到craps表的dice屬性中

  注意這是本文中真正的焦點所在這個到Spring的小擴展就是編寫基於插件的組件的所有要求注意如果你刪除這個包含該Spring上下文的jar文件那麼你還要從extensionpointcrapstable bean中分離你的bean然後把該jar添加回去並且把它自己綁定到系統中的適當位置

   使用輕量級插件進行開發

  我常常吃驚於大多數的架構師團隊極少地考慮開發者能否容易地使用他們的框架其實EJB就是一種具有學術式優點的極好的例子但是其實踐中的開發缺點使其變得極為昂貴所以我認為當選用一種框架實現典型的編碼/構建/調試工作時先了解一下該框架具有什麼樣的負荷能力和影響是非常重要的

  從這種角度來看輕量級插件則是相當無痛苦你可以把每一個插件作為它自己的簡單地依賴於核心產品的jar的可構建工程這在一種類似於Eclipse這樣的工具(在其中核心產品具有其自己的Java工程並且每一種插件也都有其自己的)中是很容易建模的你僅需要一個最終的裝配工程它依賴於核心產品和包括的各種插件工程通過使裝配工程依附於核心和插件工程你的classpath會被自動地正確構建本文的下載源碼中提供了一個類似這樣的工程記住你可以為每一種客戶創建一個裝配工程從而允許你把不同的插件與不同的客戶相匹配這種方式與Eclipse恰好吻合允許在調試期間的增長式編譯和代碼熱交換這使你的開發進程相當靈活不必要加入完全妨礙Eclipse的本機Java支持的構建步驟

   一切都是插件嗎?

  Eclipse的一個根本特征是一切都是插件(請參考注釋Eclipse插件比較)從系統的初始啟動到Java開發環境再到在線幫助系統每一種捐獻代碼(即使不是Java代碼的代碼)都以一種插件形式存在這種方式具有其優點但是它規定了一種工業插件開發環境具有完整的工具例如管理組件調試器支持等等幸好Eclipse提供了這些功能但是具有這種級別支持的服務器端框架並不存在(據我所知)

  注釋Eclipse插件比較

  比較於Eclipse插件我一直把該插件稱作是輕量級的但是你可能疑惑憑什麼說它們是輕量級的?其實我使用術語輕量級術語主要是強調實現一種基於插件的架構的主要優點是相當輕快和簡單的

  Eclipse工程基於一種具有工業強度的插件架構因此我認為把稍微擴展Spring框架功能的插件架構與一種具有豐富特征的插件實現進行比較是很有價值的

  多個類加載器支持

  Eclipse工程具有一種復雜的類加載模型這區別於(但非完全不同於)一種應用程序服務器的使用類加載器層次的方式既然Eclipse鼓勵第三方進行插件開發那麼很可能存在具體類的命名和版本沖突問題

  通過不支持同一個類的多個版本輕量級方法則可以完全避免這個問題例如對於我所工作的應用程序來說這就是一種合理的約束因為我們主要使用插件來提供一種可信的升級功能我們只是或多或少控制我們想使用哪些版本和jar文件因此我們不需要多個類加載器支持

  Manifest和其它Meta信息

  Eclipse插件提供了一種詳細的manifest它負責不僅提供有關一個插件擴展了哪些擴展點的信息而且還提供有關它如何依賴於其它插件的信息在運行時刻你可以浏覽該插件倉庫以發現插件並且遍歷它們的依賴性Eclipse鼓勵使用一種懶惰式插件加載模型當實現一個擴展點時你必須顯式地查找擴展它的那個插件並且典型地你僅加載你需要的那些插件這種方案減少了啟動時間並且能夠防止因加載不用的對象而造成資源浪費

  Meta信息也是很重要的Eclipse可以使用它來強制實現你的聲明Eclipse能夠通知你有關丟失的相關性信息告訴你使用相同的擴展點時何時你有太多或太少的插件等等

  借助於輕量級插件你不必擁有一個正常的manifest它允許你以編程方式存取你依賴的內容而且所有的你的插件在運行時刻加載只是你必須自己來進行任何類型的檢查

  如果不使用一種顯式的依賴性列表那麼你必須或者把所有你對於第三方的依賴性打包到你的插件jar中或者假定一種第三方類的基本集合總是位於客戶環境中如果你忘記一些東西那麼你就會遇到典型的挑戰確定丟失了哪些jar文件

  在實踐中我經常需要決定是否要構建第三方庫(如果它是顧客特定的jar那麼典型情況下我都把它們嵌入到這個插件jar中如果我使用一種標准的開源包例如Jakarta commons那麼我會經常把它添加到核心應用程序中)當輕量級過於輕量級並且你不能以一種adhoc方式來管理這些類型的依賴性時作好調用判斷確實更為重要

  至於其它的manifest相關數據你可以通過提供一種輕量級的manifest來擴展PluginBeanFactoryPostProcessorjava以便跟蹤具有插件的bean你還可以使用該信息來強制實施一些約束規則

  在服務器端開發中構建的EJBJSP/Servlet等組件並不是以真正插件的形式出現的它們都要求真正的工作以便定義和歸檔一個擴展點因此把一切都當作一個插件可能會增加大量工作因為大多數JEE工程師可能對此不太熟悉

  我總是試圖把插件作為一種工具來實現特定領域的定制目的同樣地利用Spring創建輕量級的與你的現有應用程序和技術無縫接合的插件就成為極其緊迫的任務注意在大部分情況下你的應用程序通常是一種比較獨立的Spring/JEE應用程序

  另外你還應該熟悉一些可選擇的插件框架特別是那些經常在http://jpfsourceforgenet/上發布的Java插件框架工程我從來沒有使用這些框架來確定是否它能夠與Spring良好協作以及你的應用程序需要花多大代價來采納它但是如果基於Spring的其它插件不太適合你的口味的話那麼這些框架可能是你的一個不錯的選擇


From:http://tw.wingwit.com/Article/program/Java/ky/201311/28956.html
    推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.