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 |
|
从上面能够看出当引用一个依赖时,version能够省略掉,这样在获取依赖时会选择最新的版本。而存储这些组件的仓库有远程仓库和本地仓库之分。远程仓库可使用世界公用的central仓库,也可使用Apache Nexus自建私有仓库;本地仓库则在本地计算机上。经过Maven安装目录下的settings.xml文件能够配置本地仓库的路径,以及采用的远程仓库的地址。
Gradle在设计的时候基本沿用了Maven的这套依赖管理体系。不过它在引用依赖时仍是进行了一些改进。首先引用依赖方面变得很是简洁。
1 2 3 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 |
|
执行命令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 |
|
而这种构建周期也是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 |
|
而在Gradle中则一切变得很是简单。
1 2 3 |
|
在建立自定义插件方面,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命令行黑魔法。