gradle和maven有哪些區(qū)別
gradle和maven都可以用來(lái)構(gòu)建java程序,甚至在某些情況下,兩者還可以互相轉(zhuǎn)換,那么他們兩個(gè)的共同點(diǎn)和不同點(diǎn)是什么?我們?nèi)绾卧陧?xiàng)目中選擇使用哪種技術(shù)呢?一起來(lái)看看吧。
gradle和maven的比較
雖然gradle和maven都可以作為java程序的構(gòu)建工具。但是兩者還是有很大的不同之處的。我們可以從下面幾個(gè)方面來(lái)進(jìn)行分析。
可擴(kuò)展性
Google選擇gradle作為android的構(gòu)建工具不是沒(méi)有理由的,其中一個(gè)非常重要的原因就是因?yàn)間radle夠靈活。一方面是因?yàn)間radle使用的是groovy或者kotlin語(yǔ)言作為腳本的編寫(xiě)語(yǔ)言,這樣極大的提高了腳本的靈活性,但是其本質(zhì)上的原因是gradle的基礎(chǔ)架構(gòu)能夠支持這種靈活性。
你可以使用gradle來(lái)構(gòu)建native的C/C++程序,甚至擴(kuò)展到任何語(yǔ)言的構(gòu)建。
相對(duì)而言,maven的靈活性就差一些,并且自定義起來(lái)也比較麻煩,但是maven的項(xiàng)目比較容易看懂,并且上手簡(jiǎn)單。
所以如果你的項(xiàng)目沒(méi)有太多自定義構(gòu)建需求的話(huà)還是推薦使用maven,但是如果有自定義的構(gòu)建需求,那么還是投入gradle的懷抱吧。
性能比較
雖然現(xiàn)在大家的機(jī)子性能都比較強(qiáng)勁,好像在做項(xiàng)目構(gòu)建的時(shí)候性能的優(yōu)勢(shì)并不是那么的迫切,但是對(duì)于大型項(xiàng)目來(lái)說(shuō),一次構(gòu)建可能會(huì)需要很長(zhǎng)的時(shí)間,尤其對(duì)于自動(dòng)化構(gòu)建和CI的環(huán)境來(lái)說(shuō),當(dāng)然希望這個(gè)構(gòu)建是越快越好。
Gradle和Maven都支持并行的項(xiàng)目構(gòu)建和依賴(lài)解析。但是gradle的三個(gè)特點(diǎn)讓gradle可以跑的比maven快上一點(diǎn):
增量構(gòu)建
gradle為了提升構(gòu)建的效率,提出了增量構(gòu)建的概念,為了實(shí)現(xiàn)增量構(gòu)建,gradle將每一個(gè)task都分成了三部分,分別是input輸入,任務(wù)本身和output輸出。下圖是一個(gè)典型的java編譯的task。

以上圖為例,input就是目標(biāo)jdk的版本,源代碼等,output就是編譯出來(lái)的class文件。
增量構(gòu)建的原理就是監(jiān)控input的變化,只有input發(fā)送變化了,才重新執(zhí)行task任務(wù),否則gradle認(rèn)為可以重用之前的執(zhí)行結(jié)果。
所以在編寫(xiě)gradle的task的時(shí)候,需要指定task的輸入和輸出。
并且要注意只有會(huì)對(duì)輸出結(jié)果產(chǎn)生變化的才能被稱(chēng)為輸入,如果你定義了對(duì)初始結(jié)果完全無(wú)關(guān)的變量作為輸入,則這些變量的變化會(huì)導(dǎo)致gradle重新執(zhí)行task,導(dǎo)致了不必要的性能的損耗。
還要注意不確定執(zhí)行結(jié)果的任務(wù),比如說(shuō)同樣的輸入可能會(huì)得到不同的輸出結(jié)果,那么這樣的任務(wù)將不能夠被配置為增量構(gòu)建任務(wù)。
構(gòu)建緩存
gradle可以重用同樣input的輸出作為緩存,大家可能會(huì)有疑問(wèn)了,這個(gè)緩存和增量編譯不是一個(gè)意思嗎?
在同一個(gè)機(jī)子上是的,但是緩存可以跨機(jī)器共享.如果你是在一個(gè)CI服務(wù)的話(huà),build cache將會(huì)非常有用。因?yàn)閐eveloper的build可以直接從CI服務(wù)器上面拉取構(gòu)建結(jié)果,非常的方便。
Gradle守護(hù)進(jìn)程
gradle會(huì)開(kāi)啟一個(gè)守護(hù)進(jìn)程來(lái)和各個(gè)build任務(wù)進(jìn)行交互,優(yōu)點(diǎn)就是不需要每次構(gòu)建都初始化需要的組件和服務(wù)。
同時(shí)因?yàn)槭刈o(hù)進(jìn)程是一個(gè)一直運(yùn)行的進(jìn)程,除了可以避免每次JVM啟動(dòng)的開(kāi)銷(xiāo)之外,還可以緩存項(xiàng)目結(jié)構(gòu),文件,task和其他的信息,從而提升運(yùn)行速度。
我們可以運(yùn)行 gradle –status 來(lái)查看正在運(yùn)行的daemons進(jìn)程。
從Gradle 3.0之后,daemons是默認(rèn)開(kāi)啟的,你可以使用 org.gradle.daemon=false 來(lái)禁止daemons。
我們可以通過(guò)下面的幾個(gè)圖來(lái)直觀的感受一下gradle和maven的性能比較:
使用gradle和maven構(gòu)建 Apache Commons Lang 3的比較:

使用gradle和maven構(gòu)建小項(xiàng)目(10個(gè)模塊,每個(gè)模塊50個(gè)源文件和50個(gè)測(cè)試文件)的比較:

使用gradle和maven構(gòu)建大項(xiàng)目(500個(gè)模塊,每個(gè)模塊100個(gè)源文件和100個(gè)測(cè)試文件)的比較:

可以看到gradle性能的提升是非常明顯的。
依賴(lài)的區(qū)別
gralde和maven都可以本地緩存依賴(lài)文件,并且都支持依賴(lài)文件的并行下載。
在maven中只可以通過(guò)版本號(hào)來(lái)覆蓋一個(gè)依賴(lài)項(xiàng)。而gradle更加靈活,你可以自定義依賴(lài)關(guān)系和替換規(guī)則,通過(guò)這些替換規(guī)則,gradle可以構(gòu)建非常復(fù)雜的項(xiàng)目。
從maven遷移到gradle
因?yàn)閙aven出現(xiàn)的時(shí)間比較早,所以基本上所有的java項(xiàng)目都支持maven,但是并不是所有的項(xiàng)目都支持gradle。如果你有需要把maven項(xiàng)目遷移到gradle的想法,那么就一起來(lái)看看吧。
根據(jù)我們之前的介紹,大家可以發(fā)現(xiàn)gradle和maven從本質(zhì)上來(lái)說(shuō)就是不同的,gradle通過(guò)task的DAG圖來(lái)組織任務(wù),而maven則是通過(guò)attach到phases的goals來(lái)執(zhí)行任務(wù)。
雖然兩者的構(gòu)建有很大的不同,但是得益于gradle和maven相識(shí)的各種約定規(guī)則,從maven移植到gradle并不是那么難。
要想從maven移植到gradle,首先要了解下maven的build生命周期,maven的生命周期包含了clean,compile,test,package,verify,install和deploy這幾個(gè)phase。
我們需要將maven的生命周期phase轉(zhuǎn)換為gradle的生命周期task。這里需要使用到gradle的Base Plugin,Java Plugin和Maven Publish Plugin。
先看下怎么引入這三個(gè)plugin:
plugins {
id 'base'
id 'java'
id 'maven-publish'
}
clean會(huì)被轉(zhuǎn)換成為clean task,compile會(huì)被轉(zhuǎn)換成為classes task,test會(huì)被轉(zhuǎn)換成為test task,package會(huì)被轉(zhuǎn)換成為assemble task,verify 會(huì)被轉(zhuǎn)換成為check task,install會(huì)被轉(zhuǎn)換成為 Maven Publish Plugin 中的publishToMavenLocal task,deploy 會(huì)被轉(zhuǎn)換成為Maven Publish Plugin 中的publish task。
有了這些task之間的對(duì)應(yīng)關(guān)系,我們就可以嘗試進(jìn)行maven到gradle的轉(zhuǎn)換了。
自動(dòng)轉(zhuǎn)換
我們除了可以使用 gradle init 命令來(lái)創(chuàng)建一個(gè)gradle的架子之外,還可以使用這個(gè)命令來(lái)將maven項(xiàng)目轉(zhuǎn)換成為gradle項(xiàng)目,gradle init命令會(huì)去讀取pom文件,并將其轉(zhuǎn)換成為gradle項(xiàng)目。
轉(zhuǎn)換依賴(lài)
gradle和maven的依賴(lài)都包含了group ID, artifact ID 和版本號(hào)。兩者本質(zhì)上是一樣的,只是形式不同,我們看一個(gè)轉(zhuǎn)換的例子:
<dependencies>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
</dependencies>
上是一個(gè)maven的例子,我們看下gradle的例子怎寫(xiě):
dependencies {
implementation 'log4j:log4j:1.2.12'
}
可以看到gradle比maven寫(xiě)起來(lái)要簡(jiǎn)單很多。
注意這里的implementation實(shí)際上是由 Java Plugin 來(lái)實(shí)現(xiàn)的。
我們?cè)趍aven的依賴(lài)中有時(shí)候還會(huì)用到scope選項(xiàng),用來(lái)表示依賴(lài)的范圍,我們看下這些范圍該如何進(jìn)行轉(zhuǎn)換:
compile:
在gradle可以有兩種配置來(lái)替換compile,我們可以使用implementation或者api。
前者在任何使用Java Plugin的gradle中都可以使用,而api只能在使用Java Library Plugin的項(xiàng)目中使用。
當(dāng)然兩者是有區(qū)別的,如果你是構(gòu)建應(yīng)用程序或者webapp,那么推薦使用implementation,如果你是在構(gòu)建Java libraries,那么推薦使用api。
runtime:
可以替換成 runtimeOnly 。
test:
gradle中的test分為兩種,一種是編譯test項(xiàng)目的時(shí)候需要,那么可以使用testImplementation,一種是運(yùn)行test項(xiàng)目的時(shí)候需要,那么可以使用testRuntimeOnly。
provided:
可以替換成為compileOnly。
import:
在maven中,import經(jīng)常用在dependencyManagement中,通常用來(lái)從一個(gè)pom文件中導(dǎo)入依賴(lài)項(xiàng),從而保證項(xiàng)目中依賴(lài)項(xiàng)目版本的一致性。
在gradle中,可以使用 platform() 或者 enforcedPlatform() 來(lái)導(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。因?yàn)檫@個(gè)pom中已經(jīng)定義了依賴(lài)項(xiàng)的版本號(hào),所以我們?cè)诤竺嬉雊son的時(shí)候就不需要指定版本號(hào)了。
platform和enforcedPlatform的區(qū)別在于,enforcedPlatform會(huì)將導(dǎo)入的pom版本號(hào)覆蓋其他導(dǎo)入的版本號(hà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倉(cāng)庫(kù)
gradle可以兼容使用maven或者lvy的repository。gradle沒(méi)有默認(rèn)的倉(cāng)庫(kù)地址,所以你必須手動(dòng)指定一個(gè)。
你可以在gradle使用maven的倉(cāng)庫(kù):
repositories {
mavenCentral()
}
我們還可以直接指定maven倉(cāng)庫(kù)的地址:
repositories {
maven {
url "http://repo.mycompany.com/maven2"
}
}
如果你想使用maven本地的倉(cāng)庫(kù),則可以這樣使用:
repositories {
mavenLocal()
}
但是mavenLocal是不推薦使用的,為什么呢?
mavenLocal只是maven在本地的一個(gè)cache,它包含的內(nèi)容并不完整。比如說(shuō)一個(gè)本地的maven repository module可能只包含了jar包文件,并沒(méi)有包含source或者javadoc文件。那么我們將不能夠在gradle中查看這個(gè)module的源代碼,因?yàn)間radle會(huì)首先在maven本地的路徑中查找這個(gè)module。
并且本地的repository是不可信任的,因?yàn)槔锩娴膬?nèi)容可以輕易被修改,并沒(méi)有任何的驗(yàn)證機(jī)制。
控制依賴(lài)的版本
如果同一個(gè)項(xiàng)目中對(duì)同一個(gè)模塊有不同版本的兩個(gè)依賴(lài)的話(huà),默認(rèn)情況下Gradle會(huì)在解析完DAG之后,選擇版本最高的那個(gè)依賴(lài)包。
但是這樣做并不一定就是正確的, 所以我們需要自定義依賴(lài)版本的功能。
首先就是上面我們提到的使用platform()和enforcedPlatform() 來(lái)導(dǎo)入BOM(packaging類(lèi)型是POM的)文件。
如果我們項(xiàng)目中依賴(lài)了某個(gè)module,而這個(gè)module又依賴(lài)了另外的module,我們叫做傳遞依賴(lài)。在這種情況下,如果我們希望控制傳遞依賴(lài)的版本,比如說(shuō)將傳遞依賴(lài)的版本升級(jí)為一個(gè)新的版本,那么可以使用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只對(duì)傳遞依賴(lài)有效,如果上面的例子中commons-codec并不是傳遞依賴(lài),那么將不會(huì)有任何影響。
同時(shí) Dependency constraints需要Gradle Module Metadata的支持,也就是說(shuō)只有你的module是發(fā)布在gradle中才支持這個(gè)特性,如果是發(fā)布在maven或者ivy中是不支持的。
上面講的是傳遞依賴(lài)的版本升級(jí)。同樣是傳遞依賴(lài),如果本項(xiàng)目也需要使用到這個(gè)傳遞依賴(lài)的module,但是需要使用到更低的版本(因?yàn)槟J(rèn)gradle會(huì)使用最新的版本),就需要用到版本降級(jí)了。
dependencies {
implementation 'org.apache.httpcomponents:httpclient:4.5.4'
implementation('commons-codec:commons-codec') {
version {
strictly '1.9'
}
}
}
我們可以在implementation中指定特定的version即可。
strictly表示的是強(qiáng)制匹配特定的版本號(hào),除了strictly之外,還有require,表示需要的版本號(hào)大于等于給定的版本號(hào)。prefer,如果沒(méi)有指定其他的版本號(hào),那么就使用prefer這個(gè)。reject,拒絕使用這個(gè)版本。
除此之外,你還可以使用Java Platform Plugin來(lái)指定特定的platform,從而限制版本號(hào)。
最后看一下如何exclude一個(gè)依賴(lài):
dependencies {
implementation('commons-beanutils:commons-beanutils:1.9.4') {
exclude group: 'commons-collections', module: 'commons-collections'
}
}
多模塊項(xiàng)目
maven中可以創(chuàng)建多模塊項(xià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來(lái)區(qū)別不同的環(huán)境,在gradle中,我們可以定義好不同的profile文件,然后通過(guò)腳本來(lái)加載他們:
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'
我們可以這樣來(lái)運(yùn)行:
> gradle greeting foobar > gradle -PbuildProfile=test greeting testing 1 2 3
資源處理
在maven中有一個(gè)process-resources階段,可以執(zhí)行resources:resources用來(lái)進(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ì)講解了。
以上就是gradle和maven有哪些區(qū)別的詳細(xì)內(nèi)容,更多關(guān)于gradle和maven 區(qū)別的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
解決使用httpclient傳遞json數(shù)據(jù)亂碼的問(wèn)題
這篇文章主要介紹了解決使用httpclient傳遞json數(shù)據(jù)亂碼的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-01-01
springboot自定義yml配置文件及其外部部署過(guò)程
這篇文章主要介紹了springboot自定義yml配置文件及其外部部署過(guò)程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03
Java數(shù)據(jù)結(jié)構(gòu)及算法實(shí)例:插入排序 Insertion Sort
這篇文章主要介紹了Java數(shù)據(jù)結(jié)構(gòu)及算法實(shí)例:插入排序 Insertion Sort,本文直接給出實(shí)例代碼,代碼中包含詳細(xì)注釋,需要的朋友可以參考下2015-06-06
最新Spring?Security實(shí)戰(zhàn)教程之表單登錄定制到處理邏輯的深度改造(最新推薦)
本章節(jié)介紹了如何通過(guò)Spring Security實(shí)現(xiàn)從配置自定義登錄頁(yè)面、表單登錄處理邏輯的配置,并簡(jiǎn)單模擬了前后分離的適配方案,本章節(jié)我們將Spring?Security?默認(rèn)表單進(jìn)行登錄定制到處理邏輯的深度改造,感興趣的朋友一起看看吧2025-03-03
idea報(bào)錯(cuò)之找不到符號(hào):類(lèi)的問(wèn)題及解決
這篇文章主要介紹了idea報(bào)錯(cuò)之找不到符號(hào):類(lèi)的問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12
Elasticsearch Join字段類(lèi)型簡(jiǎn)單快速上手教程
這篇文章主要為大家介紹了Elasticsearch Join字段類(lèi)型簡(jiǎn)單快速上手教程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09

