Maven和Gradle对比

Java世界中主要有三大构建工具:Ant、Maven和Gradle。经过几年的发展,Ant几乎销声匿迹、Maven也日薄西山,而Gradle的发展则如日中天。笔者有幸见证了Maven的没落和Gradle的兴起。Maven的主要功能主要分为5点,分别是依赖管理系统、多模块构建、一致的项目结构、一致的构建模型和插件机制。我们可以从这五个方面来分析一下Gradle比起Maven的先进之处。

依赖管理系统

Maven为Java世界引入了一个新的依赖管理系统。在Java世界中,可以用groupId、artifactId、version组成的Coordination(坐标)唯一标识一个依赖。任何基于Maven构建的项目自身也必须定义这三项属性,生成的包可以是Jar包,也可以是war包或者ear包。一个典型的依赖引用如下所示:

1
2
3
4
5
6
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命令行黑魔法

Maven和Gradle的更多相关文章

  1. Maven和Gradle对比

    Java世界中主要有三大构建工具:Ant.Maven和Gradle.经过几年的发展,Ant几乎销声匿迹.Maven也日薄西山,而Gradle的发展则如日中天.笔者有幸见证了Maven的没落和Gradl ...

  2. Java构建工具:如何用Maven,Gradle和Ant+Ivy进行依赖管理

    原文来自:https://zeroturnaround.com/rebellabs/java-build-tools-how-dependency-management-works-with-mave ...

  3. Maven、gradle、Ant、Eclipse IDE

    Maven.gradle.Ant.Eclipse IDE之间的关系 http://wenku.baidu.com/view/d33208810912a21615792910.html?from=sea ...

  4. 项目自动构建工具对比(Maven、Gradle、Ant)

    Java世界中主要有三大构建工具:Ant.Maven和Gradle.经过几年的发展,Ant几乎销声匿迹.Maven也日薄西山,而Gradle的发展则如日中天. Maven的主要功能主要分为5点,分别是 ...

  5. Zxing 的集成 ---- Maven 对应 Gradle 的写法

    Zxing 的集成 ---- Maven 对应 Gradle 的写法 刚刚想耍耍二维码,想到了zxing和zbar,又想到zxing是Google老爹的,想想就算了吧,虽然zbar快但是识别错误率也高 ...

  6. maven到Gradle,一些对比和分享

    Gradle作为一款基于Groovy语言的构建工具,已经吸引众多的ant,maven使用者转投gradle的怀抱,和Gradle相比,ant显得冗余复杂,maven显得有些死板落后,而gradle基于 ...

  7. CentOS7 nexus 3 搭建maven或gradle 私有代理服务器

    1.下载nexus 3, 选择与操作系统对应版本 2.解压nexus并运行: 3.在浏览器中输入http://192.168.127.128:8081/,并以amdin为用户名,以admin123为密 ...

  8. Java 中三大构建工具:Ant、Maven和Gradle

    Java世界中主要有三大构建工具:Ant.Maven和Gradle 目前:Ant已经销声匿迹.Maven也没落了,而Gradle的发展则如日中天. Maven的主要功能主要分为5点,分别是依赖管理系统 ...

  9. 如何在Maven和Gradle中配置使用Groovy 2.4与Spock 1.0

    如何在Maven和Gradle中配置使用Groovy 2.4与Spock 1.0 原文 https://dzone.com/articles/spock-10-groovy-24 翻译 hxfiref ...

随机推荐

  1. 使用函数指针和多态代替冗长的if-else或者switch-case

    在编程中,if-else和switch-case是很常见的分支结构,很少在程序中不用这些控制语句.但是不能否认,在一些场景下,由于分支结构过分长,导致代码不美观且不容易维护,在<重构>一书 ...

  2. 上Https 和 http 差分

    HTTPS 和 HTTP 差协议 超文本传输协定HTTP 对于web 浏览器和现场服务之间传递消息,HTTP 以纯文本协议 发送内容 无论不提供数据加密方法 假设拦截攻击web 浏览器和网站serve ...

  3. 网站集成QQ登录功能(转)

    最近在做一个项目时,客户要求网站能够集成QQ登录的功能,以前没做过这方面的开发,于是去QQ的开放平台官网研究了一下相关资料,经过自己的艰苦探索,终于实现了集成QQ登录的功能,现在把相关的开发经验总结一 ...

  4. Android架构设计和软硬整合完整训练

    Android架构设计和软硬整合完整训练 Android架构设计和软硬整合完整训练:HAL&Framework&Native Service&Android Service&a ...

  5. 探索Oracle之数据库升级七 11gR2 to 12c 升级完毕后插入PDB

    探索Oracle之数据库升级七 11gR2 to 12c 升级完毕后插入PDB 前言:        从Oracle 12c開始,引入了容器数据库的概念,能够实现数据库插拔操作,例如以下图: 如今我们 ...

  6. hdu3294(manacher)

    传送门:Girls' research 题意:求最长回文串并输出位置及转换后的字符串. 分析:manacher算法算出最长回文串后记录中心位置,然后再转换回原字符串的起始和结束位置. #pragma ...

  7. 你是否是团队里面最默默付出的那个coder,却发现滔滔不绝的产品和设计是团队里的开心果(转)

    程序员,你是否是团队里面最默默付出的那个coder,却发现滔滔不绝的产品和设计是团队里的开心果? 你是否自命不凡,精通Java.C++.Python……却发现得到的只是做不完的工作? 你是否觉得自己是 ...

  8. 命令含执行JAVA程序

    1.当类没有包名时 javac Test.java java Test   2.当有包名情况下 package com.me.Test; javac -d . Test.java java com.m ...

  9. java读取XML文件的四种方式

    java读取XML文件的四种方式 Xml代码 <?xml version="1.0" encoding="GB2312"?> <RESULT& ...

  10. 9个杀手级 JVM 编程语言

    9个杀手级 JVM 编程语言 Java虚拟机已经不再是仅仅局限在 Java 了,很多语言提供了脚本转换,可以让其他的程序在java虚拟机上运行,这样能够让更多的开发者能够依靠JVM在Java平台上大有 ...