深刻了解gradle和maven的區別

簡介

gradle和maven均可以用來構建java程序,甚至在某些狀況下,二者還能夠互相轉換,那麼他們兩個的共同點和不一樣點是什麼?咱們如何在項目中選擇使用哪一種技術呢?一塊兒來看看吧。java

gradle和maven的比較

雖然gradle和maven均可以做爲java程序的構建工具。可是二者仍是有很大的不一樣之處的。咱們能夠從下面幾個方面來進行分析。android

可擴展性

Google選擇gradle做爲android的構建工具不是沒有理由的,其中一個很是重要的緣由就是由於gradle夠靈活。一方面是由於gradle使用的是groovy或者kotlin語言做爲腳本的編寫語言,這樣極大的提升了腳本的靈活性,可是其本質上的緣由是gradle的基礎架構可以支持這種靈活性。web

你可使用gradle來構建native的C/C++程序,甚至擴展到任何語言的構建。spring

相對而言,maven的靈活性就差一些,而且自定義起來也比較麻煩,可是maven的項目比較容易看懂,而且上手簡單。apache

因此若是你的項目沒有太多自定義構建需求的話仍是推薦使用maven,可是若是有自定義的構建需求,那麼仍是投入gradle的懷抱吧。api

性能比較

雖然如今你們的機子性能都比較強勁,好像在作項目構建的時候性能的優點並非那麼的迫切,可是對於大型項目來講,一次構建可能會須要很長的時間,尤爲對於自動化構建和CI的環境來講,固然但願這個構建是越快越好。緩存

Gradle和Maven都支持並行的項目構建和依賴解析。可是gradle的三個特色讓gradle能夠跑的比maven快上一點:服務器

  • 增量構建

gradle爲了提高構建的效率,提出了增量構建的概念,爲了實現增量構建,gradle將每個task都分紅了三部分,分別是input輸入,任務自己和output輸出。下圖是一個典型的java編譯的task。markdown

以上圖爲例,input就是目標jdk的版本,源代碼等,output就是編譯出來的class文件。架構

增量構建的原理就是監控input的變化,只有input發送變化了,才從新執行task任務,不然gradle認爲能夠重用以前的執行結果。

因此在編寫gradle的task的時候,須要指定task的輸入和輸出。

而且要注意只有會對輸出結果產生變化的才能被稱爲輸入,若是你定義了對初始結果徹底無關的變量做爲輸入,則這些變量的變化會致使gradle從新執行task,致使了沒必要要的性能的損耗。

還要注意不肯定執行結果的任務,好比說一樣的輸入可能會獲得不一樣的輸出結果,那麼這樣的任務將不可以被配置爲增量構建任務。

  • 構建緩存

gradle能夠重用一樣input的輸出做爲緩存,你們可能會有疑問了,這個緩存和增量編譯不是一個意思嗎?

在同一個機子上是的,可是緩存能夠跨機器共享.若是你是在一個CI服務的話,build cache將會很是有用。由於developer的build能夠直接從CI服務器上面拉取構建結果,很是的方便。

  • Gradle守護進程

gradle會開啓一個守護進程來和各個build任務進行交互,優勢就是不須要每次構建都初始化須要的組件和服務。

同時由於守護進程是一個一直運行的進程,除了能夠避免每次JVM啓動的開銷以外,還能夠緩存項目結構,文件,task和其餘的信息,從而提高運行速度。

咱們能夠運行 gradle --status 來查看正在運行的daemons進程。

從Gradle 3.0以後,daemons是默認開啓的,你可使用 org.gradle.daemon=false 來禁止daemons。

咱們能夠經過下面的幾個圖來直觀的感覺一下gradle和maven的性能比較:

  • 使用gradle和maven構建 Apache Commons Lang 3的比較:

  • 使用gradle和maven構建小項目(10個模塊,每一個模塊50個源文件和50個測試文件)的比較:

  • 使用gradle和maven構建大項目(500個模塊,每一個模塊100個源文件和100個測試文件)的比較:

能夠看到gradle性能的提高是很是明顯的。

依賴的區別

gralde和maven均可以本地緩存依賴文件,而且都支持依賴文件的並行下載。

在maven中只能夠經過版本號來覆蓋一個依賴項。而gradle更加靈活,你能夠自定義依賴關係和替換規則,經過這些替換規則,gradle能夠構建很是複雜的項目。

從maven遷移到gradle

由於maven出現的時間比較早,因此基本上全部的java項目都支持maven,可是並非全部的項目都支持gradle。若是你有須要把maven項目遷移到gradle的想法,那麼就一塊兒來看看吧。

根據咱們以前的介紹,你們能夠發現gradle和maven從本質上來講就是不一樣的,gradle經過task的DAG圖來組織任務,而maven則是經過attach到phases的goals來執行任務。

雖然二者的構建有很大的不一樣,可是得益於gradle和maven相識的各類約定規則,從maven移植到gradle並非那麼難。

要想從maven移植到gradle,首先要了解下maven的build生命週期,maven的生命週期包含了clean,compile,test,package,verify,install和deploy這幾個phase。

咱們須要將maven的生命週期phase轉換爲gradle的生命週期task。這裏須要使用到gradle的Base Plugin,Java Plugin和Maven Publish Plugin。

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

plugins {
    id 'base'
    id 'java'
    id 'maven-publish'
}
複製代碼

clean會被轉換成爲clean task,compile會被轉換成爲classes task,test會被轉換成爲test task,package會被轉換成爲assemble task,verify 會被轉換成爲check task,install會被轉換成爲 Maven Publish Plugin 中的publishToMavenLocal task,deploy 會被轉換成爲Maven Publish Plugin 中的publish task。

有了這些task之間的對應關係,咱們就能夠嘗試進行maven到gradle的轉換了。

自動轉換

咱們除了可使用 gradle init 命令來建立一個gradle的架子以外,還可使用這個命令來將maven項目轉換成爲gradle項目,gradle init命令會去讀取pom文件,並將其轉換成爲gradle項目。

轉換依賴

gradle和maven的依賴都包含了group ID, artifact ID 和版本號。二者本質上是同樣的,只是形式不一樣,咱們看一個轉換的例子:

<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 來實現的。

咱們在maven的依賴中有時候還會用到scope選項,用來表示依賴的範圍,咱們看下這些範圍該如何進行轉換:

  • compile:

在gradle能夠有兩種配置來替換compile,咱們可使用implementation或者api。

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

固然二者是有區別的,若是你是構建應用程序或者webapp,那麼推薦使用implementation,若是你是在構建Java libraries,那麼推薦使用api。

  • runtime:

能夠替換成 runtimeOnly 。

  • test:

gradle中的test分爲兩種,一種是編譯test項目的時候須要,那麼可使用testImplementation,一種是運行test項目的時候須要,那麼可使用testRuntimeOnly。

  • provided:

能夠替換成爲compileOnly。

  • import:

在maven中,import常常用在dependencyManagement中,一般用來從一個pom文件中導入依賴項,從而保證項目中依賴項目版本的一致性。

在gradle中,可使用 platform() 或者 enforcedPlatform() 來導入pom文件:

dependencies {
    implementation platform('org.springframework.boot:spring-boot-dependencies:1.5.8.RELEASE') implementation 'com.google.code.gson:gson' implementation 'dom4j:dom4j' } 複製代碼

好比上面的例子中,咱們導入了spring-boot-dependencies。由於這個pom中已經定義了依賴項的版本號,因此咱們在後面引入gson的時候就不須要指定版本號了。

platform和enforcedPlatform的區別在於,enforcedPlatform會將導入的pom版本號覆蓋其餘導入的版本號:

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' } 複製代碼

轉換repositories倉庫

gradle能夠兼容使用maven或者lvy的repository。gradle沒有默認的倉庫地址,因此你必須手動指定一個。

你能夠在gradle使用maven的倉庫:

repositories {
    mavenCentral()
}
複製代碼

咱們還能夠直接指定maven倉庫的地址:

repositories {
    maven {
        url "http://repo.mycompany.com/maven2"
    }
}
複製代碼

若是你想使用maven本地的倉庫,則能夠這樣使用:

repositories {
    mavenLocal()
}
複製代碼

可是mavenLocal是不推薦使用的,爲何呢?

mavenLocal只是maven在本地的一個cache,它包含的內容並不完整。好比說一個本地的maven repository module可能只包含了jar包文件,並無包含source或者javadoc文件。那麼咱們將不可以在gradle中查看這個module的源代碼,由於gradle會首先在maven本地的路徑中查找這個module。

而且本地的repository是不可信任的,由於裏面的內容能夠輕易被修改,並無任何的驗證機制。

控制依賴的版本

若是同一個項目中對同一個模塊有不一樣版本的兩個依賴的話,默認狀況下Gradle會在解析完DAG以後,選擇版本最高的那個依賴包。

可是這樣作並不必定就是正確的, 因此咱們須要自定義依賴版本的功能。

首先就是上面咱們提到的使用platform()和enforcedPlatform() 來導入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是發佈在gradle中才支持這個特性,若是是發佈在maven或者ivy中是不支持的。

上面講的是傳遞依賴的版本升級。一樣是傳遞依賴,若是本項目也須要使用到這個傳遞依賴的module,可是須要使用到更低的版本(由於默認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中能夠建立多模塊項目:

<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來區別不一樣的環境,在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階段,能夠執行resources:resources用來進行resource文件的拷貝操做。

在Gradle中的Java plugin的processResources task也能夠作相同的事情。

好比我能夠執行copy任務:

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

更加複雜的拷貝:

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

固然拷貝還有更加複雜的應用。這裏就不詳細講解了。

本文已收錄於 www.flydean.com/gradle-vs-m…

最通俗的解讀,最深入的乾貨,最簡潔的教程,衆多你不知道的小技巧等你來發現!

歡迎關注個人公衆號:「程序那些事」,懂技術,更懂你!

相關文章
相關標籤/搜索