Maven實戰(六)——Gradle

http://www.infoq.com/cn/news/2011/04/xxb-maven-6-gradle


Maven面臨的挑戰

軟件行業新舊交替的速度之快往往令人咂舌,不用多少時間,你就會發現曾經大紅大紫的技術已經成爲了昨日黃花,當然,Maven也不會例外。雖然目前它基本上是Java構建的事實標準,但我們也能看到新興的工具在涌現,比如基於Goovy的Gradle,而去年Hibernate宣佈從Maven遷移至Gradle這一事件更是吸引了不少眼球。在此之前,我也聽到了不少對Maven的抱怨,包括XML的繁冗,不夠靈活,學習曲線陡峭等等。那Gradle是否能夠在繼承 Maven優點的基礎上,克服這些缺點呢?帶着這個疑問,我開始閱讀Gradle的文檔並嘗試着將一個基於Maven的項目轉成用Gradle構建,本文所要講述大概就是這樣的一個體驗。需要注意的是,本文完全是基於Maven的角度來看Gradle的,因此對於Ant用戶來說,視角肯定會大有不同。

Gradle初體驗

Gradle的安裝非常方便,下載ZIP包,解壓到本地目錄,設置 GRADLE_HOME 環境變量並將 GRADLE_HOME/bin 加到 PATH 環境變量中,安裝就完成了。用戶可以運行gradle -v命令驗證安裝,這些初始的步驟和Maven沒什麼兩樣。Gradle目前的版本是1.0-milestone-1,根據其Wiki上的Roadmap,在1.0正式版發佈之前,還至少會有3個里程碑版本,而1.0的發佈日期最快也不會早於6月份。而正是這樣一個看起來似乎還不怎麼成熟的項目,卻有着讓很多成熟項目都汗顏的文檔,其包括了安裝指南、基本教程、以及一份近300頁的全面用戶指南。這對於用戶來說是非常友好的,同時也說明了Gradle的開發者對這個項目非常有信心,要知道編寫並維護文檔可不是件輕鬆的工作,對於Gradle這樣未來仍可能發生很大變動的項目來說尤爲如此。

類似於Maven的pom.xml文件,每個Gradle項目都需要有一個對應的build.gradle文件,該文件定義一些任務(task)來完成構建工作,當然,每個任務是可配置的,任務之間也可以依賴,用戶亦能配置缺省任務,就像這樣:

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

運行命令$ gradle -q之後(參數q讓Gradle不要打印錯誤之外的日誌),就能看到如下的預期輸出:

i'm task A
i'm task B, and I depend on taskA

這不是和Ant如出一轍麼?的確是這樣,這種“任務”的概念與用法與Ant及其相似。Ant任務是Gradle世界的第一公民,Gradle對Ant做了很好的集成。除此之外,由於Gradle使用的Grovvy腳本較XML更爲靈活,因此,即使我自己不是Ant用戶,我也仍然覺得Ant用戶會喜歡上Gradle。

依賴管理和集成Maven倉庫

我們知道依賴管理、倉庫、約定優於配置等概念是Maven的核心內容,拋開其實現是否最優不談,概念本身沒什麼問題,並且已經被廣泛學習和接受。那Gradle實現了這些優秀概念了麼?答案是肯定的。

先看依賴管理,我有一個簡單的項目依賴於一些第三方類庫包括SpringFramework、JUnit、Kaptcha等等。原來的Maven POM配置大概是這樣的(篇幅關係,省略了部分父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>

然後我將其轉換成Gradle腳本,結果是驚人的:

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')
}

注意配置從原來的28行縮減至7行!這還不算我省略的一些父POM配置。依賴的groupId、artifactId、 version,scope甚至是classfier,一點都不少。較之於Maven或者Ant的XML配置腳本,Gradle使用的Grovvy腳本殺傷力太大了,愛美之心,人皆有之,相比於七旬老婦鬆鬆垮垮的皺紋,大家肯定都喜歡少女緊緻的臉蛋,XML就是那老婦的皺紋。

關於Gradle的依賴管理起初我有一點擔心,就是它是否有傳遞性依賴的機制呢?經過文檔閱讀和實際試驗後,這個疑慮打消了,Gradle能夠解析現有的Maven POM或者Ivy的XML配置,從而得到傳遞性依賴的信息,並且引入到當前項目中,這實在是一個聰明的做法。在此基礎上,它也支持排除傳遞性依賴或者乾脆關閉傳遞性依賴,其中第二點是Maven所不具備的特性。

自動化依賴管理的基石是倉庫,Maven中央倉庫已經成爲了Java開發者不可或缺的資源,Gradle既然有依賴管理,那必然也得用到倉庫,這當然也包括了Maven中央倉庫,就像這樣:

repositories {
    mavenLocal()
    mavenCentral()
    mavenRepo urls: "http://repository.sonatype.org/content/groups/forge/"
}

這段代碼幾乎不用解釋,就是在Gradle中配置使用Maven本地倉庫、中央倉庫、以及自定義地址倉庫。在我實際構建項目的時候,能看到終端打印的下載信息,下載後的文件被存儲在USER_HOME/.gradle/cache/ 目錄下供項目使用,這種實現的方法與Maven又是及其類似了,可以說Gradle不僅最大限度的繼承Maven的很多理念,倉庫資源也是直接拿來用。

Gradle項目使用Maven項目生成的資源已經不是個問題了,接着需要反過來考慮,Maven用戶是否能夠使用 Gradle生成的資源呢?或者更簡單點問,Gradle項目生成的構件是否可以發佈到Maven倉庫中供人使用呢?這一點非常重要,因爲如果做不到這一點,你可能就會丟失大量的用戶。幸運的是Gradle再次給出了令人滿意的答案。使用Gradle的Maven Plugin,用戶就可以輕鬆地將項目構件上傳到Maven倉庫中:

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"
        }
    }
}

在上傳的過程中,Gradle能夠基於build.gradle生成對應的Maven POM文件,用戶可以自行配置POM信息,比如這裏的groupId和artifactId,而諸如依賴配置這樣的內容,Gradle是會自動幫你進行轉換的。由於Maven項目之間依賴交互的直接途徑就是倉庫,而Gradle既能夠使用Maven倉庫,也能以Maven的格式將自己的內容發佈到倉庫中,因此從技術角度來說,即使在一個基於Maven的大環境中,局部使用Gradle也幾乎不會是一個問題。

約定優於配置

如同Ant一般,Gradle給了用戶足夠的自由去定義自己的任務,不過同時Gradle也提供了類似Maven的約定由於配置方式,這是通過Gradle的Java Plugin實現的,從文檔上看,Gradle是推薦這種方式的。Java Plugin定義了與Maven完全一致的項目佈局:

  • src/main/java

  • src/main/resources

  • src/test/java

  • src/test/resources

區別在於,使用Groovy自定義項目佈局更加的方便:

sourceSets {
    main {
        java {
            srcDir 'src/java'
        }
        resources {
            srcDir 'src/resources'
        }
    }
}

Gradle Java Plugin也定義了構建生命週期,包括編譯主代碼、處理資源、編譯測試代碼、執行測試、上傳歸檔等等任務:

Figure 1. Gradle的構建生命週期

相對於Maven完全線性的生命週期,Gradle的構建生命週期略微複雜,不過也更爲靈活,例如jar這個任務是用來打包的,它不像Maven那樣依賴於執行測試的test任務,類似的,從圖中可以看到,一個最終的build任務也沒有依賴於uploadArchives任務。這個生命週期並沒有將用戶限制得很死,舉個例子,我希望每次build都發布 SNAPSHOT版本到Maven倉庫中,而且我只想使用最簡單的$ gradle clean build命令,那只需要添加一行任務依賴配置即可:

build.dependsOn 'uploadArchives'

由於Gradle完全是基於靈活的任務模型,因此很多事情包括覆蓋現有任務,跳過任務都非常易於實現。而這些事情,在Maven的世界中,實現起來就比較的麻煩,或者說Maven壓根就不希望用戶這麼做。

小結

一番體驗下來,Gradle給我最大的感覺是兩點。其一是簡潔,基於Groovy的緊湊腳本實在讓人愛不釋手,在表述意圖方面也沒有什麼不清晰的地方。其二是靈活,各種在Maven中難以下手的事情,在Gradle就是小菜一碟,比如修改現有的構建生命週期,幾行配置就完成了,同樣的事情,在Maven中你必須編寫一個插件,那對於一個剛入門的用戶來說,沒個一兩天幾乎是不可能完成的任務。

不過即使如此,Gradle在未來能否取代Maven,在我看來也還是個未知數。它的一大障礙就是Grovvy,幾乎所有 Java開發者都熟悉XML,可又有幾個人瞭解Groovy呢?學習成本這道坎是很難跨越的,很多人抵制Maven就是因爲學起來不容易,你現在讓因爲一個構建工具學習一門新語言(即使這門語言和Java非常接近),那得到冷淡的回覆幾乎是必然的事情。Gradle的另外一個問題就是它太靈活了,雖然它支持約定優於配置,不過從本文你也看到了,破壞約定是多麼容易的事情。人都喜歡自由,愛自定義,覺得自己的需求是多麼的特別,可事實上,從Maven的流行來看,幾乎95%以上的情況你不需要自行擴展,如果你這麼做了,只會讓構建變得難以理解。從這個角度來看,自由是把雙刃劍,Gradle給了你足夠的自由,約定優於配置只是它的一個選項而已,這初看起來很誘人,卻也可能使其重蹈Ant的覆轍。Maven在Ant的基礎上引入了依賴管理、倉庫以及約定優於配置等概念,是一個很大的進步,不過在我現在看來,Gradle並沒有引入新的概念,給我感覺它是一個結合Ant和Maven理念的優秀實現。

如果你瞭解Groovy,也理解Maven的約定優於配置,那試試Gradle倒也不錯,尤其是它幾乎能和現有的Maven系統無縫集成,而且你也能享受到簡潔帶來的極大樂趣。其實說到簡潔,也許在不久的將來Maven用戶也能直接享受到,Polyglot Maven在這方面已經做了不少工作。本文完全基於Maven的視角介紹Gradle這一構建工具的新秀,不過限於篇幅原因,無法深入Gradle的方方面面,例如Gradle也支持多模塊構建,它提供了GUI操作界面,支持Grovvy(理所當然)和Scala項目等等。有興趣的讀者可以自行進一步瞭解。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章