摘要 這篇文章介紹了通過ANT任務的擴展來實現IoC管理對象或非管理對象的執行
同時也介紹了OGNL(對象圖形導航語言)如何被用來使ANT執行任何方法表達式
包括帶有運行時參數的
也介紹了如何使用JUNIT來測試ANT的擴展
此外
還包含一個使用SPRING框架的實現
Ant
IoC的組合為創建松耦合的軟件開發支持任務開創了新的天地
我需要增加一個基於ANT驅動的新任務
並且我用SPRING(一個輕量級的IoC框架)來實現這個任務
我幾乎沒有碰到什麼問題
因為IoC容器是非侵入式的
這很容易創建一個包裝或者直接使用對象來實現任務
於是我開始想知道是否ANT可以直接使用SPRING配置的對象
然後重用已經定義和測試的依賴圖和配置
那為什麼還要重復和引入波紋效應或其他問題呢?如果IoC容器果真能提供這樣的便利
就可以保證更直接的使用
這篇文章介紹了這種方法並且演示了一種概念上的實現
剛接觸ANT擴展的開發者會發現這個例子十分有趣
ANT擴展 為了給ANT增加自定義任務
ANT手冊建議使用為這個目的而提供的類
如Task類
但是這個建議不是強制的
ANT可以執行任何擁有execute()方法的類(當然ANT也可以通過使用exec或java任務來執行任何程序
但那是另一種擴展方式)
ANT也支持集成這些任務擴展到各種類型的屬性或XML文件中
給ANT增加一個自定義任務的最佳方法是通過Task擴展來重用IoC框架
因此
執行獨立應用的Task必須設置和使用建立在ANT基礎上的框架內置的對象和資源
控制反轉 IoC設計模式
也稱作DI(依賴注射)
在框架的上下文中
這與JAVA對象的組成有關
在IoC框架上增加的投資很大一部分是由於SPRING框架的開發人員演示了在一個IoC/AOP/XML/JavaBeans輕量框架中的協同作用
而這正是通過允許為其他API或組件創建強大的抽象層來提供超越DI能力的原因
SPRING本身就是一個使用IoC的例子
ANT看起來與IoC容器相適應
因為他也是基於XML或者JavaBean的
從某方面來說
他也使用了IoC
需求 我們的ANT IoC任務擴展需求可以通過角色/目標/需求的格式來定義(這裡的需求不分順序)
●角色
開發人員
●目標
修改IoC任務
●需求
在任何代碼改變或構建後執行回歸測試
很容易在回歸測試中增加新的測試用例
支持不同的IoC框架
通過修改ANT日志的級別或IoC日志的配置使調試時可以得到更有效的輸出
●角色
構建創建人
●目標
編輯ANT目標並使用任務來定義IoC容器的輸入或輸出Bean
●需求
設置IoC描述符的位置
在不需要容器時
定義FQCN(完全限定類名)作為目標
使用IoC時
設置POJO(普通JAVA對象)Bean名
缺省為antBean
定義目標方法名
缺省為execute
定義一個調用可以帶參數的表達式的方法
定義可以插入目標Bean的屬性
用來復寫容器屬性
定義目標的元素文本
沒有必要定義用來處理Ant/IoC組合的新類
為了各種擴展需要重用現存的屬性文件
●角色
任務擴展對象
●目標
執行對象方法
●需求
執行在IoC Bean定義中定義的POJO
執行容器外的定義類
如果沒有定義使用缺省的Bean名antBean
執行簡單的方法
缺省為execute()
執行帶可選參數的方法表達式
如果目標是ANT相關的則插入工程
插入動態屬性
任務 支持這些需求的任務定義是SpringContextTask
描述 這個任務執行由SPRING容器管理的或者是未管理的FQCN的對象的方法
目前還不支持SRPING Bean定義引用的Classpath
SpringContextTask的參數如下表所示
例子
最簡單的應用我們的ANT任務擴展的例子如下
&!
create the task definition
>&taskdef name=
runBean
classpat classname=
jbetancourt
ant
task
spring
SpringContextTask
/>&target name=
simpleAppContextUseWithDefaults
> &runBean beanLocations=
applicationContext
xml
>&/runBean>&/target>
simpleAppContextUseWithDefaults目標執行在文件路徑中找到的Bean定義文件applicationContext
xml中的Bean名為antBean的execute()方法
路徑屬性名是復數的以便將來支持多個Bean定義文件
Bean的執行類似ANT執行對象的方法
然而
這裡是IoC容器來管理Bean
容器可以增加事務依賴
包裝數據庫
設置網絡服務代理
使用遠程甚至提供AOP代理來代替實際目標Bean
我們的方法簡化了配置
因為ANT腳本不再需要知道如何配置對象
特別是復雜的對象
但是如果ANT腳本確實需要為服務調用設置特定的屬性時會怎麼樣呢
&target name=
publish
> &spring beanLocations=
applicationContext
xml
beanName=
siteGenerator
methodName=
generateSite
host=
${host
site
url}
port=
${site
port}
> Made a few tweaks
Removed some sentence fragments
&/spring> &/target>
注意因為任務名已經在taskdef中定義了
使用的名字將依賴於ANT的taskdef定義
這兒任務名是spring
現在我們定義Bean名字和調用的方法
元素文本也會被放到目標Bena中
在這個例子中
文本是一個發布的注釋
通過使用ANT的動態屬性功能
我們也可以將需要的屬性放到目標對象中
通常在ANT文件中一個屬性被解析時
對應的set方法會被調用
使用動態屬性
非對象屬性或字段會通過setDynamicAttribute()方法被增加到對象中
通常因為容器已經包裝了其中的Bean的屬性
這種屬性注入提供了一種重寫的能力
但是
是否這樣會將配置復雜化?我們將不得不維護ANT任務使用的屬性及管理對象所需要的屬性
當然這不是必須的
如例子中的SPRING用法
相同的屬性文件被ANT和SPRING同時使用— 即使使用了ANT的占位符語法(${
})
SPRING提供了這種目的的類
如PropertyPlaceHolderConfigurer
因此
這種方法不會引入新的配置惡夢
可參考旁注
屬性中的屬性
獲得更多的幫助
另一種放置屬性的方法是通過使用call屬性來調用帶運行時參數的目標方法或者嵌套的methodCall元素
他的內容是java表達式
這個元素很容易使用因為XML需要的符號如實體轉義符可以用CDATA來避免
call=
generateSite("${host
site
url}"
"${site
port}")
Or better: &methodCall>&![CDATA[ generateSite(
${host
site
url}
${site
port}
) ]]>&/methodCall>
因此先前的例子可以如下寫法
&target name=
publish
> &spring beanLocations=
applicationContext
xml
beanName=
siteGenerator
> &methodCall> generateSite(
${host
site
url}
${site
port}
) &/methodCall> Made a few tweaks
Removed some sentence fragments
&/spring> &/target>
當然
目標對象必須包含需要的方法和參數標識符
上面的例子簡單介紹了SpringContextTask方法
可能他們可以有其他或更好的實現
有人可能會對這個Task擴展的特性有疑問
如調用任何方法的功能
這個功能甚至可以被移除
因為任何不包含execute()方法的目標Bean可以被包裝
一個任務在IoC框架中可能更容易完成
但既然通過OGNL(後面會討論)支持方法表達式很容易
那麼方法參數的支持也不是個問題了
有趣的是
既然任何方法可以被調用
那麼同一對象可以在同一個構建文件中被重用來提供不同的服務
這樣就可以在執行需要很多屬性的任務中減少過度的ANT腳本混亂了
如果任務實例可以通過ID來引用的話這個功能就會有實際意義了
我們可以象下面這樣寫
&spring id=
metrics
beanLocations=
metricsContext
xml
beanName=
main
exampleAttribute=
a value
and so forth
/> &target name=
ComputeMetrics
> &spring refid=
metrics
call=
computeNCSS
/> &spring refid=
metrics
call=
computeCCM
/> &spring refid=
metrics
call=
findBugs
/> &/target> &target name=
genDocs
> &!
here are calls to other types of docs
/> &!
now call the metric docs
/> &spring refid=
metrics
call=
createDocs
/> &/target>
現在我們擁有更易讀的格式而隱藏了更多的信息
我們不再關心容器中有什麼
只要那兒有一個入口點—main
那個Bean可以是實際的Bean或者通過依賴注射代理給其他工具如PMD
JavaNCSS
或者FindBugs
我沒有選擇通過ID引用重用SpringContextTask的開發方式
另一種完成重用的方式是在上下文中使用不同的Bean
如
&target name=
ComputeMetrics
> &spring beanLocations=
metricsContext
xml
beanName=
computeNCSS
/> &spring beanLocations=
metricsContext
xml
beanName=
computeCCM
/> &spring beanLocations=
metricsContext
xml
beanName=
findBugs
/>&/target>
但在這個例子中的每一個Bean必須有一個execute()方法來啟動服務
而且每一個Bean實際上只是引用同樣的類或對象
現在需求已經確定而
From:http://tw.wingwit.com/Article/program/Java/ky/201311/28461.html