轉(zhuǎn)載地址:http://www.infoq.com/cn/news/2011/04/xxb-maven-6-gradle
Maven面臨的挑戰(zhàn)
軟件行業(yè)新舊交替的速度之快往往令人咂舌,不用多少時(shí)間,你就會(huì)發(fā)現(xiàn)曾經(jīng)大紅大紫的技術(shù)已經(jīng)成為了昨日黃花,當(dāng)然,Maven也不會(huì)例外。雖然目前它基本上是Java構(gòu)建的事實(shí)標(biāo)準(zhǔn),但我們也能看到新興的工具在涌現(xiàn),比如基于Goovy的Gradle,而去年Hibernate宣布從Maven遷移至Gradle這 一事件更是吸引了不少眼球。在此之前,我也聽到了不少對(duì)Maven的抱怨,包括XML的繁冗,不夠靈活,學(xué)習(xí)曲線陡峭等等。那Gradle是否能夠在繼承 Maven優(yōu)點(diǎn)的基礎(chǔ)上,克服這些缺點(diǎn)呢?帶著這個(gè)疑問,我開始閱讀Gradle的文檔并嘗試著將一個(gè)基于Maven的項(xiàng)目轉(zhuǎn)成用Gradle構(gòu)建,本文 所要講述大概就是這樣的一個(gè)體驗(yàn)。需要注意的是,本文完全是基于Maven的角度來(lái)看Gradle的,因此對(duì)于Ant用戶來(lái)說(shuō),視角肯定會(huì)大有不同。
Gradle初體驗(yàn)
Gradle的安裝非常方便,下載ZIP包,解壓到本地目錄,設(shè)置 GRADLE_HOME 環(huán)境變量并將 GRADLE_HOME/bin 加到 PATH 環(huán)境變量中,安裝就完成了。用戶可以運(yùn)行gradle -v命令驗(yàn)證安裝,這些初始的步驟和Maven沒什么兩樣。Gradle目前的版本是1.0-milestone-1,根據(jù)其Wiki上的Roadmap,在1.0正式版發(fā)布之前,還至少會(huì)有3個(gè)里程碑版本,而1.0的發(fā)布日期最快也不會(huì)早于6月份。而正是這樣一個(gè)看起來(lái)似乎還不怎么成熟的項(xiàng)目,卻有著讓很多成熟項(xiàng)目都汗顏的文檔,其包括了安裝指南、基本教程、以及一份近300頁(yè)的全面用戶指南。這對(duì)于用戶來(lái)說(shuō)是非常友好的,同時(shí)也說(shuō)明了Gradle的開發(fā)者對(duì)這個(gè)項(xiàng)目非常有信心,要知道編寫并維護(hù)文檔可不是件輕松的工作,對(duì)于Gradle這樣未來(lái)仍可能發(fā)生很大變動(dòng)的項(xiàng)目來(lái)說(shuō)尤為如此。
類似于Maven的pom.xml
文件,每個(gè)Gradle項(xiàng)目都需要有一個(gè)對(duì)應(yīng)的build.gradle
文件,該文件定義一些任務(wù)(task)來(lái)完成構(gòu)建工作,當(dāng)然,每個(gè)任務(wù)是可配置的,任務(wù)之間也可以依賴,用戶亦能配置缺省任務(wù),就像這樣:
defaultTasks 'taskB' task taskA << { println "i'm task A" } task taskB << { println "i'm task B, and I depend on " + taskA.name } taskB.dependsOn taskA
運(yùn)行命令$ gradle -q之后(參數(shù)q讓Gradle不要打印錯(cuò)誤之外的日志),就能看到如下的預(yù)期輸出:
i'm task A i'm task B, and I depend on taskA
這不是和Ant如出一轍么?的確是這樣,這種“任務(wù)”的概念與用法與Ant及其相似。Ant任務(wù)是Gradle世界的第一公民,Gradle對(duì) Ant做了很好的集成。除此之外,由于Gradle使用的Grovvy腳本較XML更為靈活,因此,即使我自己不是Ant用戶,我也仍然覺得Ant用戶會(huì) 喜歡上Gradle。
依賴管理和集成Maven倉(cāng)庫(kù)
我們知道依賴管理、倉(cāng)庫(kù)、約定優(yōu)于配置等概念是Maven的核心內(nèi)容,拋開其實(shí)現(xiàn)是否最優(yōu)不談,概念本身沒什么問題,并且已經(jīng)被廣泛學(xué)習(xí)和接受。那Gradle實(shí)現(xiàn)了這些優(yōu)秀概念了么?答案是肯定的。
先看依賴管理,我有一個(gè)簡(jiǎn)單的項(xiàng)目依賴于一些第三方類庫(kù)包括SpringFramework、JUnit、Kaptcha等等。原來(lái)的Maven POM配置大概是這樣的(篇幅關(guān)系,省略了部分父POM配置):
<properties> <kaptcha.version>2.3</kaptcha.version> </properties> <dependencies> <dependency> <groupId>com.google.code.kaptcha</groupId> <artifactId>kaptcha</artifactId> <version>${kaptcha.version}</version> <classifier>jdk15</classifier> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> </dependency> </dependencies>
然后我將其轉(zhuǎn)換成Gradle腳本,結(jié)果是驚人的:
dependencies { compile('org.springframework:spring-core:2.5.6') compile('org.springframework:spring-beans:2.5.6') compile('org.springframework:spring-context:2.5.6') compile('com.google.code.kaptcha:kaptcha:2.3:jdk15') testCompile('junit:junit:4.7') }
注意配置從原來(lái)的28行縮減至7行!這還不算我省略的一些父POM配置。依賴的groupId、artifactId、 version,scope甚至是classfier,一點(diǎn)都不少。較之于Maven或者Ant的XML配置腳本,Gradle使用的Grovvy腳本殺 傷力太大了,愛美之心,人皆有之,相比于七旬老婦松松垮垮的皺紋,大家肯定都喜歡少女緊致的臉蛋,XML就是那老婦的皺紋。
關(guān)于Gradle的依賴管理起初我有一點(diǎn)擔(dān)心,就是它是否有傳遞性依賴的機(jī)制呢?經(jīng)過(guò)文檔閱讀和實(shí)際試驗(yàn)后,這個(gè)疑慮打消了,Gradle能夠解析 現(xiàn)有的Maven POM或者Ivy的XML配置,從而得到傳遞性依賴的信息,并且引入到當(dāng)前項(xiàng)目中,這實(shí)在是一個(gè)聰明的做法。在此基礎(chǔ)上,它也支持排除傳遞性依賴或者干脆 關(guān)閉傳遞性依賴,其中第二點(diǎn)是Maven所不具備的特性。
自動(dòng)化依賴管理的基石是倉(cāng)庫(kù),Maven中央倉(cāng)庫(kù)已經(jīng)成為了Java開發(fā)者不可或缺的資源,Gradle既然有依賴管理,那必然也得用到倉(cāng)庫(kù),這當(dāng)然也包括了Maven中央倉(cāng)庫(kù),就像這樣:
repositories { mavenLocal() mavenCentral() mavenRepo urls: "http://repository.sonatype.org/content/groups/forge/" }
這段代碼幾乎不用解釋,就是在Gradle中配置使用Maven本地倉(cāng)庫(kù)、中央倉(cāng)庫(kù)、以及自定義地址倉(cāng)庫(kù)。在我實(shí)際構(gòu)建項(xiàng)目的時(shí)候,能看到終端打印的下載信息,下載后的文件被存儲(chǔ)在 USER_HOME/.gradle/cache/
目錄下供項(xiàng)目使用,這種實(shí)現(xiàn)的方法與Maven又是及其類似了,可以說(shuō)Gradle不僅最大限度的繼承Maven的很多理念,倉(cāng)庫(kù)資源也是直接拿來(lái)用。
Gradle項(xiàng)目使用Maven項(xiàng)目生成的資源已經(jīng)不是個(gè)問題了,接著需要反過(guò)來(lái)考慮,Maven用戶是否能夠使用 Gradle生成的資源呢?或者更簡(jiǎn)單點(diǎn)問,Gradle項(xiàng)目生成的構(gòu)件是否可以發(fā)布到Maven倉(cāng)庫(kù)中供人使用呢?這一點(diǎn)非常重要,因?yàn)槿绻霾坏竭@一 點(diǎn),你可能就會(huì)丟失大量的用戶。幸運(yùn)的是Gradle再次給出了令人滿意的答案。使用Gradle的Maven Plugin,用戶就可以輕松地將項(xiàng)目構(gòu)件上傳到Maven倉(cāng)庫(kù)中:
apply plugin: 'maven' ... uploadArchives { repositories.mavenDeployer { repository(url: "http://localhost:8088/nexus/content/repositories/snapshots/") { authentication(userName: "admin", password: "admin123") pom.groupId = "com.juvenxu" pom.artifactId = "account-captcha" } } }
在上傳的過(guò)程中,Gradle能夠基于build.gradle
生成對(duì)應(yīng)的Maven POM文件,用戶可以自行配置POM信息,比如這里的groupId和artifactId,而諸如依賴配置這樣的內(nèi)容,Gradle是會(huì)自動(dòng)幫你進(jìn)行轉(zhuǎn) 換的。由于Maven項(xiàng)目之間依賴交互的直接途徑就是倉(cāng)庫(kù),而Gradle既能夠使用Maven倉(cāng)庫(kù),也能以Maven的格式將自己的內(nèi)容發(fā)布到倉(cāng)庫(kù)中, 因此從技術(shù)角度來(lái)說(shuō),即使在一個(gè)基于Maven的大環(huán)境中,局部使用Gradle也幾乎不會(huì)是一個(gè)問題。
約定優(yōu)于配置
如同Ant一般,Gradle給了用戶足夠的自由去定義自己的任務(wù),不過(guò)同時(shí)Gradle也提供了類似Maven的約定由于配置方式,這是通過(guò) Gradle的Java Plugin實(shí)現(xiàn)的,從文檔上看,Gradle是推薦這種方式的。Java Plugin定義了與Maven完全一致的項(xiàng)目布局:
src/main/java
src/main/resources
src/test/java
src/test/resources
區(qū)別在于,使用Groovy自定義項(xiàng)目布局更加的方便:
Gradle Java Plugin也定義了構(gòu)建生命周期,包括編譯主代碼、處理資源、編譯測(cè)試代碼、執(zhí)行測(cè)試、上傳歸檔等等任務(wù):
Figure 1. Gradle的構(gòu)建生命周期
相對(duì)于Maven完全線性的生命周期,Gradle的構(gòu)建生命周期略微復(fù)雜,不過(guò)也更為靈活,例如jar這個(gè)任務(wù)是用來(lái)打包的,它不像Maven那 樣依賴于執(zhí)行測(cè)試的test任務(wù),類似的,從圖中可以看到,一個(gè)最終的build任務(wù)也沒有依賴于uploadArchives任務(wù)。這個(gè)生命周期并沒有 將用戶限制得很死,舉個(gè)例子,我希望每次build都發(fā)布 SNAPSHOT版本到Maven倉(cāng)庫(kù)中,而且我只想使用最簡(jiǎn)單的$ gradle clean build命令,那只需要添加一行任務(wù)依賴配置即可:
build.dependsOn 'uploadArchives'
小結(jié)
一番體驗(yàn)下來(lái),Gradle給我最大的感覺是兩點(diǎn)。其一是簡(jiǎn)潔,基于Groovy的緊湊腳本實(shí)在讓人愛不釋手,在表述意圖方面也沒有什么不清晰的地 方。其二是靈活,各種在Maven中難以下手的事情,在Gradle就是小菜一碟,比如修改現(xiàn)有的構(gòu)建生命周期,幾行配置就完成了,同樣的事情,在 Maven中你必須編寫一個(gè)插件,那對(duì)于一個(gè)剛?cè)腴T的用戶來(lái)說(shuō),沒個(gè)一兩天幾乎是不可能完成的任務(wù)。
不過(guò)即使如此,Gradle在未來(lái)能否取代Maven,在我看來(lái)也還是個(gè)未知數(shù)。它的一大障礙就是Grovvy,幾乎所有 Java開發(fā)者都熟悉XML,可又有幾個(gè)人了解Groovy呢?學(xué)習(xí)成本這道坎是很難跨越的,很多人抵制Maven就是因?yàn)閷W(xué)起來(lái)不容易,你現(xiàn)在讓因?yàn)橐?個(gè)構(gòu)建工具學(xué)習(xí)一門新語(yǔ)言(即使這門語(yǔ)言和Java非常接近),那得到冷淡的回復(fù)幾乎是必然的事情。Gradle的另外一個(gè)問題就是它太靈活了,雖然它支 持約定優(yōu)于配置,不過(guò)從本文你也看到了,破壞約定是多么容易的事情。人都喜歡自由,愛自定義,覺得自己的需求是多么的特別,可事實(shí)上,從Maven的流行 來(lái)看,幾乎95%以上的情況你不需要自行擴(kuò)展,如果你這么做了,只會(huì)讓構(gòu)建變得難以理解。從這個(gè)角度來(lái)看,自由是把雙刃劍,Gradle給了你足夠的自 由,約定優(yōu)于配置只是它的一個(gè)選項(xiàng)而已,這初看起來(lái)很誘人,卻也可能使其重蹈Ant的覆轍。Maven在Ant的基礎(chǔ)上引入了依賴管理、倉(cāng)庫(kù)以及約定優(yōu)于 配置等概念,是一個(gè)很大的進(jìn)步,不過(guò)在我現(xiàn)在看來(lái),Gradle并沒有引入新的概念,給我感覺它是一個(gè)結(jié)合Ant和Maven理念的優(yōu)秀實(shí)現(xiàn)。
如果你了解Groovy,也理解Maven的約定優(yōu)于配置,那試試Gradle倒也不錯(cuò),尤其是它幾乎能和現(xiàn)有的Maven系統(tǒng)無(wú)縫集成,而且你也能享受到簡(jiǎn)潔帶來(lái)的極大樂趣。其實(shí)說(shuō)到簡(jiǎn)潔,也許在不久的將來(lái)Maven用戶也能直接享受到,Polyglot Maven在 這方面已經(jīng)做了不少工作。本文完全基于Maven的視角介紹Gradle這一構(gòu)建工具的新秀,不過(guò)限于篇幅原因,無(wú)法深入Gradle的方方面面,例如 Gradle也支持多模塊構(gòu)建,它提供了GUI操作界面,支持Grovvy(理所當(dāng)然)和Scala項(xiàng)目等等。有興趣的讀者可以自行進(jìn)一步了解。
關(guān)于作者
許曉斌(Juven Xu),國(guó)內(nèi)社區(qū)公認(rèn)的Maven技術(shù)專家、Maven中文用戶組創(chuàng)始人、Maven技術(shù)的先驅(qū)和積極推動(dòng)者,著有《Maven實(shí)戰(zhàn)》一 書。對(duì)Maven有深刻的認(rèn)識(shí),實(shí)戰(zhàn)經(jīng)驗(yàn)豐富,不僅撰寫了大量關(guān)于Maven的技術(shù)文章,而且還翻譯了開源書籍《Maven權(quán)威指南》,對(duì)Maven技術(shù) 在國(guó)內(nèi)的普及和發(fā)展做出了很大的貢獻(xiàn)。就職于Maven之父的公司,負(fù)責(zé)維護(hù)Maven中央倉(cāng)庫(kù),是Maven倉(cāng)庫(kù)管理器Nexus(著名開源軟件)的核 心開發(fā)者之一,曾多次受邀到淘寶等大型企業(yè)開展Maven方面的培訓(xùn)。此外,他還是開源技術(shù)的積極倡導(dǎo)者和推動(dòng)者,擅長(zhǎng)Java開發(fā)和敏捷開發(fā)實(shí)踐。他的 個(gè)人網(wǎng)站是:http://www.juvenxu.com。