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官网下载需要的版本进行安装
Gradle | Releases
或者
Gradle Distributions
gradle官网下载由于某些原因网速会特别慢,也可以去国内镜像网站下载:
Index of /gradle/ (tencent.com)
说明: gradle有完整版(以all结尾的)和二进制版(以bin结尾的)两种,完整版的相对于二进制版的多了一个gradle的源码和文档。
注意: idea和gradle也会有兼容性问题,如果想要知道自己安装的idea对应的gradle是什么版本,可以去idea的安装目录下的\plugins\gradle\lib
下去查看
配置环境变量
- 将gradle解压到对应目录中之后,在环境变量中配置gradle的路径
- 先在环境变量中新增一个
GRADLE_HOME
的变量 - 然后把gradle下的bin目录配置到path中
- 先在环境变量中新增一个
注意: gradle默认会把相关依赖下载到C盘用户目录下,需要在环境变量中配置GRADLE_USER_HOME
,配置了该变量相当于是maven指定本地仓库
检查是否按照成功
1 | gradle -v # 或者 graddle --version |
Gradle项目目录结构
gradle项目目录结构和maven项目结构一直,都是基于约定大于配置;
其完整目录结构如下所示:
- build:封装编译后的字节码、打成的包(jar、war)、测试报告等信息
- gradle:封装包装器文件夹
- gradle-wrapper.jar
- gradle-wrapper.properties
- src:源码
- main:项目源码
- java:java的代码
- resources:配置
- test:项目测试源码
- java:测试java代码
- resources:测试配置
- main:项目源码
- build.gradle:构建脚本,类似与maven中的pom.xml
- gradlew:包装器启动脚本,linux下的可执行脚本
- gradlew.bat:Windows下的可执行脚本,(无论是在linux还是window下,启动脚本执行的都是
gradle/wrapper
目录中配置的gradle的版本,而不是本地安装的graddle) - settings.gradle:设置文件,低调定义项目及子项目名称信息,和项目是一一对应
创建gradle项目
- 创建gradle项目有两种方式,第一种是可以使用spring脚手架的方式
- springboot脚手架:Spring Initializr
- 第二种方式是使用gradle命令创建
- 新建一个文件夹,在文件夹内打开命令行,并执行
graddle init
命令 - 第一步:选择是生成一个应用还是其他的,我们是生成一个应用所以选择2
- 第二步:选择语言,我们是使用java语言,选择3
- 第三步:创建单应用还是多模块应用,我们这里是单应用,选择1
- 第四步:选择构建脚本的语言,选择1
- 第五步:选择测试框架,选择1
- 第六部:输入项目名,如果不输入,默认使用当前文件夹名称
- 第七步:输入包名称
- 新建一个文件夹,在文件夹内打开命令行,并执行
注意: 通过命令行创建出来的项目根目录下没有src和build.gradle文件,而是多了一个app目录,src和build.gradle就在app目录中,其实就是相当于创建了一个多模块应用,只不过只有一个模块。
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开始之前执行,所以你可以在这个文件内配置一些你想要预先加载的操作
在init.d
文件夹创建init.gradle
文件
1 | allprojects { |
注意: mavenLocal()
是指本地maven仓库,需要配置环境变量才行,M2_HOME
启用 init.gradle
文件的方法
- 在命令行指定文件,例如
gradle --init-script dir/init.graddle -q taskName
你可以多次输入此命令来指定多个init文件 - 把
init.gradle
放到USER_HOME/.gradle/
目录下 - 把以
.gradle
结尾的文件放到USER_HOME/.gradle/init.d/
目录下 - 把以
.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中的字符串有单引号、双引号、三引号
- 单引号:作为字符串常量使用,没有运算能力
- 双引号:可以引用变量${},有运算能力
- 三引号:模板字符串,支持换行,不支持变量引用
- groovy中的字符串有单引号、双引号、三引号
- 数据类型:变量、属性、方法、闭包的参数以及方法返回值都类型都是可有可无的,都是在给变量赋值的时候才决定他的类型
类型转换: 当需要时,类型之间会自动发生类型转换,字符串(String)、基本类型(如int)和包装类型(如Integer)
类说明: 如果在一个groovy文件中没有任何类定义,它将被当成script来处理,也就意味着这个文件将被透明的转换为一个script类型的类,这个自动转换得到的类型将使用原始的groovy文件作为类的名字,groovy文件的内容被打包进run方法,另外在新生产的类中被加入一个main方法以进行外部执行该脚本
Groovy和java类之间的主要区别:
- 没有可见性修饰符的类或者方法,默认是公共的(可以使用一个特殊的注解来实现包的私有可见性)
- 没有可见性修饰符的字段将自动转化为属性,不需要显示的getter和setter方法
- 如果属性声明为final,则不会生成setter方法
- 一个源文件可能包含一个或多个类(但是如果一个文件不包含类定义的代码,则将其视为脚本),脚本只是具有一些特殊约定的类,他们的名称与原文件相同(所以不要再脚本 中包含与脚本源文件名相同的类定义)
Gradle 对测试的支持
测试任务自动检测并执行测试源集中的所有单元测试,测试执行完后会生成一个报告。
支持Junit测试和TestNG测试。
测试源码目录结构:
测试源码编译后目录结构:
Junit使用
Gradle 对Junit4.x支持
1
2
3
4
5
6dependencies {
testImplementation('junit:junit:4.12')
}
test{
useJUnit()
}Gradle 对Junit5.x版本的支持
1
2
3
4
5
6
7dependencies {
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
4test {
enabled false
useJUnitPlatform()
}也可以指定只执行某个包下的测试类
1
2
3
4
5test {
enabled true
useJUnitPlatform()
include('example/**')
}排除不执行某个包下面的测试类
1
2
3
4
5test {
enabled true
useJUnitPlatform()
exclude('example/**')
}
Gradle进阶
项目的生命周期
Gradle项目的生命周期分为三大阶段:Initialization -> Configuration -> Execution(初始化->配置->执行),每个阶段都有自己的职责,具体如下图所示:
首先在初始化阶段,会按照上面说的
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 组成的有向无环图,如下
最终构成有向无环图
- 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
5rootProject.name = 'demo-gradle'
rootProject.name = 'demo-gradle'
include('demo1')
include('demo1:demo11')
include('demo1:demo12')
项目名称中【:】表示项目的分隔符,类似于路径中的【/】,如果以【:】开头表示相对于rootProject,
Task
项目实质上是 Task 对象的集合,一个Task表示一个逻辑上较为独立的执行过程,比如编译java源代码,拷贝文件,打包jar文件,甚至可以执行一个系统命令,另外一个Task可以读取和设置项目的属性以完成特定的操作
一个简单的任务:
1 | task A{ |
在文件所在目录执行命令 gradle A
即可执行该任务
- task 的配置段是在配置阶段完成
- task 的doFirst、doLast 方法是在执行阶段完成,并且doFirst在doLast之情执行
- 区分任务的配置段和任务的行为,任务的配置段在配置阶段执行,任务的行为在执行阶段执行
也可以在任务外部定义行为:
1 | task A{ |
给任务设置action:
1 | def map = new HashMap<String,Object>() |
底层原理分析:无论是定义任务自身的 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 | task A{ |
方式二:内部依赖
1 | task A{ |
方式三:外部依赖
1 | //可变参数,引号可加可不加 |
也可以跨项目执行,比如子工程demo01下面定义了一个任务A:
1 | 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默认各指令之间的依赖关系:
相关解释:
任务的定义 方式
任务定义方式总体分为两大类,一种是通过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指定顺序:
- dependsOn 强依赖方式
- 通过Task输入输出
- 通过API指定执行顺序
详细的可以参考官网文档:https://docs.gradle.org/current/dsl/org.gradle.api.Task.html
动态分配任务
gradle支持在循环中注册同一类型的多个任务:
1 | 4.times {counter->{ |
任务的关系和开启
每个任务都有一个 enabled 标志,默认为 true ,将其设置为 flase 可以阻止任务执行,任务将会标记为”跳过”
1 | task A{ |
任务超时
每个任务都有一个 timeout 属性可以限制其执行时间,当任务达到超时时间时,任务执行线程将被中断,该任务将被标记为失败。如果 –continue 使用,其他任务可以在此之后继续运行。
1 | task A{ |
在控制台使用: gradle A B 测试会发现执行 A 的时候,由于 A 执行超时,抛出异常,所以没有继续往下执行【B 也没执行】。 然后在控制台使用: gradle A B –continue,测试会发现 a 虽然执行失败,但是 B 还是执行
任务的查找
常见的任务查找方法:
1 | //通过任务名查找 |
任务的规则
当我们执行或者依赖一个不存在的任务时,gradle会执行失败,报错误信息,那我们是否能够修改规则,当一个任务不存在时,不是报错而是打印提示信息呢?
1 | task hello { |
测试: 使用 gradle abc hello 进行测试,此时当 abc 任务不存在时,也不会报异常【不中断执行】而是提示自定义的规则信 息,继续执行 hello 任务。此外,它还可以根据不同的规则动态创建需要的任务等情况。
任务的 onlyIf 断言
断言就是一个条件表达式。Task 有一个 onlyIf 方法。它接受一个闭包作为参数,如果该闭包返回 true 则该任务执行, 否则跳过。这有很多用途,比如控制程序哪些情况下打什么包,什么时候执行单元测试,什么情况下执行单元测试的时 候不执行网络测试等。具体案例如下所示:
1 | task hello { |
测试:通过-P 为 Projec
gradle hello -Pfensi
gradle中的文件操作
几种常见的文件操作方式:
- 本地文件
- 文件集合
- 文件树
- 文件拷贝
- 归档文件
本地文件
使用 Project.file(java.lang.Object)
方法,通过指定文件的相对路径或绝对路径来对文件的操作,其中相对路径为相对当前 project[根 project 或者子 project]的目录。其实使用 Project.file(java.lang.Object)
方法创建的 File 对象就是 Java 中的 File 对象,我们可以使用它就像在 Java 中使用一样
1 | //使用相对路径 |
文件集合
文 件 集 合 就 是 一 组 文 件 的 列 表 , 在 Gradle 中 , 文 件 集 合 用 FileCollection 接 口 表 示 。 我 们 可 以 使 用 Project.files(java.lang.Object[])方法来获得一个文件集合对象
1 | def collection = files('src/test1.txt',new File('src/test2.txt'),['src/test3.txt', 'src/test4.txt']) |
对于文件集合我们可以遍历它;也可以把它转换成 java 类型;同时还能使用+来添加一个集合,或使用-来删除集合。
文件树
文件树是有层级结构的文件集合,一个文件树它可以代表一个目录结构或一 ZIP 压缩包中的内容结构。文件树是从文件集 合继承过来的,所以文件树具有文件集合所有的功能。我们可以使用 Project.fileTree(java.util.Map)方法来创建文件树对象, 还可以使用过虑条件来包含或排除相关文件。
1 | // 第一种方式:使用路径创建文件树对象,同时指定包含的文件 |
文件拷贝
我们可以使用 Copy 任务来拷贝文件,通过它可以过虑指定拷贝内容,还能对文件进行重命名操作等。Copy 任务必须指 定一组需要拷贝的文件和拷贝到的目录,这里使用 CopySpec.from(java.lang.Object[])方法指定原文件;使用 CopySpec.into(java.lang.Object)方法指定目标目录。
1 | task copyTask(type: Copy) { |
from()方法接受的参数和文件集合时 files()一样。当参数为一个目录时,该目录下所有的文件都会被拷贝到指定目录下(目 录自身不会被拷贝);当参数为一个文件时,该文件会被拷贝到指定目录;如果参数指定的文件不存在,就会被忽略; 当参数为一个 Zip 压缩文件,该压缩文件的内容会被拷贝到指定目录。
into()方法接受的参数与本地文件时 file()一样。
1 | task copyTask(type: Copy) { |
在拷贝文件的时候还可以添加过虑条件来指定包含或排除的文件,示例如下:
1 | task copyTaskWithPatterns(type: Copy) { |
在拷贝文件的时候还可以对文件进行重命名操作,示例如下:
1 | task rename(type: Copy) { |
在上面的例子中我们都是使用 Copy 任务来完成拷贝功能的,那么有没有另外一种方式呢?答案是肯定的,那就是 Project.copy(org.gradle.api.Action)方法。下面示例展示了 copy()方法的使用方式:
1 | task copyMethod { |
或者使用 project 对象的 copy 方法
1 | copy { |
执行 gradle build 指令即可。去 build 目录的本地磁盘查看,就能看到
归档文件
通常一个项目会有很多的 Jar 包,我们希望把项目打包成一个 WAR,ZIP 或 TAR 包进行发布,这时我们就可以使用 Zip,Tar,Jar,War 和 Ear 任务来实现,不过它们的用法都一样,所以在这里我只介绍 Zip 任务的示例。 首先,创建一个 Zip 压缩文件,并指定压缩文件名称,如下代码所示:
1 | apply plugin: 'java' |
Dependencies
依赖方式
Gradle 中的依赖分别为直接依赖,项目依赖,本地 jar
1 | dependencies { |
- 直接依赖:在项目中直接导入的依赖,就是直接依赖
- 项目依赖: 从项目的某个模块依赖另一个模块
- 本地 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|
编译时:如果 libC 的内容发生变化,由于使用的是 api 依赖,依赖会传递,所以 libC、libA、projectX 都要发生变化,都需要重新编译,速度慢;
运行时: libC、libA、projectX 中的 class 都要被加载。
编译时: 如果 libD 的内容发生变化,由于使用的是 implemetation 依赖,依赖不会传递,只有 libD、libB 要变化并重新编译,速度快。
运行时: libC、libA、projectX 中的 class 都要被加载。
依赖冲突及解决方案
依赖冲突是指在编译过程中,如果存在某个依赖的多个版本,构建系统是应该选择哪个进行构建的问题。
gradle默认情况下会使用最新的版本,因为考虑到大多数情况下新版本都会向下兼容
不过也可以强制排除某个版本
排除某个依赖
1 | dependencies { |
不允许依赖传递
1 | dependencies { |
强制使用某个版本
1 | dependencies { |
1 | //下面我们配置,当 Gradle 构建遇到依赖冲突时,就立即构建失败 |
Gradle插件
使用插件的原因
简单的说,通过应用插件我们可以:
- 促进代码重用、减少功能类似代码编写、提升工作效率
- 促进项目更高程度的模块化、自动化、便捷化
- 可插拔式的的扩展项目的功能
插件的作用
在项目构建过程中做很多事情,把插件应用到项目中,通常可以完成:
- 可以添加任务【task】到项目中,从而帮助完成测试、编译、打包等。
- 可以添加依赖配置到项目中。
- 可以向项目中拓展新的扩展属性、方法等。
- 可以对项目进行一些约定,如应用 Java 插件后,约定 src/main/java 目录是我们的源代码存在位置,编译时编译这个目录下的 Java 源代码文件。
插件的分类和使用
第一种:脚本插件
脚本插件的本质就是一个脚本文件,使用脚本插件时通过 apply from
将脚本加载进来就可以了,后面的脚本文件可以是本地的也可以是网络上的脚本文件,比如定义version.gradle
脚本文件:
1 | //version.gradle |
在构建文件 bulid.gradle
引入脚本文件(执行的时候相当于把脚本文件中的内容复制到主脚本文件),在主脚本文件中添加如下代码进行测试:
1 | apply from: 'version.gradle' |
在根目录命令行执行 gradle testVersion
命令即可进行测试
意义
脚本文件模块化的基础,可按功能把我们的脚本进行拆分成一个个公用、职责分明的文件,然后在主脚本文件中引用,比如:将很多共有的库版本号一起管理、应用构建版本一起管理等。
第二种:对象插件之内部插件【核心插件】
二进制插件【对象插件】就是实现了org.gradle.api.Plugin
接口的插件,每个 Java Gradle 插件都有一个PluginId
核心插件【内部插件】
gradle具体包含哪些内部插件,可以参考gradle官方文档:
Gradle Plugin Reference
plugins DSL 引入方式
官方默认的方式
比如引入一个java插件可以使用如下写法:
1 | plugins { |
apply方式
map具名参数
1 | //一共有三种写法 |
闭包
1 | apply { |
第三方插件
如果使用第三方提供的插件,一般需要配置对应的仓库和类路径。
具体也可以参考gradle的插件官网:
Gradle - Plugins
使用传统方式
传统方式一般分为两步,需要先引入插件,然后再应用插件。
1 | buildscript { |
注意: 如果使用传统方式引入插件buildscript
标签必须放到bulid.gradle
文件最上面
plugins DSL方式
如果第三方插件已经被托管到了 https://plugins.gradle.org/ 网站上,就可以不用在 buildscript
里面配置引入了,可以使用使用 plugins DSL 方式引入,如下所示:
1 | plugins { |
自定义插件
可以参考官方文档进行自定义:
Developing Custom Gradle Plugins
1 | //首先定义一个插件,我们插件里面只设置了一个任务 |
定义一个插件的方式并不止这一种,具体的可以参考上面的官方文档。
这种定义插件的方式局限性太大,只能在当前project中使用,在其他project中是不能使用的。
buildSrc项目
buildSrc是Gradle默认的插件目录,编辑Gradle的时候会自动识别这个目录,将其中的代码编译为插件。
- 首先需要创建一个名为
buildSrc
的java Module
,将buildSrc
从included modules
中移除,然后只保留build.gradle
和src/main
目录,其他全部删除,注意名字一定是buildSrc,不然会找不到插件。 - 然后修改build.gradle文件中的内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20apply plugin: 'groovy' //必须
apply plugin: 'maven-publish'
dependencies {
implementation gradleApi() //必须
implementation localGroovy() //必须
}
repositories {
google()
jcenter()
mavenCentral() //必须
}
//设置项目的入口
sourceSets {
main {
groovy {
srcDir '/src/main/groovy'
}
}
} - 创建入口目录,在 src/main下面创建代码入口目录,如下:
- 然后 实现插件代码
Test.groovy
:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15package org.example
import org.gradle.api.Plugin
import org.gradle.api.Project
class Test implements Plugin<Project> {
void apply(Project project) {
project.task("testPlugin"){
doLast {
println("自定义testPlugin插件")
}
}
}
} - 接下来在
main
目录下面创建resources
目录,在resources
目录下面创建META-INF
目录,在META-INF
目录下创建gradle-plugins
目录gradle-plugins
目录下创建properties
文件。 properties
文件可以自己命名,但是要以.properties
结尾,比如org.example.plugin.properties
其中org.example.plugin
就是定义的包路径(引入的时候的id)。- 最后需要在
properties
文件指明我们实现插件的全类名implementation-class=org.example.Test
插件已经定义完毕,可以直接在module中引入我们定义的插件:
1 | apply plugin: 'org.example.plugin' |
然后执行命令: gradle testPlugin
改进
上面的写法,可以在整个工程里面使用到插件,但是也仅限在本工程内,其他工程并不能使用。
如果想要让其他工程也使用这个插件,可以把插件发布到maven私服中。
第一步: 首先将上述的buildSrc
目录复制一份,修改文件夹名,然后在settings.gradle
文件中引入;
第二步: 修改 build.gradle
文件,发布到maven仓库。
1 | apply plugin: 'groovy' //必须 |
第三步: 可以选择发布到maven私服,或者本地仓库,也可以发布到某一个指定文件夹,这里演示的是发布到项目根目录的某个文件夹内.
执行publish命令发布到指定的文件夹,还有其他几个选项可以发布到本地仓库或者maven私服
第四步: 使用插件,在项目级 build.gradle
文件中,将插件添加到calsspath
1 | buildscript { |
第五步: 指定gradle build
即可看到自定义插件的输出
如果本地环境变量配置的gradle和java版本和idea中使用的不一致,在命令行运行该命令可能会失败,可以刷新之后,在idea右侧的gradle中找到根目录下的tasks,找到other,自定义的任务会出现在这里
插件的关注点
第一点:插件的引用
1 | apply plugin: '插件名' |
第二点:主要的功能【任务】
当我们在工程中引入插件后,插件会自动为我们的工程添加一些额外的任务来完成相应的功能。
以java插件为例,当我们加入java插件之后,就加入了如下功能:
具体的可以使用 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文件中常见的属性和方法如下所示: