Maven和Gradle對比

Java世界中主要有三大構建工具:Ant、Maven和Gradle。通過幾年的發展,Ant幾乎銷聲匿跡、Maven也日薄西山,而Gradle的發展則如日中天。筆者有幸見證了Maven的沒落和Gradle的興起。Maven的主要功能主要分爲5點,分別是依賴管理系統、多模塊構建、一致的項目結構、一致的構建模型和插件機制。咱們能夠從這五個方面來分析一下Gradle比起Maven的先進之處。javascript

依賴管理系統html

Maven爲Java世界引入了一個新的依賴管理系統。在Java世界中,能夠用groupId、artifactId、version組成的Coordination(座標)惟一標識一個依賴。任何基於Maven構建的項目自身也必須定義這三項屬性,生成的包能夠是Jar包,也能夠是war包或者ear包。一個典型的依賴引用以下所示:java

1spring

2shell

3app

4curl

5maven

6ide

7函數

8

9

10

<dependency>

 <groupId>junit</groupId>

 <artifactId>junit</artifactId>

 <version>4.12</version>

 <scope>test</scope>

</dependency>

<dependency>

 <groupId>org.springframework</groupId>

 <artifactId>spring-test</artifactId>

</dependency>

從上面能夠看出當引用一個依賴時,version能夠省略掉,這樣在獲取依賴時會選擇最新的版本。而存儲這些組件的倉庫有遠程倉庫和本地倉庫之分。遠程倉庫可使用世界公用的central倉庫,也可使用Apache Nexus自建私有倉庫;本地倉庫則在本地計算機上。經過Maven安裝目錄下的settings.xml文件能夠配置本地倉庫的路徑,以及採用的遠程倉庫的地址。

Gradle在設計的時候基本沿用了Maven的這套依賴管理體系。不過它在引用依賴時仍是進行了一些改進。首先引用依賴方面變得很是簡潔。

1

2

3

4

dependencies {

 compile 'org.hibernate:hibernate-core:3.6.7.Final'

 testCompile ‘junit:junit:4.+'

}

第二,Maven和Gradle對依賴項的scope有所不一樣。在Maven世界中,一個依賴項有6種scope,分別是complie(默認)、provided、runtime、test、system、import。而grade將其簡化爲了4種,compile、runtime、testCompile、testRuntime。那麼若是想在gradle使用相似於provided的scope怎麼辦?彆着急,因爲gradle語言的強大表現力,咱們能夠輕鬆編寫代碼來實現相似於provided scope的概念(例如How to use provided scope for jar file in Gradle build?)。

第三點是Gradle支持動態的版本依賴。在版本號後面使用+號的方式能夠實現動態的版本管理。

第四點是在解決依賴衝突方面Gradle的實現機制更加明確。使用Maven和Gradle進行依賴管理時都採用的是傳遞性依賴;而若是多個依賴項指向同一個依賴項的不一樣版本時就會引發依賴衝突。而Maven處理這種依賴關係每每是噩夢通常的存在。而Gradle在解決依賴衝突方面相對來講比較明確。在Chapter 23. Dependency Management
中的23.2.3章節詳細解讀了gradle是如何處理版本衝突的。

多模塊構建

在SOA和微服務的浪潮下,將一個項目分解爲多個模塊已是很通用的一種方式。在Maven中須要定義個parent POM做爲一組module的聚合POM。在該POM中可使用<modules>標籤來定義一組子模塊。parent POM不會有什麼實際構建產出。而parent POM中的build配置以及依賴配置都會自動繼承給子module。

而Gradle也支持多模塊構建。而在parent的build.gradle中可使用allprojects和subprojects代碼塊來分別定義裏面的配置是應用於全部項目仍是子項目。對於子模塊的定義是放置在setttings.gradle文件中的。在gradle的設計當中,每一個模塊都是Project的對象實例。而在parent build.gradle中經過allprojects或subprojects能夠對這些對象進行各類操做。這無疑比Maven要靈活的多。

好比在parent的build.gradle中有如下代碼:

1

2

3

allprojects {

 task hello << { task -> println "I'm $task.project.name" }

}

執行命令gradle -q hello會依次打印出父module以及各個submodule的項目名稱。這種強大的能力能讓gradle對各個模塊具備更強的定製化。

一致的項目結構

在Ant時代你們建立Java項目目錄時比較隨意,而後經過Ant配置指定哪些屬於source,那些屬於testSource等。而Maven在設計之初的理念就是Conversion over configuration(約定大於配置)。其制定了一套項目目錄結構做爲標準的Java項目結構。一個典型的Maven項目結構以下:

Gradle也沿用了這一標準的目錄結構。若是你在Gradle項目中使用了標準的Maven項目結構的話,那麼在Gradle中也無需進行多餘的配置,只需在文件中包含apply plugin:’java’,系統會自動識別source、resource、test srouce、 test resource等相應資源。不過Gradle做爲JVM上的構建工具,也同時支持groovy、scala等源代碼的構建,甚至支持Java、groovy、scala語言的混合構建。雖然Maven經過一些插件(好比maven-scala-plugin)也能達到相同目的,但配置方面顯然Gradle要更優雅一些。

一致的構建模型

爲了解決Ant中對項目構建活動缺少標準化的問題,Maven特地設置了標準的項目構建週期,其默認的構建週期以下所示:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

<phases>

 <phase>validate</phase>

 <phase>initialize</phase>

 <phase>generate-sources</phase>

 <phase>process-sources</phase>

 <phase>generate-resources</phase>

 <phase>process-resources</phase>

 <phase>compile</phase>

 <phase>process-classes</phase>

 <phase>generate-test-sources</phase>

 <phase>process-test-sources</phase>

 <phase>generate-test-resources</phase>

 <phase>process-test-resources</phase>

 <phase>test-compile</phase>

 <phase>process-test-classes</phase>

 <phase>test</phase>

 <phase>prepare-package</phase>

 <phase>package</phase>

 <phase>pre-integration-test</phase>

 <phase>integration-test</phase>

 <phase>post-integration-test</phase>

 <phase>verify</phase>

 <phase>install</phase>

 <phase>deploy</phase>

</phases>

而這種構建週期也是Maven最爲人詬病的地方。由於Maven將項目的構建週期限制的太死,你沒法在構建週期中添加新的phase,只能將插件綁定到已有的phase上。而如今項目的構建過程變得愈來愈複雜,並且多樣化,顯然Maven對這種複雜度缺乏足夠的應變能力。好比你想在項目構建過程當中進行一項壓縮全部javascript的任務,那麼就要綁定到Maven的現有的某個phase上,而顯然貌似放在哪一個phase都不太合適。並且這些phase都是串行的,整個執行下來是一條線,這也限制了Maven的構建效率。而Gradle在構建模型上則很是靈活。在Gradle世界裏能夠輕鬆建立一個task,並隨時經過depends語法創建與已有task的依賴關係。甚至對於Java項目的構建來講,Gradle是經過名爲java的插件來包含了一個對Java項目的構建週期,這等於Gradle自己直接與項目構建週期是解耦的。

插件機制

Maven和Gradle設計時都採用了插件機制。但顯然Gradle更勝一籌。主要緣由在於Maven是基於XML進行配置。因此其配置語法太受限於XML。即便實現很小的功能都須要設計一個插件,創建其與XML配置的關聯。好比想在Maven中執行一條shell命令,其配置以下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

<plugin>

 <groupId>org.codehaus.mojo</groupId>

 <artifactId>exec-maven-plugin</artifactId>

 <version>1.2</version>

 <executions>

 <execution>

 <id>drop DB => db_name</id>

 <phase>pre-integration-test</phase>

 <goals>

 <goal>exec</goal>

 </goals>

 <configuration>

 <executable>curl</executable>

 <arguments>

 <argument>-s</argument>

 <argument>-S</argument>

 <argument>-X</argument>

 <argument>DELETE</argument>

 <argument>http://${db.server}:${db.port}/db_name</argument>

 </arguments>

 </configuration>

 </execution>

 </executions>

</plugin>

而在Gradle中則一切變得很是簡單。

1

2

3

task dropDB(type: Exec) {

 commandLine ‘curl’,’-s’,’s’,’-x’,’DELETE’,"http://${db.server}:{db.port}/db_name"

}

在建立自定義插件方面,Maven和Gradle的機制都差很少,都是繼承自插件基類,而後實現要求的方法。這裏就不展開說明。

從以上五個方面能夠看出Maven和Gradle的主要差別。Maven的設計核心Convention Over Configuration被Gradle更加發揚光大,而Gradle的配置即代碼又超越了Maven。在Gradle中任何配置均可以做爲代碼被執行的,咱們也能夠隨時使用已有的Ant腳本(Ant task是Gradle中的一等公民)、Java類庫、Groovy類庫來輔助完成構建任務的編寫。

這種採用自己語言實現的DSL對自己語言項目進行構建管理的例子比比皆是。好比Rake和Ruby、Grunt和JavaScript、Sbt和Ruby…..而Gradle之因此使用Groovy語言實現,是由於Groovy比Java語言更具表現力,其語法特性更豐富,又兼具函數式的特色。這幾年興起的語言(好比Scala、Go、Swift)都屬於強類型的語言,兼具面向對象和函數式的特色。

最後想說的Gradle的命令行比Maven的要強大的多。之前寫過一篇文章專門講述了Gradle的命令行操做,詳情請見Gradle命令行黑魔法。

相關文章
相關標籤/搜索