Java构建工具:Maven与Gradle的对比
在Java码农的世界里,构建工具一直是一个不可或缺的元素。一开始,世上是只有一个构建工具的那就是Make后来发展为GNU Make。但是由于需求的不断涌现,这个小圈子里又逐渐衍生出其他千奇百怪的构建工具。
在这个小圈子中影响力最大的角色莫过于Maven了。它使用XML作为配置文件,改进了前辈Ant的复杂的构建配置,提供了一些现成的目标,而不需要我们一个个的将构建任务的每个命令一一列出。另外它提供了一个杀手锏功能,那就是依赖管理,它通过简单的配置就可以自动从网络上下载项目所需的依赖,这革命性的改变了我们开发软件的方式。可以想象,如果你是一个大型软件开发项目组的成员,如果使用代码仓库管理依赖jar包会令仓库变得多么庞大!Maven的制品仓库设计实现了制品与代码间的解耦,为基于制品的协作提供了可能。
可是软件行业新旧交替的速度之快往往令人咋舌,不用多少时间,你就会发现曾经大红大紫的技术已经成了昨日黄花。在Java构建领域,我们能够看到一些新兴的工具在涌现。比如基于Groovy的Gradle。Hibernate就将自己的项目从Maven迁移到了Gradle,Google官方Android开发的IDE Android Studio也默认使用了Gradle进行构建。这些事件令Gradle吸引了不少眼球。Gradle真的要替代Maven了么?当然没有,Maven在如今仍然是Java构建技术的事实标准。Gradle也仍然使用了Maven的制品库来做依赖管理。但是从Gradle身上,我们确实看到了进步。简洁的Groovy语法和灵活的配置令我们眼前一亮。
那Maven和Gradle到底有何不同呢?就请读者和我一起探寻吧。
安装
在安装上两者大同小易。我们都需要从官网下载解压到本地目录。配置好MAVEN_HOME或Gradle_Home环境变量,并加入PATH环境变量中。我们就可以在命令行中使用maven和gradle了。
主流的Java IDE: Eclipse可以安装Maven和Gradle的插件。Intellij有内置的Gradle和Maven可以使用。
二者均有详尽的文档供用户参考。别看gradle是个新来的小子,它的文档也是有70章,500多页的内容的!
在这里附上Maven和Gradle的下载地址:
maven: Maven – Welcome to Apache Maven
gradle: https://gradle.org
依赖管理
Maven的配置文件是.pom文件。POM是项目对象模型(Project Object Model)的简称,它是Maven项目中的文件,使用XML表示。其中包含项目的基本信息,构建过程,环境信息,依赖信息等。我们可以看下面这个简单的例子:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.vs</groupId>
<artifactId>com.vs.maven.gradle</artifactId>
<version>1.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.6.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa
</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.25</version>
</dependency>
</dependencies>
<properties>
<java.version>1.8</java.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
这个例子构建了一个简单的spring-boot项目, 最开始定义了一些项目的基本信息,包括组名、制品名、版本等。parent标签表示我们继承了一个spring定义的一个制品的配置,这样我们不需要配置很多依赖的版本就可以引入依赖。dependencies标签间配置该项目所依赖的其他制品,分别配置每个依赖制品的groupId, artifactId和version。当我们执行mvn insall时,maven就会自动下载依赖,并帮我们将它编译打包放入本地仓库之中。我们就可以在用户目录下的.m2文件夹中找到我们的制品。
那如果我们使用Gradle来构建这个项目,我们该如何做呢?
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:1.2.6.RELEASE")
}
}
dependencies {
compile("org.springframework.boot:spring-boot-starter-web") {
exclude module: "spring-boot-starter-tomcat"
}
compile("org.springframework.boot:spring-boot-starter-security")
compile("org.springframework.boot:spring-boot-starter-data-jpa")
testCompile("mysql:mysql-connector-java:5.1.25")
}
咦?冗长的代码怎么突然少了这么多?^?
仔细阅读下你会发现,原来是依赖管理所需的配置长度变短了。在pom.xml中我们需要引入一个依赖时需要将它的groupId, artifactId和version都用标签引起来。但是在build.gradle中你会发现,仅仅需要将三者的value用:连起来,并"调用compile函数"就可以啦。
比如,我们要引用spring-boot的starter-security,maven的配置是这样写的:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
在gradle中的配置却是这样的:
dependencies {
compile("org.springframework.boot:spring-boot-starter-security")
}
觉得怎么样?我反正感觉和写代码一样清爽!Gradle使用了groovy作为它的DSL,非常的易用。如果你使用了很久的Maven,你也许会发现Gradle的配置写起来实在是太爽了!
构建生命周期管理
除了依赖管理以外,构建工具的另一个主要用途就是构建的生命周期管理。
Maven有三个生命周期,每个生命周期又分为多个阶段:
Clean:包含3个阶段,与清理上次构建生成的文件相关
Default:Maven的核心生命周期,包含多个阶段如预处理、编译、测试、打包、安装到本地仓库、发布到远程仓库等。
Site: 包含4个阶段,与生成项目报告,站点,发布站点相关。
这些生命周期都是系统自定义好的,如果我们需要修改现有的构建生命周期的话,我们就要编写一个Maven插件。因为Maven是通过插件发来完成大多数的构建任务。每个插件可以绑定一个生命周期。配置好绑定生命周期后,我们需要定义插件的任务,在Maven中每个任务的goal称作Mojo,每个Mojo我们都需要实现org.apache.maven.plugin.Mojo接口。也就是我们需要定义一个类来实现这个接口。使用时我们需要引入这个插件,并配置需要执行的goal。
比如我们要完成输出Hello world这个需求的话,我们需要:
新建GreeingMojo类:
package sample.plugin;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.Mojo;
/**
* Says "Hi" to the user.
*
*/
@Mojo( name = "sayhi")
public class GreetingMojo extends AbstractMojo
{
public void execute() throws MojoExecutionException
{
getLog().info( "Hello, world." );
}
}
定义pom文件:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>sample.plugin</groupId>
<artifactId>hello-maven-plugin</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>maven-plugin</packaging>
<name>Sample Parameter-less Maven Plugin</name>
<dependencies>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<version>2.0</version>
</dependency>
<!-- dependencies to annotations -->
<dependency>
<groupId>org.apache.maven.plugin-tools</groupId>
<artifactId>maven-plugin-annotations</artifactId>
<version>3.4</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
在项目pom文件目录运行install命令将插件安装到本地仓库
在使用插件的项目加入配置:
<build>
<plugins>
<plugin>
<groupId>sample.plugin</groupId>
<artifactId>hello-maven-plugin</artifactId>
<version>1.0-SNAPSHOT</version>
</plugin>
</plugins>
</build>
执行:
mvn groupId:artifactId:version:goal
即可完成Hello,world的输出。
那么在Gradle中我们该怎么做呢?Gradle中有一个基本概念叫Task,我们可以使用Task来完成这个需求:
task hello << {
println "hello world"
}
执行Gradle hello,即可完成Hello, World的输出。是不是很简单?像写代码一样~
当然实际工程我们要完成的构建任务不仅仅是输出一个info这么简单,我们很可能会去做一些其他复杂的操作,比如初始化文件、数据库,检测系统环境等等。但由于Gradle使用了Groovy作为配置文件,在使用时会比Maven的xml配置灵活许多,更适合我们码农进行使用。
制品发布
在制品发布这个操作上,Maven要扳回一局。Maven原生支持maven jar的格式,发布很简单;而Gradle虽说既支持Maven又支持Gradle,但是就要我们自己做很多额外的工作。比如Maven要发布制品,只需要配置远程仓库的参数:
<distributionManagement>
<repository>
<id>internal</id>
<name>Internal Release Repository</name>
<url>http://repository.springsource.com/maven/bundles/release</url>
</repository>
</distributionManagement>
而gradle发布制品,还需要生成pom文件:
uploadArchives {
repositories {
mavenDeployer {
//为Pom文件做数字签名
beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
repository(url: "http://repository.springsource.com/maven/bundles/release") {
authentication(userName: username, password: password)
}
//构造项目的Pom文件
pom.project {
name project.name
packaging 'jar'
description 'description'
}
}
}
}
总结
因为时间关系,简单介绍了这么多,可能读者也对Gradle与Maven的差别有了一些了解。Gradle与Maven相比更为灵活,简单。但是Maven相比于Gradle的不灵活,是否也是它的优点呢?这就需要读者在工程中自己进行思考了。
除此之外Gradle的官方网站专门给出了一个专栏来讲Gradle与Maven的区别: Maven vs Gradle: Feature Comparison。
有耐心的读者可以看看这些对比。如果说你要说我没耐心看完这些,只想快点挑个构建工作开始我的项目! 那么我要说的是:Just have a try! Maven和Gradle都是非常优秀的构建工具,增加二者的使用经验不是一个很亏的事情。当然如果你要开发Android应用的话,还是推荐你使用Gradle做构建工具,因为Google官方推的Android Studio就使用了Gradle作为原生构建工具,这使得Gradle对Android各版本软件的构建支持得更好一些。其他项目的话,二者选其一吧~