Java构建工具:Maven与Gradle的对比

Java构建工具:Maven与Gradle的对比

Xander发表在天码营

在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的配置文件是.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有三个生命周期,每个生命周期又分为多个阶段:

  1. Clean:包含3个阶段,与清理上次构建生成的文件相关
  2. Default:Maven的核心生命周期,包含多个阶段如预处理、编译、测试、打包、安装到本地仓库、发布到远程仓库等。
  3. Site: 包含4个阶段,与生成项目报告,站点,发布站点相关。

这些生命周期都是系统自定义好的,如果我们需要修改现有的构建生命周期的话,我们就要编写一个Maven插件。因为Maven是通过插件发来完成大多数的构建任务。每个插件可以绑定一个生命周期。配置好绑定生命周期后,我们需要定义插件的任务,在Maven中每个任务的goal称作Mojo,每个Mojo我们都需要实现org.apache.maven.plugin.Mojo接口。也就是我们需要定义一个类来实现这个接口。使用时我们需要引入这个插件,并配置需要执行的goal。

比如我们要完成输出Hello world这个需求的话,我们需要:

  1. 新建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." );
    }
}
  1. 定义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>
  1. 在项目pom文件目录运行install命令将插件安装到本地仓库
  2. 在使用插件的项目加入配置:
  <build>
    <plugins>
      <plugin>
        <groupId>sample.plugin</groupId>
        <artifactId>hello-maven-plugin</artifactId>
        <version>1.0-SNAPSHOT</version>
      </plugin>
    </plugins>
  </build>
  1. 执行:
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各版本软件的构建支持得更好一些。其他项目的话,二者选其一吧~


欢迎关注天码营微信公众号: TMY-EDU

小编重点推荐:

Spring MVC实战入门训练

Spring Data JPA实战入门训练

Java Web实战训练

Node.js全栈开发

更多精彩内容请访问天码营网站
编辑于 2016-09-06

文章被以下专栏收录

    我是天码营的David老师,为大家提供编程开发技术学习的内容、工具与服务,帮助你迅速掌握实战技能!