深入了解gradle和maven的區(qū)別

簡介

gradle和maven都可以用來構(gòu)建java程序瓢阴,甚至在某些情況下络拌,兩者還可以互相轉(zhuǎn)換鬼癣,那么他們兩個的共同點和不同點是什么?我們?nèi)绾卧陧椖恐羞x擇使用哪種技術(shù)呢翘悉?一起來看看吧茫打。

gradle和maven的比較

雖然gradle和maven都可以作為java程序的構(gòu)建工具。但是兩者還是有很大的不同之處的妖混。我們可以從下面幾個方面來進(jìn)行分析老赤。

可擴(kuò)展性

Google選擇gradle作為android的構(gòu)建工具不是沒有理由的,其中一個非常重要的原因就是因為gradle夠靈活源葫。一方面是因為gradle使用的是groovy或者kotlin語言作為腳本的編寫語言诗越,這樣極大的提高了腳本的靈活性,但是其本質(zhì)上的原因是gradle的基礎(chǔ)架構(gòu)能夠支持這種靈活性息堂。

你可以使用gradle來構(gòu)建native的C/C++程序嚷狞,甚至擴(kuò)展到任何語言的構(gòu)建。

相對而言荣堰,maven的靈活性就差一些床未,并且自定義起來也比較麻煩,但是maven的項目比較容易看懂振坚,并且上手簡單薇搁。

所以如果你的項目沒有太多自定義構(gòu)建需求的話還是推薦使用maven,但是如果有自定義的構(gòu)建需求渡八,那么還是投入gradle的懷抱吧啃洋。

性能比較

雖然現(xiàn)在大家的機子性能都比較強勁,好像在做項目構(gòu)建的時候性能的優(yōu)勢并不是那么的迫切屎鳍,但是對于大型項目來說宏娄,一次構(gòu)建可能會需要很長的時間,尤其對于自動化構(gòu)建和CI的環(huán)境來說逮壁,當(dāng)然希望這個構(gòu)建是越快越好孵坚。

Gradle和Maven都支持并行的項目構(gòu)建和依賴解析。但是gradle的三個特點讓gradle可以跑的比maven快上一點:

  • 增量構(gòu)建

gradle為了提升構(gòu)建的效率窥淆,提出了增量構(gòu)建的概念卖宠,為了實現(xiàn)增量構(gòu)建,gradle將每一個task都分成了三部分忧饭,分別是input輸入扛伍,任務(wù)本身和output輸出。下圖是一個典型的java編譯的task蜒秤。

image

以上圖為例汁咏,input就是目標(biāo)jdk的版本亚斋,源代碼等作媚,output就是編譯出來的class文件。

增量構(gòu)建的原理就是監(jiān)控input的變化帅刊,只有input發(fā)送變化了纸泡,才重新執(zhí)行task任務(wù),否則gradle認(rèn)為可以重用之前的執(zhí)行結(jié)果赖瞒。

所以在編寫gradle的task的時候女揭,需要指定task的輸入和輸出。

并且要注意只有會對輸出結(jié)果產(chǎn)生變化的才能被稱為輸入栏饮,如果你定義了對初始結(jié)果完全無關(guān)的變量作為輸入吧兔,則這些變量的變化會導(dǎo)致gradle重新執(zhí)行task,導(dǎo)致了不必要的性能的損耗袍嬉。

還要注意不確定執(zhí)行結(jié)果的任務(wù)境蔼,比如說同樣的輸入可能會得到不同的輸出結(jié)果,那么這樣的任務(wù)將不能夠被配置為增量構(gòu)建任務(wù)伺通。

  • 構(gòu)建緩存

gradle可以重用同樣input的輸出作為緩存箍土,大家可能會有疑問了,這個緩存和增量編譯不是一個意思嗎罐监?

在同一個機子上是的吴藻,但是緩存可以跨機器共享.如果你是在一個CI服務(wù)的話,build cache將會非常有用弓柱。因為developer的build可以直接從CI服務(wù)器上面拉取構(gòu)建結(jié)果沟堡,非常的方便。

  • Gradle守護(hù)進(jìn)程

gradle會開啟一個守護(hù)進(jìn)程來和各個build任務(wù)進(jìn)行交互矢空,優(yōu)點就是不需要每次構(gòu)建都初始化需要的組件和服務(wù)航罗。

同時因為守護(hù)進(jìn)程是一個一直運行的進(jìn)程,除了可以避免每次JVM啟動的開銷之外妇多,還可以緩存項目結(jié)構(gòu)伤哺,文件,task和其他的信息者祖,從而提升運行速度立莉。

我們可以運行 gradle --status 來查看正在運行的daemons進(jìn)程。

從Gradle 3.0之后七问,daemons是默認(rèn)開啟的蜓耻,你可以使用 org.gradle.daemon=false 來禁止daemons。

我們可以通過下面的幾個圖來直觀的感受一下gradle和maven的性能比較:

  • 使用gradle和maven構(gòu)建 Apache Commons Lang 3的比較:
image
  • 使用gradle和maven構(gòu)建小項目(10個模塊械巡,每個模塊50個源文件和50個測試文件)的比較:
image
  • 使用gradle和maven構(gòu)建大項目(500個模塊刹淌,每個模塊100個源文件和100個測試文件)的比較:
image

可以看到gradle性能的提升是非常明顯的饶氏。

依賴的區(qū)別

gralde和maven都可以本地緩存依賴文件,并且都支持依賴文件的并行下載有勾。

在maven中只可以通過版本號來覆蓋一個依賴項疹启。而gradle更加靈活,你可以自定義依賴關(guān)系和替換規(guī)則蔼卡,通過這些替換規(guī)則喊崖,gradle可以構(gòu)建非常復(fù)雜的項目。

從maven遷移到gradle

因為maven出現(xiàn)的時間比較早雇逞,所以基本上所有的java項目都支持maven荤懂,但是并不是所有的項目都支持gradle。如果你有需要把maven項目遷移到gradle的想法塘砸,那么就一起來看看吧节仿。

根據(jù)我們之前的介紹,大家可以發(fā)現(xiàn)gradle和maven從本質(zhì)上來說就是不同的掉蔬,gradle通過task的DAG圖來組織任務(wù)廊宪,而maven則是通過attach到phases的goals來執(zhí)行任務(wù)。

雖然兩者的構(gòu)建有很大的不同眉踱,但是得益于gradle和maven相識的各種約定規(guī)則挤忙,從maven移植到gradle并不是那么難。

要想從maven移植到gradle谈喳,首先要了解下maven的build生命周期册烈,maven的生命周期包含了clean,compile婿禽,test赏僧,package,verify扭倾,install和deploy這幾個phase淀零。

我們需要將maven的生命周期phase轉(zhuǎn)換為gradle的生命周期task。這里需要使用到gradle的Base Plugin膛壹,Java Plugin和Maven Publish Plugin驾中。

先看下怎么引入這三個plugin:

plugins {
    id 'base'
    id 'java'
    id 'maven-publish'
}

clean會被轉(zhuǎn)換成為clean task,compile會被轉(zhuǎn)換成為classes task模聋,test會被轉(zhuǎn)換成為test task肩民,package會被轉(zhuǎn)換成為assemble task,verify 會被轉(zhuǎn)換成為check task链方,install會被轉(zhuǎn)換成為 Maven Publish Plugin 中的publishToMavenLocal task持痰,deploy 會被轉(zhuǎn)換成為Maven Publish Plugin 中的publish task。

有了這些task之間的對應(yīng)關(guān)系祟蚀,我們就可以嘗試進(jìn)行maven到gradle的轉(zhuǎn)換了工窍。

自動轉(zhuǎn)換

我們除了可以使用 gradle init 命令來創(chuàng)建一個gradle的架子之外割卖,還可以使用這個命令來將maven項目轉(zhuǎn)換成為gradle項目,gradle init命令會去讀取pom文件患雏,并將其轉(zhuǎn)換成為gradle項目鹏溯。

轉(zhuǎn)換依賴

gradle和maven的依賴都包含了group ID, artifact ID 和版本號。兩者本質(zhì)上是一樣的纵苛,只是形式不同剿涮,我們看一個轉(zhuǎn)換的例子:

<dependencies>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.12</version>
    </dependency>
</dependencies>

上是一個maven的例子,我們看下gradle的例子怎寫:

dependencies {
    implementation 'log4j:log4j:1.2.12'  
}

可以看到gradle比maven寫起來要簡單很多攻人。

注意這里的implementation實際上是由 Java Plugin 來實現(xiàn)的。

我們在maven的依賴中有時候還會用到scope選項悬槽,用來表示依賴的范圍怀吻,我們看下這些范圍該如何進(jìn)行轉(zhuǎn)換:

  • compile:

在gradle可以有兩種配置來替換compile,我們可以使用implementation或者api初婆。

前者在任何使用Java Plugin的gradle中都可以使用蓬坡,而api只能在使用Java Library Plugin的項目中使用。

當(dāng)然兩者是有區(qū)別的磅叛,如果你是構(gòu)建應(yīng)用程序或者webapp屑咳,那么推薦使用implementation,如果你是在構(gòu)建Java libraries弊琴,那么推薦使用api。

  • runtime:

可以替換成 runtimeOnly 。

  • test:

gradle中的test分為兩種诚欠,一種是編譯test項目的時候需要空另,那么可以使用testImplementation,一種是運行test項目的時候需要腋寨,那么可以使用testRuntimeOnly聪铺。

  • provided:

可以替換成為compileOnly。

  • import:

在maven中萄窜,import經(jīng)常用在dependencyManagement中铃剔,通常用來從一個pom文件中導(dǎo)入依賴項,從而保證項目中依賴項目版本的一致性查刻。

在gradle中键兜,可以使用 platform() 或者 enforcedPlatform() 來導(dǎo)入pom文件:

dependencies {
    implementation platform('org.springframework.boot:spring-boot-dependencies:1.5.8.RELEASE') 

    implementation 'com.google.code.gson:gson' 
    implementation 'dom4j:dom4j'
}

比如上面的例子中,我們導(dǎo)入了spring-boot-dependencies赖阻。因為這個pom中已經(jīng)定義了依賴項的版本號蝶押,所以我們在后面引入gson的時候就不需要指定版本號了。

platform和enforcedPlatform的區(qū)別在于火欧,enforcedPlatform會將導(dǎo)入的pom版本號覆蓋其他導(dǎo)入的版本號:

dependencies {
    // import a BOM. The versions used in this file will override any other version found in the graph
    implementation enforcedPlatform('org.springframework.boot:spring-boot-dependencies:1.5.8.RELEASE')

    // define dependencies without versions
    implementation 'com.google.code.gson:gson'
    implementation 'dom4j:dom4j'

    // this version will be overridden by the one found in the BOM
    implementation 'org.codehaus.groovy:groovy:1.8.6'
}

轉(zhuǎn)換repositories倉庫

gradle可以兼容使用maven或者lvy的repository棋电。gradle沒有默認(rèn)的倉庫地址茎截,所以你必須手動指定一個。

你可以在gradle使用maven的倉庫:

repositories {
    mavenCentral()
}

我們還可以直接指定maven倉庫的地址:

repositories {
    maven {
        url "http://repo.mycompany.com/maven2"
    }
}

如果你想使用maven本地的倉庫赶盔,則可以這樣使用:

repositories {
    mavenLocal()
}

但是mavenLocal是不推薦使用的企锌,為什么呢?

mavenLocal只是maven在本地的一個cache于未,它包含的內(nèi)容并不完整撕攒。比如說一個本地的maven repository module可能只包含了jar包文件,并沒有包含source或者javadoc文件烘浦。那么我們將不能夠在gradle中查看這個module的源代碼抖坪,因為gradle會首先在maven本地的路徑中查找這個module。

并且本地的repository是不可信任的闷叉,因為里面的內(nèi)容可以輕易被修改擦俐,并沒有任何的驗證機制。

控制依賴的版本

如果同一個項目中對同一個模塊有不同版本的兩個依賴的話握侧,默認(rèn)情況下Gradle會在解析完DAG之后蚯瞧,選擇版本最高的那個依賴包。

但是這樣做并不一定就是正確的品擎, 所以我們需要自定義依賴版本的功能埋合。

首先就是上面我們提到的使用platform()和enforcedPlatform() 來導(dǎo)入BOM(packaging類型是POM的)文件。

如果我們項目中依賴了某個module萄传,而這個module又依賴了另外的module甚颂,我們叫做傳遞依賴。在這種情況下盲再,如果我們希望控制傳遞依賴的版本西设,比如說將傳遞依賴的版本升級為一個新的版本,那么可以使用dependency constraints:

dependencies {
    implementation 'org.apache.httpcomponents:httpclient'
    constraints {
        implementation('org.apache.httpcomponents:httpclient:4.5.3') {
            because 'previous versions have a bug impacting this application'
        }
        implementation('commons-codec:commons-codec:1.11') {
            because 'version 1.9 pulled from httpclient has bugs affecting this application'
        }
    }
}

注意答朋,dependency constraints只對傳遞依賴有效贷揽,如果上面的例子中commons-codec并不是傳遞依賴,那么將不會有任何影響梦碗。

同時 Dependency constraints需要Gradle Module Metadata的支持禽绪,也就是說只有你的module是發(fā)布在gradle中才支持這個特性,如果是發(fā)布在maven或者ivy中是不支持的洪规。

上面講的是傳遞依賴的版本升級印屁。同樣是傳遞依賴,如果本項目也需要使用到這個傳遞依賴的module斩例,但是需要使用到更低的版本(因為默認(rèn)gradle會使用最新的版本)雄人,就需要用到版本降級了。

dependencies {
    implementation 'org.apache.httpcomponents:httpclient:4.5.4'
    implementation('commons-codec:commons-codec') {
        version {
            strictly '1.9'
        }
    }
}

我們可以在implementation中指定特定的version即可。

strictly表示的是強制匹配特定的版本號础钠,除了strictly之外恰力,還有require,表示需要的版本號大于等于給定的版本號旗吁。prefer踩萎,如果沒有指定其他的版本號,那么就使用prefer這個很钓。reject香府,拒絕使用這個版本。

除此之外码倦,你還可以使用Java Platform Plugin來指定特定的platform企孩,從而限制版本號。

最后看一下如何exclude一個依賴:

dependencies {
    implementation('commons-beanutils:commons-beanutils:1.9.4') {
        exclude group: 'commons-collections', module: 'commons-collections'
    }
}

多模塊項目

maven中可以創(chuàng)建多模塊項目:

<modules>
    <module>simple-weather</module>
    <module>simple-webapp</module>
</modules>

我們可以在gradle中做同樣的事情settings.gradle:

rootProject.name = 'simple-multi-module'  

include 'simple-weather', 'simple-webapp'  

profile和屬性

maven中可以使用profile來區(qū)別不同的環(huán)境叹洲,在gradle中柠硕,我們可以定義好不同的profile文件,然后通過腳本來加載他們:

build.gradle:

if (!hasProperty('buildProfile')) ext.buildProfile = 'default'  

apply from: "profile-${buildProfile}.gradle"  

task greeting {
    doLast {
        println message  
    }
}

profile-default.gradle:

ext.message = 'foobar'  

profile-test.gradle:

ext.message = 'testing 1 2 3'

我們可以這樣來運行:

> gradle greeting
foobar

> gradle -PbuildProfile=test greeting
testing 1 2 3

資源處理

在maven中有一個process-resources階段运提,可以執(zhí)行resources:resources用來進(jìn)行resource文件的拷貝操作。

在Gradle中的Java plugin的processResources task也可以做相同的事情闻葵。

比如我可以執(zhí)行copy任務(wù):

task copyReport(type: Copy) {
    from file("$buildDir/reports/my-report.pdf")
    into file("$buildDir/toArchive")
}

更加復(fù)雜的拷貝:

task copyPdfReportsForArchiving(type: Copy) {
    from "$buildDir/reports"
    include "*.pdf"
    into "$buildDir/toArchive"
}

當(dāng)然拷貝還有更加復(fù)雜的應(yīng)用民泵。這里就不詳細(xì)講解了。

本文已收錄于 http://www.flydean.com/gradle-vs-maven/

最通俗的解讀槽畔,最深刻的干貨栈妆,最簡潔的教程,眾多你不知道的小技巧等你來發(fā)現(xiàn)厢钧!

歡迎關(guān)注我的公眾號:「程序那些事」,懂技術(shù)鳞尔,更懂你!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末早直,一起剝皮案震驚了整個濱河市寥假,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌霞扬,老刑警劉巖糕韧,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異喻圃,居然都是意外死亡萤彩,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進(jìn)店門斧拍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來雀扶,“玉大人,你說我怎么就攤上這事肆汹∮弈梗” “怎么了予权?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長转绷。 經(jīng)常有香客問我伟件,道長,這世上最難降的妖魔是什么议经? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任斧账,我火速辦了婚禮,結(jié)果婚禮上煞肾,老公的妹妹穿的比我還像新娘咧织。我一直安慰自己,他們只是感情好籍救,可當(dāng)我...
    茶點故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布习绢。 她就那樣靜靜地躺著,像睡著了一般蝙昙。 火紅的嫁衣襯著肌膚如雪闪萄。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天奇颠,我揣著相機與錄音败去,去河邊找鬼。 笑死烈拒,一個胖子當(dāng)著我的面吹牛圆裕,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播荆几,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼吓妆,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了吨铸?” 一聲冷哼從身側(cè)響起行拢,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎焊傅,沒想到半個月后剂陡,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡狐胎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年鸭栖,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片握巢。...
    茶點故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡晕鹊,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情溅话,我是刑警寧澤晓锻,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站飞几,受9級特大地震影響砚哆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜屑墨,卻給世界環(huán)境...
    茶點故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一躁锁、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧卵史,春花似錦战转、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至忧设,卻和暖如春刁标,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背址晕。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工命雀, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人斩箫。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像撵儿,于是被迫代替她去往敵國和親乘客。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,512評論 2 359

推薦閱讀更多精彩內(nèi)容