如果你看一下最新推出的Spring框架,馬上就會遭遇排山倒海般襲來的多樣性配置(Configuration),然而從一開始的XML、標註(Annotation)、JavaConfig到Groovy DSL,框架中提供的配置方式如此多樣,令人不禁訝異與質疑:配置文件真的要玩到這麼複雜嗎?

已成昨日黃花的XML

對Java開發者來說,XML無疑是最熟悉的配置文件格式,即便現今已不鼓勵使用XML進行配置,然而,過去Java十幾年的風行,造就了許多的系統,而這些系統中遺留了許多XML配置文件,身為Java開發者,在配置需求上仍必須與XML打交道,然而相對於一些新的配置方式,在學習、閱讀與維護XML配置文件上,多半是令人感到無奈與厭煩。

就現今而言,XML的問題顯而易見,除了難以閱讀的各式繁瑣標籤之外,在缺乏適當編輯工具的輔助之下(經常如此),沒辦法透過自動完成(auto-complete)來加速設定、減少打字錯誤,配置文件無法重構、非型態安全(type-safe),許多配置上的錯誤只能在執行時期才能發現……以歷史回顧的角度來看,要舉出XML的諸多缺點,並不困難。

過去XML身為配置主角的年代可不是如此,這要思考一下配置的需求從何而來。一開始開發者基於需求撰寫程式,隨著需求擴大,開發者必須撰寫可膠合(glue)出應用程式的各個模組,然而,撰寫程式來膠合模組是麻煩且重複的,因此必須設計出可配置(configurable)模組,透過XML來配置模組的膠合方式,然後撰寫一隻專門讀取XML並完成膠合的程式,這種方式帶來了產能增進,造就Java程式庫與框架的風行,使用XML在當時也是理所當然。

過去各框架或容器基於本身瞄準的問題或領域,有著各種專用的配置方式,而且大多與框架或容器本身有著很大的耦合性,開發者的程式碼不得不被框架約束,在程式中撰寫特定程式碼,這使得程式本身脫離了框架或容器就無法運作(像是EJB2.x),而且,如此的侵入式作法,違反了開發者不想在程式上費心於配置之目的,而這也成了Spring框架的核心容器瞄準的問題──藉由非侵入式的設計,讓開發者真正可免於以程式撰寫方式來進行配置。

慣例優於配置與標註

隨著應用程式開發的多樣化與規模增長,XML中為了膠合模組的資訊就得不斷增加,各框架或容器也開始面臨了如何解決XML地獄(XML hell)的問題,做為一個將配置資訊完全從程式中獨立出來的Spring框架,配置資訊更為集中的優點,反而會使得XML地獄問題更為嚴重,為了解決部份問題,Spring引入了一些命名空間(namespace)來簡化Bean的屬性設定。

慣例優於配置(Convention over configuration),也是解決問題的方式之一,沒有設定就採預設值的方式,大量減少了XML的配置,令配置文件中只留下真正感興趣、應當留意或指明意圖的資訊。

XML是為了將配置資訊從程式編寫中抽離出來,配置資訊集中自然是XML的優點,如果目的是為了瞭解整個應用程式的配置,只要觀看XML就可以明瞭,然而實際上後來的開發經驗中發現,並非每個配置資訊都得從程式碼中抽離,最常見的舉例是交易(Transaction),如果一個方法會參與交易,那麼,開發者會想要在程式碼中閱讀該方法時,馬上知道這項資訊,因為這是開發者關注的事項。

JDK5開始引入了標註功能,許多框架或容器也紛紛支援了使用標註進行配置,一方面可以繼續實現慣例優於配置,另一方面開發者可將本該集中配置的資訊放在XML,而將閱讀程式碼同時所要關注的配置資訊,在程式碼中適當位置進行標註,Spring中也支援了這個選項,當時常見的建議是適當混用XML與標註,如果標註有特定功能且具有註解(Comment)當下程式碼之目的,那麼標註就會是合適的配置選擇。

當然,標註本身有缺點,因為標註撰寫在程式碼中提供了特定資訊,因而要修改程式碼中的配置就必須重新編譯、建置(Build),不像XML可直接對一個類別或方法配置不同的資訊。在這方面的補充建議是,將標註作為預設值,必要時可於XML中覆蓋設定,另一方面,隨著各種配置需求的增加,標註方式也隨之多樣化,到某種程度之後,標註本身甚至就像自成一套的程式語言。

JavaConfig與Groovy DSL

實際上,應用程式在配置方面的需求是無盡的,無論框架或容器本身如何盡力提供足夠的XML或標註配置方式,仍有無法照顧到的需求,而必須尋找另一配置方案或自行採用程式編寫,來達到目的,然而,兩種作法都有可能將關注的配置資訊,散落到與目前配置方案相異的其他方案之中。

Spring後來提供了JavaConfig的方式,可用來取代XML的配置方案,可以在Java程式碼中進行配置,代表著可善用Java身為靜態定型語言的優點,編譯時期就可檢查出型態相關的配置錯誤,在常用的一些IDE或編輯器的輔助之下,配置時就可以有自動完成功能,也可以對配置檔案進行重構,對於開發者撰寫程式碼時無需關注的配置,也具有集中到JavaConfig,以實現配置方面的關注分離等優點。

因此,當JavaConfig成為Spring核心之一後,JavaConfig成了配置的首選建議,不過,除了能善用Java是靜態定型語言的優點之外,更重要的一點是,JavaConfig本身就是一個Java程式語言撰寫的檔案,因此,在配置時如果願意,可以撰寫Java程式碼來自訂配置需求,這等於是將開發者關注的配置,再度移回同質的程式碼之中,只要是Java語言能做到的範圍,配置需求上都能滿足。

然而,也因為使用Java語言,配置上也就有Java語言本身繁瑣的缺點(從某些角度來看也許是優點),能做到的功能也受到Java語言本身的限制,因此,Spring又提供了Groovy DSL作為配置的選項之一,除了配置相較於XML或JavaConfig都更為簡潔之外,從Java建置工具Gradle,以及Web快速開發框架Grails等也能看到,Groovy語言的動態性,使得配置更為靈活有彈性,如果開發者想要,可以使用Groovy DSL寫出許多複雜而神奇的配置方式。

不過,Groovy DSL與JavaConfig並非互斥方案,就Java開發者來說,使用JavaConfig表示可運用熟悉的Java語言,無需學習另一套語言來實現自訂配置,當然若願意(基於未來擴充性或團隊中有熟悉Grovvy之人),也可以單純將Groovy配置當作一種簡潔的配置選項,這同時也可保留將來以Groovy DSL自訂各種配置的彈性,然而代價就是必須學習Groovy語言。

回到程式編寫?

從XML、標註、JavaConfig到Groovy DSL,配置離開後,怎麼又回到了程式編寫?實際上,就像開發者會對程式碼其他關注點做適當分離,「應用程式模組間的膠合配置,也是一個關注點」,從XML、標註、JavaConfig再到Groovy DSL,實際上就是各個配置選項對應用程式模組的膠合,帶來的關注分離(Separation of concerns)的過程。

從配置文件到實際上膠合模組的過程,實際上是由框架或容器完成,也就是框架或容器抽取了配置、膠合模組的關注點,而這些框架或容器也是編寫程式來實現,當配置如同James Gosling說的:「配置檔都會成為程式語言」,也就是當需要再度編寫程式進行配置時,就表示在配置需求上,開發者需要更細緻的方式來實現更細緻的關注分離。

當在標註、JavaConfig、Groovy DSL(甚至是XML)之間考量配置需求時,重要的是意識到這次關注的對象是配置,應考量哪些人會在哪個地方關注這些配置?哪些人有能力或該為編寫、維護配置負責?再次地,保持適當關注分離這項古老原則,會是選用哪個或是混用哪些方案時的最大考量。

作者簡介


Advertisement

更多 iThome相關內容