深刻了解gradle和maven的区别

简介

gradle和maven均可以用来构建java程序,甚至在某些状况下,二者还能够互相转换,那么他们两个的共同点和不一样点是什么?咱们如何在项目中选择使用哪一种技术呢?一块儿来看看吧。java

gradle和maven的比较

虽然gradle和maven均可以做为java程序的构建工具。可是二者仍是有很大的不一样之处的。咱们能够从下面几个方面来进行分析。android

可扩展性

Google选择gradle做为android的构建工具不是没有理由的,其中一个很是重要的缘由就是由于gradle够灵活。一方面是由于gradle使用的是groovy或者kotlin语言做为脚本的编写语言,这样极大的提升了脚本的灵活性,可是其本质上的缘由是gradle的基础架构可以支持这种灵活性。web

你可使用gradle来构建native的C/C++程序,甚至扩展到任何语言的构建。spring

相对而言,maven的灵活性就差一些,而且自定义起来也比较麻烦,可是maven的项目比较容易看懂,而且上手简单。apache

因此若是你的项目没有太多自定义构建需求的话仍是推荐使用maven,可是若是有自定义的构建需求,那么仍是投入gradle的怀抱吧。api

性能比较

虽然如今你们的机子性能都比较强劲,好像在作项目构建的时候性能的优点并非那么的迫切,可是对于大型项目来讲,一次构建可能会须要很长的时间,尤为对于自动化构建和CI的环境来讲,固然但愿这个构建是越快越好。缓存

Gradle和Maven都支持并行的项目构建和依赖解析。可是gradle的三个特色让gradle能够跑的比maven快上一点:服务器

  • 增量构建

gradle为了提高构建的效率,提出了增量构建的概念,为了实现增量构建,gradle将每个task都分红了三部分,分别是input输入,任务自己和output输出。下图是一个典型的java编译的task。markdown

以上图为例,input就是目标jdk的版本,源代码等,output就是编译出来的class文件。架构

增量构建的原理就是监控input的变化,只有input发送变化了,才从新执行task任务,不然gradle认为能够重用以前的执行结果。

因此在编写gradle的task的时候,须要指定task的输入和输出。

而且要注意只有会对输出结果产生变化的才能被称为输入,若是你定义了对初始结果彻底无关的变量做为输入,则这些变量的变化会致使gradle从新执行task,致使了没必要要的性能的损耗。

还要注意不肯定执行结果的任务,好比说一样的输入可能会获得不一样的输出结果,那么这样的任务将不可以被配置为增量构建任务。

  • 构建缓存

gradle能够重用一样input的输出做为缓存,你们可能会有疑问了,这个缓存和增量编译不是一个意思吗?

在同一个机子上是的,可是缓存能够跨机器共享.若是你是在一个CI服务的话,build cache将会很是有用。由于developer的build能够直接从CI服务器上面拉取构建结果,很是的方便。

  • Gradle守护进程

gradle会开启一个守护进程来和各个build任务进行交互,优势就是不须要每次构建都初始化须要的组件和服务。

同时由于守护进程是一个一直运行的进程,除了能够避免每次JVM启动的开销以外,还能够缓存项目结构,文件,task和其余的信息,从而提高运行速度。

咱们能够运行 gradle --status 来查看正在运行的daemons进程。

从Gradle 3.0以后,daemons是默认开启的,你可使用 org.gradle.daemon=false 来禁止daemons。

咱们能够经过下面的几个图来直观的感觉一下gradle和maven的性能比较:

  • 使用gradle和maven构建 Apache Commons Lang 3的比较:

  • 使用gradle和maven构建小项目(10个模块,每一个模块50个源文件和50个测试文件)的比较:

  • 使用gradle和maven构建大项目(500个模块,每一个模块100个源文件和100个测试文件)的比较:

能够看到gradle性能的提高是很是明显的。

依赖的区别

gralde和maven均可以本地缓存依赖文件,而且都支持依赖文件的并行下载。

在maven中只能够经过版本号来覆盖一个依赖项。而gradle更加灵活,你能够自定义依赖关系和替换规则,经过这些替换规则,gradle能够构建很是复杂的项目。

从maven迁移到gradle

由于maven出现的时间比较早,因此基本上全部的java项目都支持maven,可是并非全部的项目都支持gradle。若是你有须要把maven项目迁移到gradle的想法,那么就一块儿来看看吧。

根据咱们以前的介绍,你们能够发现gradle和maven从本质上来讲就是不一样的,gradle经过task的DAG图来组织任务,而maven则是经过attach到phases的goals来执行任务。

虽然二者的构建有很大的不一样,可是得益于gradle和maven相识的各类约定规则,从maven移植到gradle并非那么难。

要想从maven移植到gradle,首先要了解下maven的build生命周期,maven的生命周期包含了clean,compile,test,package,verify,install和deploy这几个phase。

咱们须要将maven的生命周期phase转换为gradle的生命周期task。这里须要使用到gradle的Base Plugin,Java Plugin和Maven Publish Plugin。

先看下怎么引入这三个plugin:

plugins {
    id 'base'
    id 'java'
    id 'maven-publish'
}
复制代码

clean会被转换成为clean task,compile会被转换成为classes task,test会被转换成为test task,package会被转换成为assemble task,verify 会被转换成为check task,install会被转换成为 Maven Publish Plugin 中的publishToMavenLocal task,deploy 会被转换成为Maven Publish Plugin 中的publish task。

有了这些task之间的对应关系,咱们就能够尝试进行maven到gradle的转换了。

自动转换

咱们除了可使用 gradle init 命令来建立一个gradle的架子以外,还可使用这个命令来将maven项目转换成为gradle项目,gradle init命令会去读取pom文件,并将其转换成为gradle项目。

转换依赖

gradle和maven的依赖都包含了group ID, artifact ID 和版本号。二者本质上是同样的,只是形式不一样,咱们看一个转换的例子:

<dependencies>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.12</version>
    </dependency>
</dependencies>
复制代码

上是一个maven的例子,咱们看下gradle的例子怎写:

dependencies {
    implementation 'log4j:log4j:1.2.12'  
}
复制代码

能够看到gradle比maven写起来要简单不少。

注意这里的implementation其实是由 Java Plugin 来实现的。

咱们在maven的依赖中有时候还会用到scope选项,用来表示依赖的范围,咱们看下这些范围该如何进行转换:

  • compile:

在gradle能够有两种配置来替换compile,咱们可使用implementation或者api。

前者在任何使用Java Plugin的gradle中均可以使用,而api只能在使用Java Library Plugin的项目中使用。

固然二者是有区别的,若是你是构建应用程序或者webapp,那么推荐使用implementation,若是你是在构建Java libraries,那么推荐使用api。

  • runtime:

能够替换成 runtimeOnly 。

  • test:

gradle中的test分为两种,一种是编译test项目的时候须要,那么可使用testImplementation,一种是运行test项目的时候须要,那么可使用testRuntimeOnly。

  • provided:

能够替换成为compileOnly。

  • import:

在maven中,import常常用在dependencyManagement中,一般用来从一个pom文件中导入依赖项,从而保证项目中依赖项目版本的一致性。

在gradle中,可使用 platform() 或者 enforcedPlatform() 来导入pom文件:

dependencies {
    implementation platform('org.springframework.boot:spring-boot-dependencies:1.5.8.RELEASE') implementation 'com.google.code.gson:gson' implementation 'dom4j:dom4j' } 复制代码

好比上面的例子中,咱们导入了spring-boot-dependencies。由于这个pom中已经定义了依赖项的版本号,因此咱们在后面引入gson的时候就不须要指定版本号了。

platform和enforcedPlatform的区别在于,enforcedPlatform会将导入的pom版本号覆盖其余导入的版本号:

dependencies {
    // import a BOM. The versions used in this file will override any other version found in the graph
    implementation enforcedPlatform('org.springframework.boot:spring-boot-dependencies:1.5.8.RELEASE') // define dependencies without versions implementation 'com.google.code.gson:gson' implementation 'dom4j:dom4j' // this version will be overridden by the one found in the BOM implementation 'org.codehaus.groovy:groovy:1.8.6' } 复制代码

转换repositories仓库

gradle能够兼容使用maven或者lvy的repository。gradle没有默认的仓库地址,因此你必须手动指定一个。

你能够在gradle使用maven的仓库:

repositories {
    mavenCentral()
}
复制代码

咱们还能够直接指定maven仓库的地址:

repositories {
    maven {
        url "http://repo.mycompany.com/maven2"
    }
}
复制代码

若是你想使用maven本地的仓库,则能够这样使用:

repositories {
    mavenLocal()
}
复制代码

可是mavenLocal是不推荐使用的,为何呢?

mavenLocal只是maven在本地的一个cache,它包含的内容并不完整。好比说一个本地的maven repository module可能只包含了jar包文件,并无包含source或者javadoc文件。那么咱们将不可以在gradle中查看这个module的源代码,由于gradle会首先在maven本地的路径中查找这个module。

而且本地的repository是不可信任的,由于里面的内容能够轻易被修改,并无任何的验证机制。

控制依赖的版本

若是同一个项目中对同一个模块有不一样版本的两个依赖的话,默认状况下Gradle会在解析完DAG以后,选择版本最高的那个依赖包。

可是这样作并不必定就是正确的, 因此咱们须要自定义依赖版本的功能。

首先就是上面咱们提到的使用platform()和enforcedPlatform() 来导入BOM(packaging类型是POM的)文件。

若是咱们项目中依赖了某个module,而这个module又依赖了另外的module,咱们叫作传递依赖。在这种状况下,若是咱们但愿控制传递依赖的版本,好比说将传递依赖的版本升级为一个新的版本,那么可使用dependency constraints:

dependencies {
    implementation 'org.apache.httpcomponents:httpclient'
    constraints {
        implementation('org.apache.httpcomponents:httpclient:4.5.3') {
            because 'previous versions have a bug impacting this application'
        }
        implementation('commons-codec:commons-codec:1.11') {
            because 'version 1.9 pulled from httpclient has bugs affecting this application'
        }
    }
}
复制代码

注意,dependency constraints只对传递依赖有效,若是上面的例子中commons-codec并非传递依赖,那么将不会有任何影响。

同时 Dependency constraints须要Gradle Module Metadata的支持,也就是说只有你的module是发布在gradle中才支持这个特性,若是是发布在maven或者ivy中是不支持的。

上面讲的是传递依赖的版本升级。一样是传递依赖,若是本项目也须要使用到这个传递依赖的module,可是须要使用到更低的版本(由于默认gradle会使用最新的版本),就须要用到版本降级了。

dependencies {
    implementation 'org.apache.httpcomponents:httpclient:4.5.4'
    implementation('commons-codec:commons-codec') {
        version {
            strictly '1.9'
        }
    }
}
复制代码

咱们能够在implementation中指定特定的version便可。

strictly表示的是强制匹配特定的版本号,除了strictly以外,还有require,表示须要的版本号大于等于给定的版本号。prefer,若是没有指定其余的版本号,那么就使用prefer这个。reject,拒绝使用这个版本。

除此以外,你还可使用Java Platform Plugin来指定特定的platform,从而限制版本号。

最后看一下如何exclude一个依赖:

dependencies {
    implementation('commons-beanutils:commons-beanutils:1.9.4') {
        exclude group: 'commons-collections', module: 'commons-collections'
    }
}
复制代码

多模块项目

maven中能够建立多模块项目:

<modules>
    <module>simple-weather</module>
    <module>simple-webapp</module>
</modules>
复制代码

咱们能够在gradle中作一样的事情settings.gradle:

rootProject.name = 'simple-multi-module'  

include 'simple-weather', 'simple-webapp'  
复制代码

profile和属性

maven中可使用profile来区别不一样的环境,在gradle中,咱们能够定义好不一样的profile文件,而后经过脚原本加载他们:

build.gradle:

if (!hasProperty('buildProfile')) ext.buildProfile = 'default'  

apply from: "profile-${buildProfile}.gradle"  

task greeting {
    doLast {
        println message  
    }
}
复制代码

profile-default.gradle:

ext.message = 'foobar'  
复制代码

profile-test.gradle:

ext.message = 'testing 1 2 3'
复制代码

咱们能够这样来运行:

> gradle greeting
foobar

> gradle -PbuildProfile=test greeting
testing 1 2 3
复制代码

资源处理

在maven中有一个process-resources阶段,能够执行resources:resources用来进行resource文件的拷贝操做。

在Gradle中的Java plugin的processResources task也能够作相同的事情。

好比我能够执行copy任务:

task copyReport(type: Copy) {
    from file("$buildDir/reports/my-report.pdf") into file("$buildDir/toArchive") } 复制代码

更加复杂的拷贝:

task copyPdfReportsForArchiving(type: Copy) {
    from "$buildDir/reports"
    include "*.pdf"
    into "$buildDir/toArchive"
}
复制代码

固然拷贝还有更加复杂的应用。这里就不详细讲解了。

本文已收录于 www.flydean.com/gradle-vs-m…

最通俗的解读,最深入的干货,最简洁的教程,众多你不知道的小技巧等你来发现!

欢迎关注个人公众号:「程序那些事」,懂技术,更懂你!

相关文章
相关标签/搜索