gradle基础内容

Gradle入门

常见的项目构建工具

Ant

2000年Apache推出的纯Java编写的构建工具,通过xml [build.xml]文件管理项目
优点:使用灵活,速度快(快于gradle和maven)
缺点:Ant没有加强任何编码约定的项目目录结构,开发人员需要编写繁杂的XML文件构建指令,对开发人员是一个挑战

Maven

2004年Apache组织推出的再次使用xml文件[pom.xml]管理项目的构建工具
优点:遵循一套约定大于配置的项目目录结构,使用统一的GAV坐标进行依赖管理,侧重于包管理。
缺点:项目构建过程僵化,配置文件编写不够灵活、不方便自定义组件,构建速度慢于gradle

Gradle

2012年Google推出的基于Groovy语言的全新项目构建工具,集合了Ant和Maven各自的优势。
优点:集Ant脚本的灵活性+Maven约定大于配置的项目目录优势,支持多种远程仓库和插件,侧重于大项目构建。
缺点:学习成本高、资料少、脚本灵活、版本兼容性差等。

自动化构建工具对比 Ant Maven Gradle
构建性能 最高 最低 居中
仓库 开发者自己处理 maven仓库 支持多种远程仓库
依赖管理 ivy管理 GAV坐标 GNV坐标
插件主持 实现方便 实现较难 实现方便
遵循特定目录结构 No 遵循 同Maven
配置文件 xml文件最为繁琐 xml文件 代码脚本,便于写业务逻辑
侧重点 小型项目构建 项目包管理 大型项目构建
目前地位 使用较少 目前主流 未来趋势(spring家族)

Gradle安装

安装jdk

gradle的运行需要jdk的支持,所以安装Gradle之前需要先安装jdk,至少是jdk8以上版本
(jdk安装不再描述)

下载并解压到指定目录

说明: gradle有完整版(以all结尾的)和二进制版(以bin结尾的)两种,完整版的相对于二进制版的多了一个gradle的源码和文档。

注意: idea和gradle也会有兼容性问题,如果想要知道自己安装的idea对应的gradle是什么版本,可以去idea的安装目录下的\plugins\gradle\lib 下去查看
查看idea对应的gradle图示

配置环境变量

  • 将gradle解压到对应目录中之后,在环境变量中配置gradle的路径
    • 先在环境变量中新增一个GRADLE_HOME 的变量
    • 然后把gradle下的bin目录配置到path中
      gradle环境变量配置图示

gradle环境变量配置图示2

注意: gradle默认会把相关依赖下载到C盘用户目录下,需要在环境变量中配置GRADLE_USER_HOME,配置了该变量相当于是maven指定本地仓库

GRADLE_USER_HOME配置图示

检查是否按照成功

1
gradle -v # 或者 graddle --version

gradle安装后检查图示

Gradle项目目录结构

gradle项目目录结构和maven项目结构一直,都是基于约定大于配置;
其完整目录结构如下所示:

gradle目录结构图示

  • build:封装编译后的字节码、打成的包(jar、war)、测试报告等信息
  • gradle:封装包装器文件夹
    • gradle-wrapper.jar
    • gradle-wrapper.properties
  • src:源码
    • main:项目源码
      • java:java的代码
      • resources:配置
    • test:项目测试源码
      • java:测试java代码
      • resources:测试配置
  • build.gradle:构建脚本,类似与maven中的pom.xml
  • gradlew:包装器启动脚本,linux下的可执行脚本
  • gradlew.bat:Windows下的可执行脚本,(无论是在linux还是window下,启动脚本执行的都是gradle/wrapper目录中配置的gradle的版本,而不是本地安装的graddle)
  • settings.gradle:设置文件,低调定义项目及子项目名称信息,和项目是一一对应

创建gradle项目

  • 创建gradle项目有两种方式,第一种是可以使用spring脚手架的方式
  • springboot脚手架:Spring Initializr

使用spring脚手架创建gradle项目图示

  • 第二种方式是使用gradle命令创建
    • 新建一个文件夹,在文件夹内打开命令行,并执行 graddle init 命令
    • 第一步:选择是生成一个应用还是其他的,我们是生成一个应用所以选择2
    • 第二步:选择语言,我们是使用java语言,选择3
    • 第三步:创建单应用还是多模块应用,我们这里是单应用,选择1
    • 第四步:选择构建脚本的语言,选择1
    • 第五步:选择测试框架,选择1
    • 第六部:输入项目名,如果不输入,默认使用当前文件夹名称
    • 第七步:输入包名称

注意: 通过命令行创建出来的项目根目录下没有src和build.gradle文件,而是多了一个app目录,src和build.gradle就在app目录中,其实就是相当于创建了一个多模块应用,只不过只有一个模块。

gradle命令行创建项目图示

Gradle中的常用命令

常用命令 作用
gradle clean 清空build目录
gradle classes 编译业务代码和配置文件
gradle test 编译测试代码,生成测试报告
gradle build 构建项目
gradle build -x test 跳过测试构建项目

注意: gradle的指令要在含有build.gradle的目录下执行。

修改Gradle下载源

gradle默认的下载源是国外地址,该下载源在国内访问特别慢,有时甚至访问失败,可以修改成国内下载源

认识init.d文件夹

我们可以在gradle的init.d目录下创建以 .gradle 结尾的文件,.gradle 文件可以实现在build开始之前执行,所以你可以在这个文件内配置一些你想要预先加载的操作

gradle的init文件夹图示

init.d文件夹创建init.gradle文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
allprojects {
repositories {
mavenLocal()
maven { name "Alibaba" ; url "https://maven.aliyun.com/repository/public/" }
maven { name "Bstek" ; url "http://nexus.bsdn.org/content/groups/public/" }
mavenCentral()
}
buildscript {
repositories {
maven { name "Alibaba" ; url "https://maven.aliyun.com/repository/public/" }
maven { name "Bstek" ; url "http://nexus.bsdn.org/content/groups/public/" }
maven { name "M2" ; url "http://plugins.gradle.org/m2/" }
}
}
}

注意: mavenLocal()是指本地maven仓库,需要配置环境变量才行,M2_HOME
M2_HOME环境变量配置图示

启用 init.gradle 文件的方法

  1. 在命令行指定文件,例如 gradle --init-script dir/init.graddle -q taskName 你可以多次输入此命令来指定多个init文件
  2. init.gradle 放到 USER_HOME/.gradle/ 目录下
  3. 把以 .gradle 结尾的文件放到 USER_HOME/.gradle/init.d/ 目录下
  4. 把以 .gradle 结尾的文件放到 GRADLE_HOME/init.d/ 目录下

如果存在上面4中方式的2种以上,gradle会按照 1-4 的顺序依次依次执行这些文件,如果给定的目录中存在多个初始化脚本,会按照拼音 a-z 顺序执行这些脚本。

仓库地址说明

mavenLocal() : 指定使用maven本地仓库,而本地仓库在配置maven时settings.xml指定的位置,gradle查找jar包顺序如下:
USER_HOME/.m2/settings.xml >> M2_HOME/conf/settings.xml >> USER_HOME/.m2/repository

maven {url 地址} :指定maven仓库,一般使用私有仓库后置其他第三方仓库,例如阿里云仓库地址
mavenCentral() :这是maven中央仓库,无序配置,直接声明即可

Wrapper包装器

Gradle Wrapper实际上就是对gradle的一层包装,用来解决实际开发中可能会遇到的不同项目需要不同版本的gradle的问题。

  • 项目中的gradlew和gradlew.bat脚本使用的就是wrapper中规定的gradle版本
  • 在项目根目录下操作项目,可以使用gradlew命令,指令方式和gradle完全一致

使用gradle命令控制Wrapper的生成

参数名 说明
–gradle-version 用于指定使用的gradle版本
–gradle-distribution-url 用于指定下载gradle发行版本的url地址
1
gradle wrapper --gradle-version=xxx

gradle-wrapper.properties文件解读

字段名 说明
distributionBase 下载的gradle压缩包解压后的存放主目录
distributionPath 相对于distributionBase的路径
distributionUrl gradle发行版压缩包下载地址
zipStoreBase 存放压缩包的路径
zipStorePath 存放压缩包的子路径

注意: 如果没有配置 GRADLE_USER_HOME 会存放到C盘用户目录下的.gradle目录下

Gradle和idea整合

Groovy简介

在某种程度上,groovy可以被视为java的一种脚本化改良,和java相同,也是运行在jvm上的,可以很好的和java相关库进行交互。
它是一种成熟的面向对象语言,既可以面向对象编程,也可以作为纯粹的脚本语言

  • 功能强大,例如提供了动态类型转换、闭包和元编程支持
  • 支持函数式编程,不需要main函数
  • 默认导入常用的包
  • 类不支持default作用域,且默认作用域为public
  • Groovy中基本类型也是对象,可以直接调用对象的方法
  • 支持DSL(领域特定语言)和其它简洁的语法,让代码变得易于阅读和维护
  • Groovy是基于java语言的,所以完全兼容java语法,所以对于java程序员学习成本较低

Groovy安装

下载地址 : The Apache Groovy programming language - Download

下载groovy之后解压到指定目录,然后把groovy下的bin目录配置到环境变量
配置完环境变量后可以执行 groovy -version 命令查看是否安装成功

Groovy基本语法

  • Groovy是基于java语言的,所以完全兼容java语法,可作为面向对象编程语言(定义类),也可以作为脚本型语言(文件定义中不出现类)
  • 在一个groovy文件中可以混合类的定义和脚本定义(不可定义和文件名相同的类名)
  • groovy中使用def定义变量、方法,不建议使用具体的数据类型
  • groovy中的注释:单行注释 // ,多行注释: /**/
  • groovy中语句末尾的分号是可以省略的,以换行行为作为结束
  • 默认类、方法、字段都是public修饰
  • 对象的属性操作
    • 给对象属性赋值
      • 对象.属性名 = xxx
      • 对象的setter方法
      • 具名构造器(groovy自带的)
        • 用处:后面给task指定分组,description描述信息使用
    • 对象属性读取
      • 对象.属性名
      • 对象[“属性名”]
      • 对象.getter方法
    • 对象属性的操作本质是通过属性的getter和setter方法完成的
  • 方法
    • 声明时
      • 参数类型、返回值类型可以省略
      • return关键字:默认使用方法最后一句的返回值作为方法返回值
    • 调用时,()可以省略
  • 支持顺序结构、分支结构、循环结构语句
  • 支持各种元算符:算术、关系、位、赋值、范围运算符
  • 基本类型也是对象,可以直接调用对象放方法
    • groovy中的字符串有单引号、双引号、三引号
      • 单引号:作为字符串常量使用,没有运算能力
      • 双引号:可以引用变量${},有运算能力
      • 三引号:模板字符串,支持换行,不支持变量引用
  • 数据类型:变量、属性、方法、闭包的参数以及方法返回值都类型都是可有可无的,都是在给变量赋值的时候才决定他的类型

类型转换: 当需要时,类型之间会自动发生类型转换,字符串(String)、基本类型(如int)和包装类型(如Integer)
类说明: 如果在一个groovy文件中没有任何类定义,它将被当成script来处理,也就意味着这个文件将被透明的转换为一个script类型的类,这个自动转换得到的类型将使用原始的groovy文件作为类的名字,groovy文件的内容被打包进run方法,另外在新生产的类中被加入一个main方法以进行外部执行该脚本

Groovy和java类之间的主要区别:

  1. 没有可见性修饰符的类或者方法,默认是公共的(可以使用一个特殊的注解来实现包的私有可见性)
  2. 没有可见性修饰符的字段将自动转化为属性,不需要显示的getter和setter方法
  3. 如果属性声明为final,则不会生成setter方法
  4. 一个源文件可能包含一个或多个类(但是如果一个文件不包含类定义的代码,则将其视为脚本),脚本只是具有一些特殊约定的类,他们的名称与原文件相同(所以不要再脚本 中包含与脚本源文件名相同的类定义)

Gradle 对测试的支持

测试任务自动检测并执行测试源集中的所有单元测试,测试执行完后会生成一个报告。
支持Junit测试和TestNG测试。

测试源码目录结构:

测试源码目录结构图示

测试源码编译后目录结构:

测试源码编译后目录结构

Junit使用

  • Gradle 对Junit4.x支持

    1
    2
    3
    4
    5
    6
    dependencies {  
    testImplementation('junit:junit:4.12')
    }
    test{
    useJUnit()
    }
  • Gradle 对Junit5.x版本的支持

    1
    2
    3
    4
    5
    6
    7
    dependencies {  
    testImplementation platform('org.junit:junit-bom:5.9.1')
    testImplementation 'org.junit.jupiter:junit-jupiter'
    }
    test {
    useJUnitPlatform()
    }

注意: 无论是Junit4.x还是Junit5.x版本,我们只需要再build.gradle目录下执行gradle test 指令,gradle就会帮我们执行所有加了@Test注解的测试,并生成测试报告。

包含和排除特定测试

  • 在执行gradle build打包的时候默认是会执行测试命令的;

  • 使用命令跳过测试 gradle build -X test

  • 也可以在build.gradle文件中进行配置跳过测试

    1
    2
    3
    4
    test {  
    enabled false
    useJUnitPlatform()
    }
  • 也可以指定只执行某个包下的测试类

    1
    2
    3
    4
    5
    test {  
    enabled true
    useJUnitPlatform()
    include('example/**')
    }
  • 排除不执行某个包下面的测试类

    1
    2
    3
    4
    5
    test {  
    enabled true
    useJUnitPlatform()
    exclude('example/**')
    }

Gradle进阶

项目的生命周期

Gradle项目的生命周期分为三大阶段:Initialization -> Configuration -> Execution(初始化->配置->执行),每个阶段都有自己的职责,具体如下图所示:

Gradle生命周期图示

首先在初始化阶段,会按照上面说的 USER_HOME/.gradle/ -> USER_HOME/.gradle/init.d/ -> GRADLE_HOME/init.d/ 的顺序去寻找初始化脚本,然后执行初始化脚本;
之后会执行项目根目录下的settings.gradle文件,里面配置了根项目的项目名,以及各个子项目的项目名;
配置阶段会根据初始化阶段settings.gradle中的信息,找到各个子项目,然后执行子项目中的build.gradle文件,执行的时候会按照层级结构进行执行,先加载根工程的脚本,然后再加载各个子工程的脚本,如果子工程还有子模块,会在所有二级子工程加载完毕之后继续向下加载;
最后按照有向无环树的结构执行任务task

  • Initialization:阶段主要目的是初始化构建,它又分两个子过程,一个是执行 init Script ,另一个是执行 setting Script。
    • init.gradle 文件会在每个项目 build 之前被调用,用于做一些初始化的操作,他主要有如下作用:
      • 配置内部的仓库信息(如公司的maven仓库信息)
      • 配置一些全局属性
      • 配置用户名及密码信息(如果公司仓库需要用户名和密码)
    • Setting Script 则更重要,它初始化了一次构建所参与的所有模块
  • Configuration: 这个阶段开始加载项目中所有模块的 Build Script ,所谓的加载就是执行build.gradle中的语句,根据脚本代码构建对应的 task ,最终根据所有task生成由 Task 组成的有向无环图,如下
    gradle有向无环图

最终构成有向无环图
Gradle有向无环树

  • Execution阶段: 这个阶段会根据上个阶段构建好的有向无环图,按着顺序执行task【Action动作】

settings文件

  • 作用:主要在项目初始化阶段确定一下引入那些工程需要加入到项目构建中,为项目工程树做准备。
  • 工程树:gradle中有工程树的概念,类似于maven中的project与module。
  • 内容:里面主要定义了当前gradle项目及子project的项目名称。
  • 位置:必须放在根工程目录下,有且仅有一个。
  • 名字:为settings.gradle文件,不能发生变化。
  • 对应实例:与org.gradle.api.initialization.Settings实例是一一对应的关系,每个项目只有一个settings文件。
  • 关注:作为开发者我们只需要关注该文件中的include方法即可,使用相对路径【:】引入子工程
  • 一个子工程只有在setting文件中配置才会被gradle识别,这样在构建的时候才会被包含进去。
    1
    2
    3
    4
    5
    rootProject.name = 'demo-gradle'  
    rootProject.name = 'demo-gradle'
    include('demo1')
    include('demo1:demo11')
    include('demo1:demo12')

项目名称中【:】表示项目的分隔符,类似于路径中的【/】,如果以【:】开头表示相对于rootProject,

Task

项目实质上是 Task 对象的集合,一个Task表示一个逻辑上较为独立的执行过程,比如编译java源代码,拷贝文件,打包jar文件,甚至可以执行一个系统命令,另外一个Task可以读取和设置项目的属性以完成特定的操作

一个简单的任务:

1
2
3
4
5
6
7
8
9
10
11
task A{  
//任务的配置阶段
println 'root task A'
//任务的行为
doFirst {
println 'root task A doFirst'
}
doLast {
println 'root task A doLast'
}
}

在文件所在目录执行命令 gradle A 即可执行该任务

  • task 的配置段是在配置阶段完成
  • task 的doFirst、doLast 方法是在执行阶段完成,并且doFirst在doLast之情执行
  • 区分任务的配置段和任务的行为,任务的配置段在配置阶段执行,任务的行为在执行阶段执行

也可以在任务外部定义行为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
task A{  
//任务的配置阶段
println 'root task A'
//任务的行为
doFirst {
println 'root task A doFirst'
}
doLast {
println 'root task A doLast'
}
}

A.doFirst{
println 'doFirst'
}
A.doLast{
println 'rdoLast'
}

给任务设置action:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def map = new HashMap<String,Object>()  
map.put('action',{println 'task A action'})
task (map,"A"){
//任务的配置阶段
println 'root task A'
//任务的行为
doFirst {
println 'root task A doFirst'
}
doLast {
println 'root task A doLast'
}
}

A.doFirst{
println 'doFirst'
}
A.doLast{
println 'rdoLast'
}

底层原理分析:无论是定义任务自身的 action 还是添加的doLast、doFirst方法,其实底层都是被放入到一个Action的List中了,最初这个action list是空的,当我们设置了action【任务自身的行为】,它先将action添加到列表中,此时列表中只有一个action,后续执行 doFirst的时候doFirst在action前面添加,执行doLast的时候,doLast在action后面添加。
doFirst永远在action list的第一位,保证添加的方法在现有的action list元素的最前面;
doLast永远在action list末尾添加,保证其添加的方法在现有action list元素的最后面;
一个往前添加,一个往后添加,最后这个action list就按顺序形成了 doFirst、doSelf、doLast的顺序,只需要从头开始执行,就形成了执行顺序。

任务的依赖关系

不同的任务之间可以有关系也可以没有关系,如果不同任务之间需要建立依赖关系,可以通过下面的方式。
Task之间的依赖关系可以在以下几部分设置:

  1. 参数依赖
  2. 内部依赖
  3. 外部依赖

方式一:参数依赖

1
2
3
4
5
6
7
8
9
10
task A{  
doLast {println('Task A')}
}
task B{
doLast {println('Task B')}
}
//参数方式依赖:dependsOn后面用冒号
task ('C',dependsOn: ['A','B']){
doLast {println('Task C')}
}

方式二:内部依赖

1
2
3
4
5
6
7
8
9
10
11
task A{  
doLast {println('Task A')}
}
task B{
doLast {println('Task B')}
}
task C{
//内部依赖dependsOn后面要用 = 号
dependsOn= ['A','B']
doLast {println('Task C')}
}

方式三:外部依赖

1
2
//可变参数,引号可加可不加
C.dependsOn('A','B')

也可以跨项目执行,比如子工程demo01下面定义了一个任务A:

1
2
3
4
5
task C{  
//使用相对于根工程的相对路径
dependsOn(':demo01:A')
doLast {println('Task C')}
}
  • 当一个task依赖多个task的时候,被依赖的task之间如果没有依赖关系,那么他们的执行顺序是随机的,并无影响。
  • 重复的任务只会执行一次,比如:
    • A依赖B和C,B也依赖C,那么C只会被执行一次

任务执行

任务执行的语法: gradle [taskName…] [–option-name…]

常见任务:

命令 解释
gradle build 构建项目:编译、测试、打包等操作
gradle run 运行一个服务,需要 application 插件支持,并且指定了主启动类才能运行
gradle clean 清空当前项目的build目录
gradle init 初始化gradle项目
gradle wrapper 生成wrapper文件夹
gradle wrapper –gradle-version=4.4 升级wrapper版本号
gradle wrapper – gradle-version 5.2.1 –distribution-type all 关联源码用

项目报告相关任务:

命令 解释
gradle projects 列出所选项目及子项目列表,以层次结构的形式显示
gradle tasks 列出所选项目(当前项目,不包含父子)的已分配给任务组的那些任务
gradle tasks –all 列出所选项目的所有任务
gradle tasks –group=”build setup” 列出所选项目中指定分组中的任务
gradle help –task someTask 显示某个任务的详细信息
gradle dependencies 查看整个项目的依赖信息,以依赖树的方式显示
gradle properties 列出所选项目的属性列表

调试相关:

命令 解释
gradle -h / –help 查看帮助信息
gradle -v / –version 打印版本信息(Gradle、Kotlin、Groovy、Ant、JVM、OS)
gradle -S/ –full-stacktrace 打印出所有异常的完整(非常详细)堆栈跟踪信息
gradle -s/–stacktrace 打印出用户异常的堆栈跟踪(例如编译错误)
-Dorg.gradle.daemon.debug=true 调试 Gradle 守护进程
-Dorg.gradle.debug=true :调试 Gradle 客户端(非 daemon)进程
-Dorg.gradle.debug.port=(port number) 指定启用调试时要侦听的端口号。默认值为 5005

性能选项:【在gradle.properties中指定这些选项中的许多选项,因此不需要命令行标志】

命令 解释
–build-cache, –no-build-cache 尝试重用先前版本的输出。默认关闭(off)
–max-workers 设置 Gradle 可以使用的 woker 数。默认值是处理器数。
-parallel, –no-parallel 并行执行项目。有关此选项的限制,请参阅并行项目执行。 默认设置为关闭(off)

守护进程选项:

命令 解释
–daemon, –no-daemon 使用 Gradle 守护进程运行构建。默认是 on
–foreground 在前台进程中启动 Gradle 守护进程。
-Dorg.gradle.daemon.idletimeout=(number of milliseconds) Gradle Daemon 将在这个空闲时间的毫秒数之后停止自己。默认值为 10800000(3 小时)。

日志选项

命令 解释
-Dorg.gradle.logging.level=(quiet,warn,lifecycle,info,debug) 通过 Gradle 属性设置日志记录级别。
-q, –quiet 只能记录错误信息
-w, –warn 设置日志级别为 warn
-i, –info 将日志级别设置为 info
-d, –debug 登录调试模式(包括正常的堆栈跟踪)

其他:

命令 解释
-x -x 等价于: –exclude-task : 常见 gradle -x test clean build
–rerun-tasks 强制执行任务,忽略 up-to-date ,常见 gradle build –rerun-tasks
–continue 忽略前面失败的任务,继续执行,而不是在遇到第一个失败时立即停止执行。每个遇到的故障都将在构建结束时报告,常见:gradle build –continue。
gradle init –type pom 将 maven 项目转换为 gradle 项目(根目录执行)
gradle [taskName] 执行自定义任务

任务名称可以使用缩写,任务名称支持驼峰命名风格,然后执行任务的时候使用缩写,例如:connecTask,简写是cT,执行任务的时候直接执行 gradle cT命令

gradle默认各指令之间的依赖关系:

gradle默认各指令之间的依赖关系

相关解释:
gradle默认各指令之间的依赖关系解释

任务的定义 方式

任务定义方式总体分为两大类,一种是通过Proiject中的 task() 方法,另一种是通过tasks对象中的 tasks.create() 或者 tasks.register() 方法
tasks.register()执行的是延迟创建,只有当该任务被需要的时候才会被创建

我们可以在定义任务的时候同时指定任务的属性,具体属性有:

配置项 描述 默认值
type 基于一个存在的Task来创建,和我们的类继承差不多 DefaultTask
overwrite 是否替换存在的Task,这个和type配合起来使用 false
dependsOn 用于配置任务的依赖 []
action 添加到任务中的一个Action或者一个闭包 null
description 用于配置任务的描述 null
group 用于配置任务的分组 null

定义任务时也可以给任务分配属性:可以定义任务的时候直接指定任务属性,也可以给已有的任务动态分配属性

任务类型

前面我们定义的task都是DefaultTask类型的,如果我们想要完成某些具体的操作完全需要我们自己去编写gradle脚本,这样有些麻烦,所以gradle官网给出了一些现成的任务类型帮助我们快速完成想要的任务,我们只需要在创建任务的时候,指定当前任务的类型即可,然后就可以使用这种类型中的属性和API方法了

常见任务类型 该类型任务的作用
Delete 删除文件或者目录
Copy 将文件复制到目标目录中,此任务还可以在复制时重命名和筛选文件。
CreateStartScripts 创建启动脚本
Exec 执行任务命令行进程
GenerateMavenPom 生成Maven模块描述(POM)文件
GradleBuild 执行Gradle构建
Jar 组装JAR归档文件
JavaCompile 编译Java源文件
Javadoc 为Java类生成Html Api文档
PublishToMavenRepository 将 MavenPublication 发布到 mavenartifactrepostal。
Tar 组装 TAR 存档文件
Test 执行 JUnit (3.8.x、4.x 或 5.x)或 TestNG 测试。
Upload 将 Configuration 的构件上传到一组存储库。
War 组装 WAR 档案。
Zip 组装

更详细的可以参考官方文档:https://docs.gradle.org/current/dsl/index.html

任务的执行顺序

在Gradle中,有三种方式可以指定Task指定顺序:

  1. dependsOn 强依赖方式
  2. 通过Task输入输出
  3. 通过API指定执行顺序

详细的可以参考官网文档:https://docs.gradle.org/current/dsl/org.gradle.api.Task.html

动态分配任务

gradle支持在循环中注册同一类型的多个任务:

1
2
3
4
5
6
7
8
9
4.times {counter->{  
tasks.register("task$counter"){
doLast {println "task$counter"}
}
}}
//注册完任务之后,可以通过 API 访问他们,比如在运行时动态的向任务添加依赖项
tasks.named('task0'){
dependsOn('task1','task2')
}

任务的关系和开启

每个任务都有一个 enabled 标志,默认为 true ,将其设置为 flase 可以阻止任务执行,任务将会标记为”跳过”

1
2
3
4
task A{  
enabled = false
doLast {println('Task A')}
}

任务超时

每个任务都有一个 timeout 属性可以限制其执行时间,当任务达到超时时间时,任务执行线程将被中断,该任务将被标记为失败。如果 –continue 使用,其他任务可以在此之后继续运行。

1
2
3
4
5
6
7
8
9
10
task A{  
doLast {
Thread.sleep(1000)
println('Task A')
}
timeout = Duration.ofMillis(500)
}
task B{
doLast {println('task B')}
}

在控制台使用: gradle A B 测试会发现执行 A 的时候,由于 A 执行超时,抛出异常,所以没有继续往下执行【B 也没执行】。 然后在控制台使用: gradle A B –continue,测试会发现 a 虽然执行失败,但是 B 还是执行

任务的查找

常见的任务查找方法:

1
2
3
4
5
6
//通过任务名查找
tasks.getByName()
tasks.findByName()
//通过相对路径查找
tasks.getByPath()
tasks.findByPath()

任务的规则

当我们执行或者依赖一个不存在的任务时,gradle会执行失败,报错误信息,那我们是否能够修改规则,当一个任务不存在时,不是报错而是打印提示信息呢?

1
2
3
4
5
6
7
8
9
10
11
12
task hello { 
doLast {
println 'hello world!'
}
}
tasks.addRule("对该规则的一个描述,便于调试、查看等"){
String taskName -> task(taskName) {
doLast {
println "该${taskName}任务不存在,请查证后再执行"
}
}
}

测试: 使用 gradle abc hello 进行测试,此时当 abc 任务不存在时,也不会报异常【不中断执行】而是提示自定义的规则信 息,继续执行 hello 任务。此外,它还可以根据不同的规则动态创建需要的任务等情况。

任务的 onlyIf 断言

断言就是一个条件表达式。Task 有一个 onlyIf 方法。它接受一个闭包作为参数,如果该闭包返回 true 则该任务执行, 否则跳过。这有很多用途,比如控制程序哪些情况下打什么包,什么时候执行单元测试,什么情况下执行单元测试的时 候不执行网络测试等。具体案例如下所示:

1
2
3
4
5
6
7
task hello { 
doLast {
println 'hello 尚硅谷的粉丝们'
}
}
//是否存在 fensi 属性
hello.onlyIf { !project.hasProperty('fensi') }

测试:通过-P 为 Projec
gradle hello -Pfensi

gradle中的文件操作

几种常见的文件操作方式:

  • 本地文件
  • 文件集合
  • 文件树
  • 文件拷贝
  • 归档文件

本地文件

使用 Project.file(java.lang.Object)方法,通过指定文件的相对路径或绝对路径来对文件的操作,其中相对路径为相对当前 project[根 project 或者子 project]的目录。其实使用 Project.file(java.lang.Object)方法创建的 File 对象就是 Java 中的 File 对象,我们可以使用它就像在 Java 中使用一样

1
2
3
4
5
6
7
8
9
10
11
//使用相对路径 
File configFile = file('src/conf.xml')
configFile.createNewFile();

// 使用绝对路径
configFile = file('D:\\conf.xml')
println(configFile.createNewFile())

// 使用一个文件对象
configFile = new File('src/config.xml')
println(configFile.exists())

文件集合

文 件 集 合 就 是 一 组 文 件 的 列 表 , 在 Gradle 中 , 文 件 集 合 用 FileCollection 接 口 表 示 。 我 们 可 以 使 用 Project.files(java.lang.Object[])方法来获得一个文件集合对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def collection = files('src/test1.txt',new File('src/test2.txt'),['src/test3.txt', 'src/test4.txt']) 
collection.forEach(){File it ->
it.createNewFile() //创建该文件
println it.name //输出文件名
}
Set set1 = collection.files // 把文件集合转换为java中的Set类型
Set set2 = collection as Set
List list = collection as List// 把文件集合转换为java中的List类型
for (item in list) {
println item.name
}
def union = collection + files('src/test5.txt') // 添加或者删除一个集合
def minus = collection - files('src/test3.txt')
union.forEach(){File it ->
println it.name
}

对于文件集合我们可以遍历它;也可以把它转换成 java 类型;同时还能使用+来添加一个集合,或使用-来删除集合。

文件树

文件树是有层级结构的文件集合,一个文件树它可以代表一个目录结构或一 ZIP 压缩包中的内容结构。文件树是从文件集 合继承过来的,所以文件树具有文件集合所有的功能。我们可以使用 Project.fileTree(java.util.Map)方法来创建文件树对象, 还可以使用过虑条件来包含或排除相关文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 第一种方式:使用路径创建文件树对象,同时指定包含的文件
tree = fileTree('src/main').include('**/*.java')

//第二种方式:通过闭包创建文件树:
tree = fileTree('src/main') {
include '**/*.java'
}

//第三种方式:通过路径和闭包创建文件树:具名参数给map传值
tree = fileTree(dir: 'src/main', include: '**/*.java')

tree = fileTree(dir: 'src/main', includes: ['**/*.java', '**/*.xml', '**/*.txt'], exclude: '**/*test*/**')

tree.each {File file -> // 遍历文件树的所有文件
println file
println file.name
}

文件拷贝

我们可以使用 Copy 任务来拷贝文件,通过它可以过虑指定拷贝内容,还能对文件进行重命名操作等。Copy 任务必须指 定一组需要拷贝的文件和拷贝到的目录,这里使用 CopySpec.from(java.lang.Object[])方法指定原文件;使用 CopySpec.into(java.lang.Object)方法指定目标目录。

1
2
3
4
task copyTask(type: Copy) { 
from 'src/main/resources'
into 'build/config'
}

from()方法接受的参数和文件集合时 files()一样。当参数为一个目录时,该目录下所有的文件都会被拷贝到指定目录下(目 录自身不会被拷贝);当参数为一个文件时,该文件会被拷贝到指定目录;如果参数指定的文件不存在,就会被忽略; 当参数为一个 Zip 压缩文件,该压缩文件的内容会被拷贝到指定目录。
into()方法接受的参数与本地文件时 file()一样。

1
2
3
4
5
6
7
8
9
10
task copyTask(type: Copy) { 
// 拷贝src/main/webapp目录下所有的文件
from 'src/main/webapp'
// 拷贝单独的一个文件
from 'src/staging/index.html'
// 从Zip压缩文件中拷贝内容
from zipTree('src/main/assets.zip')
// 拷贝到的目标目录
into 'build/explodedWar'
}

在拷贝文件的时候还可以添加过虑条件来指定包含或排除的文件,示例如下:

1
2
3
4
5
6
task copyTaskWithPatterns(type: Copy) { 
from 'src/main/webapp'
into 'build/explodedWar'
include '**/*.html' include '**/*.jsp'
exclude { details -> details.file.name.endsWith('.html') }
}

在拷贝文件的时候还可以对文件进行重命名操作,示例如下:

1
2
3
4
5
6
7
8
task rename(type: Copy) { 
from 'src/main/webapp'
into 'build/explodedWar'
// 使用一个闭包方式重命名文件
rename { String fileName ->
fileName.replace('-staging-', '')
}
}

在上面的例子中我们都是使用 Copy 任务来完成拷贝功能的,那么有没有另外一种方式呢?答案是肯定的,那就是 Project.copy(org.gradle.api.Action)方法。下面示例展示了 copy()方法的使用方式:

1
2
3
4
5
6
7
8
9
10
task copyMethod { 
doLast {
copy {
from 'src/main/webapp'
into 'build/explodedWar'
include '**/*.html'
include '**/*.jsp'
}
}
}

或者使用 project 对象的 copy 方法

1
2
3
4
5
copy { 
//相对路径或者绝对路径
from file('src/main/resources/ddd.txt') //file也可以换成new File()
into this.buildDir.absolutePath
}

执行 gradle build 指令即可。去 build 目录的本地磁盘查看,就能看到

归档文件

通常一个项目会有很多的 Jar 包,我们希望把项目打包成一个 WAR,ZIP 或 TAR 包进行发布,这时我们就可以使用 Zip,Tar,Jar,War 和 Ear 任务来实现,不过它们的用法都一样,所以在这里我只介绍 Zip 任务的示例。 首先,创建一个 Zip 压缩文件,并指定压缩文件名称,如下代码所示:

1
2
3
4
5
6
7
8
apply plugin: 'java'
version=1.0
task myZip(type: Zip) {
from 'src/main'
into 'build' //保存到build目录中
baseName = 'myGame'
}
println myZip.archiveNam

Dependencies

依赖方式

Gradle 中的依赖分别为直接依赖,项目依赖,本地 jar

1
2
3
4
5
6
7
8
9
10
11
12
13
dependencies { 
//①.依赖当前项目下的某个模块[子工程]
implementation project(':subject01')

//②.直接依赖本地的某个jar文件
implementation files('libs/foo.jar', 'libs/bar.jar')

//②.配置某文件夹作为依赖项
implementation fileTree(dir: 'libs', include: ['*.jar'])

//③.直接依赖
implementation 'org.apache.logging.log4j:log4j:2.17.2'
}
  • 直接依赖:在项目中直接导入的依赖,就是直接依赖
  • 项目依赖: 从项目的某个模块依赖另一个模块
  • 本地 jar 依赖:本地 jar 文件依赖,一般包含以下两种方式

依赖下载

当执行 build 命令时,gradle 就会去配置的依赖仓库中下载对应的 Jar,并应用到项目中。

依赖的类型

类似于 Maven 的 scope 标签,gradle 也提供了依赖的类型,具体如下所示

描述
compileOnly 由java插件提供,曾短暂的叫provided,后续版本已经改成了compileOnly,适用于编译期需要而不需要打包的情况
runtimeOnly 由 java 插件提供,只在运行期有效,编译时不需要,比如 mysql 驱动包。,取代老版本中被移除的 runtime
implementation 由 java 插件提供,针对源码[src/main 目录] ,在编译、运行时都有效,取代老版本中被移除的 compile
testCompileOnly 由 java 插件提供,用于编译测试的依赖项,运行时不需要
testRuntimeOnly 由 java 插件提供,只在测试运行时需要,而不是在测试编译时需要,取代老版本中被移除的 testRuntime
testImplementation 由 java 插件提供,针对测试代码[src/test 目录] 取代老版本中被移除的 testCompile
providedCompile war 插件提供支持,编译、测试阶段代码需要依赖此类 jar 包,而运行阶段容器已经提供了相应的支持,所 以无需将这些文件打入到 war 包中了;例如 servlet-api.jar、jsp-api.jar
api java-library 插件提供支持,这些依赖项可以传递性地导出给使用者,用于编译时和运行时。取代老版本中被 移除的 compile
compileOnlyApi java-library 插件提供支持,在声明模块和使用者在编译时需要的依赖项,但在运行时不需要
官方文档:
各个依赖范围的关系和说明
依赖范围升级和移除
API 和 implemention 区别
执行java 命令时都使用了哪些依赖范围的依赖。

java 插件提供的功能,java-library 插件都提供(java-library可以替代java插件)。

api 与 implementation 区别

| api | implementation|
|:—-:|:—-:|:—-:|
|编译时|能进行依赖传递,底层变,全部都要变,编译速度慢|不能进行依赖传递,底层变,不用全部都要变,编译速度快|
|运行时|运行时会加载,所有模块的class都要被加载|运行时会加载,所有模块的class都要被加载|
|应用场景|适用于多模块依赖,避免重复依赖模块|多数情况下使用implementation|

gradle依赖传递图示

编译时:如果 libC 的内容发生变化,由于使用的是 api 依赖,依赖会传递,所以 libC、libA、projectX 都要发生变化,都需要重新编译,速度慢;
运行时: libC、libA、projectX 中的 class 都要被加载。
编译时: 如果 libD 的内容发生变化,由于使用的是 implemetation 依赖,依赖不会传递,只有 libD、libB 要变化并重新编译,速度快。
运行时: libC、libA、projectX 中的 class 都要被加载。

依赖冲突及解决方案

依赖冲突是指在编译过程中,如果存在某个依赖的多个版本,构建系统是应该选择哪个进行构建的问题。
gradle默认情况下会使用最新的版本,因为考虑到大多数情况下新版本都会向下兼容
不过也可以强制排除某个版本
排除某个依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
dependencies {

testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
implementation('org.hibernate:hibernate-core:3.6.3.Final'){
//排除某一个库(slf4j)依赖:如下三种写法都行
exclude group: 'org.slf4j'
exclude module: 'slf4j-api'
exclude group: 'org.slf4j',module: 'slf4j-api'
}
//排除之后,使用手动的引入即可。
implementation 'org.slf4j:slf4j-api:1.4.0'
}

不允许依赖传递

1
2
3
4
5
6
7
8
9
10
dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
implementation('org.hibernate:hibernate-core:3.6.3.Final'){
//不允许依赖传递,一般不用
transitive(false)
}
//排除之后,使用手动的引入即可
implementation 'org.slf4j:slf4j-api:1.4.0'
}

强制使用某个版本

1
2
3
4
5
6
7
8
9
10
11
12
13
dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
implementation('org.hibernate:hibernate-core:3.6.3.Final')
//强制使用某个版本!!【官方建议使用这种方式】
implementation('org.slf4j:slf4j-api:1.4.0!!')
//这种效果和上面那种一样,强制指定某个版本
implementation('org.slf4j:slf4j-api:1.4.0'){
version{
strictly("1.4.0")
}
}
}
1
2
3
4
5
6
//下面我们配置,当 Gradle 构建遇到依赖冲突时,就立即构建失败  
configurations.all() {
Configuration configuration ->
//当遇到版本冲突时直接构建失败
configuration.resolutionStrategy.failOnVersionConflict()
}

Gradle插件

使用插件的原因

简单的说,通过应用插件我们可以:

  1. 促进代码重用、减少功能类似代码编写、提升工作效率
  2. 促进项目更高程度的模块化、自动化、便捷化
  3. 可插拔式的的扩展项目的功能

插件的作用

在项目构建过程中做很多事情,把插件应用到项目中,通常可以完成:

  1. 可以添加任务【task】到项目中,从而帮助完成测试、编译、打包等。
  2. 可以添加依赖配置到项目中。
  3. 可以向项目中拓展新的扩展属性、方法等。
  4. 可以对项目进行一些约定,如应用 Java 插件后,约定 src/main/java 目录是我们的源代码存在位置,编译时编译这个目录下的 Java 源代码文件。

插件的分类和使用

插件的分类和使用图示

第一种:脚本插件

脚本插件的本质就是一个脚本文件,使用脚本插件时通过 apply from 将脚本加载进来就可以了,后面的脚本文件可以是本地的也可以是网络上的脚本文件,比如定义version.gradle脚本文件:

1
2
3
4
//version.gradle 
ext{
company="Google"
}

在构建文件 bulid.gradle 引入脚本文件(执行的时候相当于把脚本文件中的内容复制到主脚本文件),在主脚本文件中添加如下代码进行测试:

1
2
3
4
5
apply from: 'version.gradle'  

task testVersion{
println("${company}")
}

在根目录命令行执行 gradle testVersion 命令即可进行测试

意义

脚本文件模块化的基础,可按功能把我们的脚本进行拆分成一个个公用、职责分明的文件,然后在主脚本文件中引用,比如:将很多共有的库版本号一起管理、应用构建版本一起管理等。

第二种:对象插件之内部插件【核心插件】

二进制插件【对象插件】就是实现了org.gradle.api.Plugin接口的插件,每个 Java Gradle 插件都有一个PluginId

gradle二进制插件示意图

核心插件【内部插件】

gradle具体包含哪些内部插件,可以参考gradle官方文档:
Gradle Plugin Reference

plugins DSL 引入方式

官方默认的方式
比如引入一个java插件可以使用如下写法:

1
2
3
plugins {
id 'java'
}
apply方式

map具名参数

1
2
3
4
5
6
7
8
9
//一共有三种写法
//第一种:key:plugin,value:插件id
apply plugin:'java'

//第二种:插件的全类名
apply plugin:org.gradle.api.plugins.JavaPlugin

//第三种:如果插件所在的包路径已经被引入,可以使用插件的简类名
apply plugin: JavaPlugin

闭包

1
2
3
apply {  
plugin 'java'
}
第三方插件

如果使用第三方提供的插件,一般需要配置对应的仓库和类路径。
具体也可以参考gradle的插件官网:
Gradle - Plugins

使用传统方式

传统方式一般分为两步,需要先引入插件,然后再应用插件。

1
2
3
4
5
6
7
8
9
10
11
12
13
buildscript {
repositories {
maven {
url = uri("https://plugins.gradle.org/m2/")
}
}
//先引入插件
dependencies {
classpath("io.github.turansky.kfc:kfc-gradle-plugin:7.9.2")
}
}
//再应用插件
apply(plugin = "io.github.turansky.kfc.application")

注意: 如果使用传统方式引入插件buildscript标签必须放到bulid.gradle文件最上面

plugins DSL方式

如果第三方插件已经被托管到了 https://plugins.gradle.org/ 网站上,就可以不用在 buildscript 里面配置引入了,可以使用使用 plugins DSL 方式引入,如下所示:

1
2
3
plugins {
id("io.github.turansky.kfc.application") version "7.9.2"
}
自定义插件

可以参考官方文档进行自定义:
Developing Custom Gradle Plugins

1
2
3
4
5
6
7
8
9
10
11
12
13
//首先定义一个插件,我们插件里面只设置了一个任务
class GreetingPlugin implements Plugin<Project> {
void apply(Project project) {
project.task('hello') {
doLast {
println 'Hello from the GreetingPlugin'
}
}
}
}

// 然后应用插件
apply plugin: GreetingPlugin

定义一个插件的方式并不止这一种,具体的可以参考上面的官方文档。

这种定义插件的方式局限性太大,只能在当前project中使用,在其他project中是不能使用的。

buildSrc项目

buildSrc是Gradle默认的插件目录,编辑Gradle的时候会自动识别这个目录,将其中的代码编译为插件。

  1. 首先需要创建一个名为 buildSrcjava Module,将buildSrcincluded modules 中移除,然后只保留build.gradlesrc/main目录,其他全部删除,注意名字一定是buildSrc,不然会找不到插件。
  2. 然后修改build.gradle文件中的内容
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    apply plugin: 'groovy' //必须  
    apply plugin: 'maven-publish'

    dependencies {
    implementation gradleApi() //必须
    implementation localGroovy() //必须
    }
    repositories {
    google()
    jcenter()
    mavenCentral() //必须
    }
    //设置项目的入口
    sourceSets {
    main {
    groovy {
    srcDir '/src/main/groovy'
    }
    }
    }
  3. 创建入口目录,在 src/main下面创建代码入口目录,如下:
    buildSrc项目目录图示
  4. 然后 实现插件代码 Test.groovy
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    package org.example  

    import org.gradle.api.Plugin
    import org.gradle.api.Project

    class Test implements Plugin<Project> {
    @Override
    void apply(Project project) {
    project.task("testPlugin"){
    doLast {
    println("自定义testPlugin插件")
    }
    }
    }
    }
  5. 接下来在 main 目录下面创建 resources 目录,在resources 目录下面创建 META-INF 目录,在 META-INF 目录下创建 gradle-plugins 目录 gradle-plugins 目录下创建 properties 文件。
  6. properties文件可以自己命名,但是要以 .properties 结尾,比如 org.example.plugin.properties 其中 org.example.plugin 就是定义的包路径(引入的时候的id)。
  7. 最后需要在properties文件指明我们实现插件的全类名 implementation-class=org.example.Test

buildSrc项目目录图示1

插件已经定义完毕,可以直接在module中引入我们定义的插件:

1
apply plugin: 'org.example.plugin'

然后执行命令: gradle testPlugin

改进

上面的写法,可以在整个工程里面使用到插件,但是也仅限在本工程内,其他工程并不能使用。
如果想要让其他工程也使用这个插件,可以把插件发布到maven私服中。

第一步: 首先将上述的buildSrc目录复制一份,修改文件夹名,然后在settings.gradle文件中引入;
第二步: 修改 build.gradle文件,发布到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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
apply plugin: 'groovy' //必须  
apply plugin: 'maven-publish' //发布插件使用

dependencies {
implementation gradleApi() //必须
implementation localGroovy() //必须
}
repositories {
google()
jcenter()
mavenCentral() //必须
}

sourceSets {
main {
groovy {
srcDir '/src/main/groovy'
}
}
}

publishing {
publications {
myLibrary(MavenPublication){
groupId = 'org.example.plugin'
artifactId = 'ysPlugin'
version = '1.1'
from components.java
}
}
repositories {
//发布到本地
maven {url "$rootDir/lib/release"}
//发布到私服
maven {
name = 'myRepo' //name属性可选,表示仓库名称,url必填
//发布地址可以是本地仓库或者是maven私服
//url = layout.buildDirectory.dir("repo")
url = 'http://my.org/repo'
//认证信息,用户名和密码
credentials{
username = 'jj'
password = 'sss'
}
}
}
}

第三步: 可以选择发布到maven私服,或者本地仓库,也可以发布到某一个指定文件夹,这里演示的是发布到项目根目录的某个文件夹内.

执行publish命令发布到指定的文件夹,还有其他几个选项可以发布到本地仓库或者maven私服

发布gradle自定义插件到指定目录

第四步: 使用插件,在项目级 build.gradle 文件中,将插件添加到calsspath

1
2
3
4
5
6
7
8
9
10
buildscript {  
repositories {
maven {url "$rootDir/lib/release"}
}
dependencies {
classpath "org.example.plugin:ysPlugin:1.1"
}
}

apply plugin: 'org.example.plugin'

第五步: 指定gradle build即可看到自定义插件的输出
如果本地环境变量配置的gradle和java版本和idea中使用的不一致,在命令行运行该命令可能会失败,可以刷新之后,在idea右侧的gradle中找到根目录下的tasks,找到other,自定义的任务会出现在这里

插件的关注点

第一点:插件的引用

1
apply plugin: '插件名'

第二点:主要的功能【任务】

当我们在工程中引入插件后,插件会自动为我们的工程添加一些额外的任务来完成相应的功能。
以java插件为例,当我们加入java插件之后,就加入了如下功能:

gradle默认各指令之间的依赖关系

具体的可以使用 gradle tasks 命令查看某个插件加入前后的区别。
说明: gradle中任务依赖关系很重要,他们之间的依赖关系形成了构建的基本流程。

第三点:工程目录结构

有些插件对工程的目录结构有约定,所以我们一般要遵循它的约定来创建工程,这要是 gradle 的约定优于配置原则,例如java插件规定的项目目录结构如下:

测试源码目录结构图示
一般来说我们会按照插件约定的目录来进行开发,但也可以自己自定义目录结构。

第四点:依赖管理

比如我们前面提到的依赖类型-依赖管理部分,不同的插件提供了不同的依赖管理。

第五点:常用的属性

每个插件都会提供一些常用的属性,以供我们直接使用,具体可以查看官方文档。

build.gradle 文件

  • build.gradle 是一个 gradle 的构建脚本文件,支持 java、groovy等语言。
  • 每个project都会有一个build.gradle文件,该文件是项目构建的入口,可配置版本、插件、依赖库等信息。
  • 每个build文件都有一个对应的project实例,build.gradle文件配置,本质就是设置project实例的属性和方法。
  • 由于每个project都会有一个build文件,那么Root Project也不例外,Root Project可以获取到所有的子项目,所以在Root Project的build文件中我们可以对子项目进行统一配置,比如应用的插件、依赖的maven中央仓库等。
  • build文件中常见的属性和方法如下所示:

build中常见的属性和方法