第一章:Gradle 任务简介
1.1 概述
- Gradle 项目中会包含若干个任务(task)进行描述,同时每个任务定义的语句形式都是利用 Groovy 语法完成的(现在也支持 kotlin 了)。
- 在一个 Gradle 项目中利用任务的概念,可以将一个完整的处理任务拆分成若干个细小的子任务。
1.2 任务列表
- 当创建完成一个 Gradle 项目之后,默认情况下都会存在若干个任务列表,可以直接通过 Gradle 命令查看:
gradle -q tasks
- 其实,在 IDEA 中展示的就是这些任务列表:
1.3 项目属性
- Gradle 项目是基于 Groovy 语言编写的,所以在使用 Groovy 操作 Gradle 项目的时候需要获取一些项目的信息,可以通过如下的命令获取所有的项目属性:
gradle -q properties
1.4 任务定义
- 清楚了
项目
和任务
的基本关系之后,下面就需要动手来直接定义一个任务,所有的任务定义都是使用 Groovy 语法来完成的,所有的任务都可以直接在build.gradle
文件里面进行定义处理。
plugins {
id 'java' // 配置的是一个 Java 插件(Java 项目)
}
group 'com.github.fairy.era' // 组织名称
version '1.0' // 项目版本
sourceCompatibility = 1.8 // 源代码版本
repositories { // 仓库配置
mavenCentral()
}
dependencies { // 依赖管理
/* junit 5 */
testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter', version: '5.8.2'
}
tasks.withType(JavaCompile) { // 针对程序编译的任务进行配置
options.encoding = "UTF-8"
}
test { // 进行测试任务的配置
useJUnitPlatform() // 使用 Junit 平台
}
task hello { // 定义一个新的任务,任务的名称是 hello
println('你好,Gradle')
}
- 当任务定义完成之后,会自动的在 Gradle 命令执行列表中出现有任务的名称信息。
- 当然,也可以通过执行命令来执行该任务:
gradle hello
第二章:Gradle 任务定义语法
2.1 概述
- 通过之前的具体操作,已经成功的实现了一个 Gradle 自定义的任务项,但是需要注意的是,在 Gradle 里面任务的定义语法形式是非常丰富的,所以本次来研究一下各种任务的定义语法形式。
2.2 原型创建
- 在 Gradle 里面提供了一个 task() 处理函数,这个函数可以直接创建一个任务,同时需要设置好任务的名称。
- 对于任务的创建主要通过 org.gradle.api.Project 接口里面提供的任务创建方法:
Task task(String var1) throws InvalidUserDataException;
- 在使用 task() 方法进行任务创建的时候,需要设置一个字符串,修改
build.gradle
文件:
// 每一个创建出来的任务都是 Task(org.gradle.api.Task) 的对象实例
def task = task(helloTask) // 创建一个任务:helloTask
- 每当使用
task()
方法创建的任务都自动返回一个 Task 接口的实例,Task 接口的定义如下:
package org.gradle.api;
public interface Task extends Comparable<Task>, ExtensionAware {
String TASK_NAME = "name";
String TASK_DESCRIPTION = "description";
String TASK_GROUP = "group";
String TASK_TYPE = "type";
String TASK_DEPENDS_ON = "dependsOn";
String TASK_OVERWRITE = "overwrite";
String TASK_ACTION = "action";
String TASK_CONSTRUCTOR_ARGS = "constructorArgs";
// 其他略
}
- 当获取到了一个 Task 接口的实例之后,就需要进行此任务的具体执行行为的配置(任务体),修改
build.gradle
文件:
// 每一个创建出来的任务都是 Task(org.gradle.api.Task) 的对象实例
def task = task(helloTask) // 创建一个任务:helloTask
task.doLast { // 在任务之后执行此操作
println '你好啊,Groovy'
}
2.3 任务属性
- 在创建 task 的时候,有可以使用 Project 接口中提供的其他的创建方法,同时设置好一些相应的任务的属性内容。
- 任务的创建方法:
Task task(Map<String, ?> var1, String var2) throws InvalidUserDataException;
- 设置任务的属性,修改
build.gradle
文件:
// 每一个创建出来的任务都是 Task(org.gradle.api.Task) 的对象实例
def task = task(description: '自定义 Gradle 任务的描述', helloTask) // 创建一个任务:helloTask
task.doLast { // 在任务之后执行此操作
println '你好啊,Groovy'
}
- 程序执行命令:
gradle helloTask
- 程序执行结果:
14:11:10: 正在执行 'helloTask'…
> Task :helloTask
你好啊,Groovy
BUILD SUCCESSFUL in 50ms
1 actionable task: 1 executed
14:11:10: 执行完成 'helloTask'。
- 程序执行命令:
gradle help --task helloTask
- 程序执行结果:
14:11:20: 正在执行 'help --task helloTask'…
> Task :help
Detailed task information for helloTask
Path
:helloTask
Type
Task (org.gradle.api.Task)
Description
自定义 Gradle 任务的描述
Group
-
BUILD SUCCESSFUL in 44ms
1 actionable task: 1 executed
14:11:20: 执行完成 'help --task helloTask'。
- 这些描述信息可以直接在进行任务帮助的时候进行详细的显示操作。
- 设置更多的属性内容,修改
build.gradle
文件:
// 每一个创建出来的任务都是 Task(org.gradle.api.Task) 的对象实例
def task = task([description: '自定义 Gradle 任务的描述',group: 'com.github.fairy.era'], helloTask) // 创建一个任务:helloTask
task.doLast { // 在任务之后执行此操作
println '你好啊,Groovy'
}
- 程序执行命令:
gradle help --task helloTask
- 程序执行结果:
14:14:28: 正在执行 'help --task helloTask'…
> Task :help
Detailed task information for helloTask
Path
:helloTask
Type
Task (org.gradle.api.Task)
Description
自定义 Gradle 任务的描述
Group
com.github.fairy.era
BUILD SUCCESSFUL in 56ms
1 actionable task: 1 executed
14:14:28: 执行完成 'help --task helloTask'。
2.4 闭包机制
- 在 Groovy 这种提供闭包的处理操作机制,也可以用这样的语法形式来实现 Task 的创建,修改
build.gradle
文件:
task helloTask { // 进行任务的创建
description '自定义 Gradle 任务的描述'
group 'com.github.fairy.era'
doLast {
println '你好啊,Groovy'
}
}
- 需要注意的是,如果修改了 group 属性,那么任务项将不在 other 任务列表中显示了:
- 程序执行命令:
gradle help --task helloTask
- 程序执行结果:
14:27:39: 正在执行 'help --task helloTask'…
> Task :help
Detailed task information for helloTask
Path
:helloTask
Type
Task (org.gradle.api.Task)
Description
自定义 Gradle 任务的描述
Group
com.github.fairy.era
BUILD SUCCESSFUL in 39ms
1 actionable task: 1 executed
14:27:39: 执行完成 'help --task helloTask'。
- 以上这种任务创建的形式是比较正统的一种形式,因为这样操作的语法相对而言简单一些,但是需要注意的是,同样的定义形式在 Gradle 中也可以采用如下的两种方式代替,修改
build.gradle
文件:
task (helloTask) { // 进行任务的创建
description '自定义 Gradle 任务的描述'
group 'com.github.fairy.era'
doLast {
println '你好啊,Groovy'
}
}
task (helloTask, { // 进行任务的创建
description '自定义 Gradle 任务的描述'
group 'com.github.fairy.era'
doLast {
println '你好啊,Groovy'
}
})
2.5 TaskContainer
- 在 Gradle 中提供有一个 TaskContainer(任务容器) 的概念实现任务的创建操作,首先来观察当前这种任务创建的方法定义:
// 此方法在 org.gradle.api.tasks.TaskContainer 中定义
Task create(Map<String, ?> var1, Closure var2) throws InvalidUserDataException;
- 创建自定义任务,修改
build.gradle
文件:
tasks.create('helloTask') {
description '自定义 Gradle 任务的描述'
group 'com.github.fairy.era'
doLast {
println '你好啊,Groovy'
}
}
- 程序执行命令:
gradle help --task helloTask
- 程序执行结果:
14:43:31: 正在执行 'help --task helloTask'…
> Task :help
Detailed task information for helloTask
Path
:helloTask
Type
Task (org.gradle.api.Task)
Description
自定义 Gradle 任务的描述
Group
com.github.fairy.era
BUILD SUCCESSFUL in 58ms
1 actionable task: 1 executed
14:43:32: 执行完成 'help --task helloTask'。
第三章:Gradle 任务属性
3.1 概述
- 在每一个任务之中都会存在若干个属性信息,如:描述(description)、任务的分组(group),这些都属于任务属性,但是除了这些任务属性之外,还有一些关于任务继承相关的属性存在。
3.2 属性设置
- 所有的任务属性除了在任务定义的时候进行设置之外,还可以直接通过任务对象进行设置,修改
build.gradle
文件:
def ta = task helloTask { // 进行任务的创建
doFirst {
println '【doFirst】你好,Groovy'
}
doLast {
println '【doLast】 你好,Groovy'
}
}
// 属性设置
ta.group = 'com.github.fairy.era'
- 在进行任务分组的时候,为了方便起见也可以将其归纳到内置的 Gradle 任务分组之中,此时可以直接使用 BasePlugin 类中提供的一系列常量来进行配置。
public class BasePlugin implements Plugin<Project> {
public static final String CLEAN_TASK_NAME = "clean";
public static final String ASSEMBLE_TASK_NAME = "assemble";
public static final String BUILD_GROUP = "build";
public static final String UPLOAD_ARCHIVES_TASK_NAME = "uploadArchives";
public static final String UPLOAD_GROUP = "upload";
// 其余略
}
- 将任务分组归纳到内置的 Gradle 任务分组之中,修改
build.gradle
文件(以下写法,任选一种即可):
def ta = task helloTask { // 进行任务的创建
doFirst {
println '【doFirst】你好,Groovy'
}
doLast {
println '【doLast】 你好,Groovy'
}
}
// 属性设置
ta.group = BasePlugin.BUILD_GROUP // 设置任务所在的分组
ta.description = '自定义 Gradle 的任务描述'
def ta = task helloTask { // 进行任务的创建
doFirst {
println '【doFirst】你好,Groovy'
}
doLast {
println '【doLast】 你好,Groovy'
}
}
// 属性设置
helloTask { // 定义任务属性内容
group = BasePlugin.BUILD_GROUP // 设置任务所在的分组
description = '自定义 Gradle 的任务描述' // 定义任务描述
}
3.3 控制属性
- 既然任务已经可以直接创建了,并且所创建的任务也都可以正常执行,那么就可以通过任务属性的配置来设置某一个任务是否可以启动,修改
build.gradle
文件:
def ta = task helloTask { // 进行任务的创建
doFirst {
println '【doFirst】你好,Groovy'
}
doLast {
println '【doLast】 你好,Groovy'
}
}
// 属性设置
helloTask { // 定义任务属性内容
group = BasePlugin.BUILD_GROUP // 设置任务所在的分组
description = '自定义 Gradle 的任务描述' // 定义任务描述
enabled = false // 任务禁用
}
- 程序执行命令:
gradle helloTask
- 程序执行结果:
20:59:38: 正在执行 'helloTask'…
> Task :helloTask SKIPPED
BUILD SUCCESSFUL in 40ms
20:59:38: 执行完成 'helloTask'。
3.4 自定义任务类型(任务继承)
- 在 Gradle 里面,如果自定义的任务类型不能满足实际的开发需求时,最简单的方法就是编写自定义的任务类型,其只需要继承 DefaultTask 父类即可,修改
build.gradle
文件:
class CustomTask extends DefaultTask {
@TaskAction // 必须追加任务执行的注解
def doSelf() { // 定义任务的主体
println '【CustomTask - doSelf】 - 执行任务的主体'
}
@TaskAction
def doLast() {
println '【CustomTask - doLast】 - 任务主体执行完毕后的操作'
}
}
task helloTask(type: CustomTask) { // 进行任务的创建
group = BasePlugin.BUILD_GROUP // 设置任务所在的分组
description = '自定义 Gradle 的任务描述' // 定义任务描述
doFirst {
println '【doFirst】你好,Groovy'
}
doLast {
println '【doLast】 你好,Groovy'
}
}
- 程序执行命令:
gradle helloTask
- 程序执行结果:
8:09:04: 正在执行 'helloTask'…
> Task :helloTask
【doFirst】你好,Groovy
【CustomTask - doLast】 - 任务主体执行完毕后的操作
【CustomTask - doSelf】 - 执行任务的主体
【doLast】 你好,Groovy
BUILD SUCCESSFUL in 53ms
1 actionable task: 1 executed
8:09:05: 执行完成 'helloTask'。
3.5 onlyif
- 在任务执行的时候还有一个 onlyif 属性,这个属性的含义可以接收一个闭包的处理,根据处理的结果来判断此任务是否要执行(回调机制,如果返回 true ,就表示需要执行;如果返回 false ,就表示不需要执行),修改
build.gradle
文件:
final String BUILD_TASK_NAME = 'hello'
task helloTask {
group BasePlugin.BUILD_GROUP
description '自定义 Gradle 的任务描述'
onlyIf { // 此属性类似于 enabled 的形式
def executeResult = false // 执行的最终结果
// 对于此任务是否执行,取决于外部设置的一个属性内容,即在执行命令的时候所配置的属性内容
if (project.hasProperty('flag')) { // 判断是否存在 flag 配置属性
def flagProperties = project.property('flag') // 获取属性内容
if (BUILD_TASK_NAME == flagProperties) { // 和内置属性相同
executeResult = true // 要执行此任务
}
}
return executeResult // 根据此结果来判断是否执行任务
}
doLast {
println '你好啊,Groovy'
}
}
- 程序执行命令:
gradle helloTask
- 程序执行结果:
8:44:17: 正在执行 'helloTask'…
> Task :helloTask SKIPPED
BUILD SUCCESSFUL in 48ms
8:44:17: 执行完成 'helloTask'。
- 程序执行命令:
gradle -Pflag=hello helloTask
- 程序执行结果:
8:46:43: 正在执行 'helloTask -Pflag=hello'…
> Task :helloTask
你好啊,Groovy
BUILD SUCCESSFUL in 50ms
1 actionable task: 1 executed
8:46:43: 执行完成 'helloTask -Pflag=hello'。
第四章:Gradle 多任务定义
4.1 概述
- 通过之前的一系列分析,我们已经清楚的知道单个任务的定义语法,但是很多情况下有可能需要在一个 Gradle 项目里面定义若干个任务,如:gradle clean build,那么现在就尝试如何在 Gradle 里面实现多个任务的定义和执行。
4.2 多任务
- 在 build.gradle 文件中定义三个任务信息,修改
build.gradle
文件:
task helloTask1 { // 第一个执行任务
println '【第一个执行任务】'
}
task helloTask2 { // 第二个执行任务
println '【第二个执行任务】'
}
task helloTask3 { // 第三个执行任务
println '【第三个执行任务】'
}
- 程序执行命令:
gradle helloTask2 helloTask1 helloTask3
- 程序执行结果:
8:58:09: 正在执行 'helloTask2 helloTask1 helloTask3'…
> Configure project :
【第一个执行任务】
【第二个执行任务】
【第三个执行任务】
> Task :helloTask2 UP-TO-DATE
> Task :helloTask1 UP-TO-DATE
> Task :helloTask3 UP-TO-DATE
BUILD SUCCESSFUL in 65ms
8:58:09: 执行完成 'helloTask2 helloTask1 helloTask3'。
- 通过此时的多任务的执行结果,可以发现,在 build.gradle 里面定义的多个任务信息,实际上都会根据其定义的顺序来执行,而不是由 gradle 命令配置的顺序来执行。
4.3 任务依赖
- 既然已经存在了多个任务,那么这些任务彼此之间就可能存在有依赖关系(任务 A 依赖于 任务 B ,如:进行打包之前必须先进行编译之后,才能进行打包),这样的依赖关系就可以通过 dependsOn 来进行配置,修改
build.gradle
文件:
// 注意:所有依赖的任务彼此之间一定要有一个顺序,被依赖的任务一定要在之前进行定义
task helloTask1 { // 第一个执行任务
doLast {
println '【第一个执行任务】'
}
}
task helloTask2 { // 第二个执行任务
doLast {
println '【第二个执行任务】'
}
}
task helloTask3(dependsOn: [helloTask1, helloTask2]) { // 第三个执行任务,当前的任务依赖于前面的两个任务
doLast {
println '【第三个执行任务】'
}
}
task allTask(dependsOn: helloTask3) { // 总的执行任务
doFirst {
println 'allTask doFirst'
}
}
- 程序执行命令:
gradle allTask
- 程序执行结果:
9:11:17: 正在执行 'allTask'…
> Task :helloTask1
【第一个执行任务】
> Task :helloTask2
【第二个执行任务】
> Task :helloTask3
【第三个执行任务】
> Task :allTask
allTask doFirst
BUILD SUCCESSFUL in 51ms
4 actionable tasks: 4 executed
9:11:17: 执行完成 'allTask'。
- 可以发现,所有被依赖的任务会首先执行,而所有要执行的任务都会最后执行。
4.4 任务禁用
- 在之前学习任务属性的时候,学习过一个 enabled 属性,这个属性的内容会决定当前的任务是否执行,那么如果说被依赖的任务没有执行,那么会不会影响到其他的任务?修改
build.gradle
文件:
task helloTask1 { // 第一个执行任务
enabled false // 此任务直接跳过
doLast {
println '【第一个执行任务】'
}
}
task helloTask2 { // 第二个执行任务
doLast {
println '【第二个执行任务】'
}
}
task helloTask3(dependsOn: [helloTask1, helloTask2]) { // 第三个执行任务,当前的任务依赖于前面的两个任务
enabled false // 此任务直接跳过
doLast {
println '【第三个执行任务】'
}
}
task allTask(dependsOn: helloTask3) { // 总的执行任务
doFirst {
println 'allTask doFirst'
}
}
- 程序执行命令:
gradle allTask
- 程序执行结果:
9:16:19: 正在执行 'allTask'…
> Task :helloTask1 SKIPPED
> Task :helloTask2
【第二个执行任务】
> Task :helloTask3 SKIPPED
> Task :allTask
allTask doFirst
BUILD SUCCESSFUL in 334ms
2 actionable tasks: 2 executed
9:16:20: 执行完成 'allTask'。
- 此时存在的依赖关系,即便父任务没有执行,那么子任务也依然正确执行。
第五章:Gradle 任务定义深入
5.1 概述
- 现在已经清楚了 Gradle 任务定义的大部分的操作语法,实际上除了这些基础的语法之外,在任务处理的时候也存在着一些项目的属性、方法等信息。
5.2 任务属性
- 所有的任务实际上都会放在 TaskContainer 中进行任务管理(也是在项目中保存),那么如果要想在任务中获取到一些任务的属性,也是可以直接完成的,修改
build.gradle
文件:
task helloTask {
doFirst {
println '常规输出:你好,Gradle!!!'
println '任务名称 - 1:' + helloTask.name // 任务名称获取
println '任务名称 - 2:' + project.helloTask.name // 项目中的任务名称获取
println '任务名称 - 3:' + tasks.helloTask.name // 任务容器获取任务名称
println '任务名称 - 4:' + tasks['helloTask'].name // 任务容器获取任务名称
}
}
- 程序执行命令:
gradle helloTask
- 程序执行结果:
9:43:55: 正在执行 'helloTask'…
> Task :helloTask
常规输出:你好,Gradle!!!
任务名称 - 1:helloTask
任务名称 - 2:helloTask
任务名称 - 3:helloTask
任务名称 - 4:helloTask
BUILD SUCCESSFUL in 48ms
1 actionable task: 1 executed
9:43:55: 执行完成 'helloTask'。
5.3 任务路径
- 在每一个任务里面都会存在有任务路径,通常而言,建议任务路径名称和任务名称相同,修改
build.gradle
文件:
task helloTask {
doFirst {
println '常规输出:你好,Gradle!!!'
println '任务路径 - 1:' + helloTask.path // 任务路径获取
println '任务路径 - 2:' + project.helloTask.path // 项目中的任务路径获取
println '任务路径 - 3:' + tasks.helloTask.path // 任务容器获取任务路径
println '任务路径 - 4:' + tasks['helloTask'].path // 任务容器获取任务路径
println '任务路径 - 5:' + tasks.getByPath('helloTask').path // 任务路径获取
println '任务路径 - 6:' + tasks.getByPath(':helloTask').path // 任务路径获取
}
}
- 程序执行命令:
gradle :helloTask
- 程序执行结果:
9:50:36: 正在执行 ':helloTask'…
> Task :helloTask
常规输出:你好,Gradle!!!
任务路径 - 1::helloTask
任务路径 - 2::helloTask
任务路径 - 3::helloTask
任务路径 - 4::helloTask
任务路径 - 5::helloTask
任务路径 - 6::helloTask
BUILD SUCCESSFUL in 56ms
1 actionable task: 1 executed
9:50:36: 执行完成 ':helloTask'。
总结:整个 Gradle 中的项目信息是通过 project 得到的,对应的属性是通过 property() 方法获取,而所有的任务信息都可以通过 tasks(TasksContainer) 来获取,里面保存的就是一个 Map 集合。
5.4 任务顺序
- 对于任务来说,在整个 Gradle 里面有各种各样的处理语法,在之前也已经学习过一种最为简单的多任务执行操作,而当时定义的多任务是直接使用 task 的形式创建的,这样的创建形式在执行的时候,就会按照定义的顺序执行任务,但是如果此时采用的是下面的语法形式,则可以根据执行的顺序来进行配置,修改
build.gradle
文件:
def taskA = task helloTask {
doFirst {
println '你好啊,Groovy 中的 helloTask'
}
}
def taskB = task helloTask1 {
doFirst {
println '你好啊,Groovy 中的 helloTask1'
}
}
- 程序执行命令:
gradle helloTask1 helloTask
- 程序执行结果:
10:01:23: 正在执行 'helloTask1 helloTask'…
> Task :helloTask1
你好啊,Groovy 中的 helloTask1
> Task :helloTask
你好啊,Groovy 中的 helloTask
BUILD SUCCESSFUL in 192ms
2 actionable tasks: 2 executed
10:01:23: 执行完成 'helloTask1 helloTask'。
- 但是,此时希望不管什么时候都希望 helloTask1 在 helloTask 之后执行,所以此时就可以通过配置顺序的形式来定义任务的执行顺序,修改
build.gradle
文件:
def taskA = task helloTask {
doFirst {
println '你好啊,Groovy 中的 helloTask'
}
}
def taskB = task helloTask1 {
doFirst {
println '你好啊,Groovy 中的 helloTask1'
}
}
// B 任务用于在 A任务之后执行
taskB.mustRunAfter(taskA)
- 程序执行命令:
gradle helloTask1 helloTask
- 程序执行结果:
10:04:29: 正在执行 'helloTask1 helloTask'…
> Task :helloTask
你好啊,Groovy 中的 helloTask
> Task :helloTask1
你好啊,Groovy 中的 helloTask1
BUILD SUCCESSFUL in 96ms
2 actionable tasks: 2 executed
10:04:29: 执行完成 'helloTask1 helloTask'。
- 通过此时的执行结果可以清楚的发现:即便执行的时候任务的顺序没有设置好,但是由于在 build.gradle 文件中设置好了任务执行的顺序,那么所有的任务也会按照事前设置好的顺序依次执行。
5.5 任务错误
- 当在配置任务的时候,使用了 mustRunAfter 属性之后,如果还同时存在有依赖的关系,那么很有可能会存在递归任务的错误之中, 修改
build.gradle
文件:
def taskA = task helloTask {
doFirst {
println '你好啊,Groovy 中的 helloTask'
}
}
def taskB = task helloTask1 {
doFirst {
println '你好啊,Groovy 中的 helloTask1'
}
}
// 依赖环境的配置
taskA.dependsOn(taskB)
// B 任务用于在 A任务之后执行
taskB.mustRunAfter(taskA)
- 程序执行命令:
gradle helloTask
- 程序执行结果:
Circular dependency between the following tasks:
:helloTask
\--- :helloTask1
\--- :helloTask (*)
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
- 按照任务依赖,可能会先执行被依赖的任务,然后在执行本身的任务,但是按照 mustRunAfter 的做法,需要配置任务的执行顺序,所以此时会出现一个程序的递归错误。
5.6 依赖顺序解决
- 在庞大的 Gradle 项目开发过程之中,你很难保证不出现上面的任务错误(程序的递归错误),所以最佳的做法就是忽略递归执行任务的死循环,修改
build.gradle
文件:
def taskA = task helloTask {
doFirst {
println '你好啊,Groovy 中的 helloTask'
}
}
def taskB = task helloTask1 {
doFirst {
println '你好啊,Groovy 中的 helloTask1'
}
}
// 依赖环境的配置
taskA.dependsOn(taskB)
// B 任务用于在 A任务之后执行,修改这边啊,将 mustRunAfter 改为 shouldRunAfter
taskB.shouldRunAfter(taskA)
- 程序执行命令:
gradle helloTask
- 程序执行结果:
10:18:40: 正在执行 'helloTask'…
> Task :helloTask1
你好啊,Groovy 中的 helloTask1
> Task :helloTask
你好啊,Groovy 中的 helloTask
BUILD SUCCESSFUL in 48ms
2 actionable tasks: 2 executed
10:18:40: 执行完成 'helloTask'。
- 此时并没有强制性的发生任务先后执行的顺序逻辑,而是采用了一种较为柔和的方式来完成任务执行顺序的配置。
5.7 任务替换
- 按照 Java 程序,如果父类中的某个操作方法不好用,那么子类就可以考虑进行方法的扩展(通过重写来解决此问题);同样的问题也出现在 Gradle 之中,虽然 Gradle 提供了大量的内置的操作方法,但是如果我们发现某些操作方法不合适,那么也可以进行重写,修改
build.gradle
文件(以下方式,任选其一即可):
task build(overwrite: true) {
doFirst {
println '你好啊,Gradle'
}
}
def task = task([overwrite: true], build) {
doFirst {
println '你好啊,Gradle'
}
}
- 程序执行命令:
gradle build
- 程序执行结果:
10:48:54: 正在执行 'build'…
> Task :compileJava UP-TO-DATE
> Task :processResources NO-SOURCE
> Task :classes UP-TO-DATE
> Task :jar
> Task :assemble
> Task :compileTestJava UP-TO-DATE
> Task :processTestResources NO-SOURCE
> Task :testClasses UP-TO-DATE
> Task :test
> Task :check
> Task :build
你好啊,Gradle
BUILD SUCCESSFUL in 813ms
5 actionable tasks: 3 executed, 2 up-to-date
10:48:55: 执行完成 'build'。
5.8 任务失败
- 既然现在操作的是多任务,那么多任务彼此之间就可能有任务会出现失败情况,那么如果有的任务失败了会影响到其他的任务吗?为了模拟任务的失败,手动抛出一个任务停止的异常,修改
build.gradle
文件:
def taskA = task helloTask {
doFirst {
if (true) {
throw new StopExecutionException('我出异常了,你能拿我咋办?')
}
println '你好啊,Groovy 中的 helloTask'
}
}
def taskB = task helloTask1 {
dependsOn taskA
doFirst {
println '你好啊,Groovy 中的 helloTask1'
}
}
- 程序执行命令:
gradle helloTask1
- 程序执行结果:
10:58:44: 正在执行 'helloTask1'…
> Task :helloTask
> Task :helloTask1
你好啊,Groovy 中的 helloTask1
BUILD SUCCESSFUL in 44ms
2 actionable tasks: 2 executed
10:58:44: 执行完成 'helloTask1'。
- 虽然此时第一个任务中产生了各种异常,但是并不影响到后续的其他任务的执行。
第六章:Gradle 文件处理任务
6.1 概述
- 在以后编写最多的处理任务就是进行项目中各类资源文件的控制了,按照一个正规的项目开发来说,肯定需要提供有 resources、config 之类的文件目录,这些文件目录在进行打包的时候,肯定要将其全部打包的目标目录之中,此时就需要进行文件操作了。
6.2 文件创建
- 既然要打包,通常而言都会提供一些配置的目录,同时在配置目录下会存在有许多
*.properties
的文件,在 src 目录下创建一个 config/database.properties 文件,内容如下:
# 随便写
druid.database.name=erp
6.3 文件定位
- 如果要进行各种资源文件的处理,那么首先肯定需要解决的就是文件定位的问题了,修改
build.gradle
文件:
task fileTask { // 文件处理任务
doFirst {
// org.gradle.api.Project 提供的 File file(Object var1);
File configFileA = file('src/config/database.properties')
println '【文件定位 - A】' + configFileA
// 如果觉得以上的 file() 方式不习惯,那么也可以按照传统的方式编写完整路径
File configFileB = file(new File('src' + File.separator + 'config' + File.separator + 'database.properties'))
println '【文件定位 - B】' + configFileB
}
}
- 程序执行命令:
gradle fileTask
- 程序执行结果:
13:32:23: 正在执行 'fileTask'…
> Task :fileTask
【文件定位 - A】D:\project\gradle-01\src\config\database.properties
【文件定位 - B】D:\project\gradle-01\src\config\database.properties
BUILD SUCCESSFUL in 204ms
1 actionable task: 1 executed
13:32:23: 执行完成 'fileTask'。
- 在 Gradle 里面可以使用 Project 接口所提供的 file() 函数,直接针对于当前项目中的资源进行定位处理。
6.4 资源集合
- 如果一个项目中包含多种资源,那么也可以考虑使用资源集合的形式进行配置,在 src 目录下创建一个 config/dubbo.properties 文件,内容如下:
dubbo.application.name=gradle
- 修改
build.gradle
文件:
task fileTask { // 文件处理任务
doFirst {
// org.gradle.api.Project 提供的 ConfigurableFileCollection files(Object... var1);
def fileCollection = files('src/config/database.properties','src/config/dubbo.properties')
fileCollection.each { File file ->
println file
}
}
}
- 程序执行命令:
gradle fileTask
- 程序执行结果:
13:45:57: 正在执行 'fileTask'…
> Task :fileTask
D:\project\gradle-01\src\config\database.properties
D:\project\gradle-01\src\config\dubbo.properties
BUILD SUCCESSFUL in 47ms
1 actionable task: 1 executed
13:45:58: 执行完成 'fileTask'。
- 通过 files 函数返回的是一个 FileCollection 文件集合,这个集合属于一个可迭代的对象,所以可以使用 each() 函数。
6.5 集合操作
- 通过 FileCollection 可以获取文件的集合,但是有些情况下可能需要对集合中的内容进行扩充,在 src 目录下创建一个 config/redis.properties 文件,内容如下:
redis.server.address=192.168.0.1
- 修改
build.gradle
文件:
task fileTask { // 文件处理任务
doFirst {
// org.gradle.api.Project 提供的 ConfigurableFileCollection files(Object... var1);
def fileCollection = files('src/config/database.properties', 'src/config/dubbo.properties')
def union = fileCollection + files('src/config/redis.properties') // 集合相加
union.each { File file -> // 输出相加后的集合
println file
}
println '----------------------------'
def diff = fileCollection - files('src/config/database.properties')
diff.each { File file -> // 输出相减后的集合
println file
}
println '----------------------------'
println '【增加后的集合】' + union.files // 输出全部的内容
println '【减少后的集合】' + diff.singleFile // 单个集合中的内容,必须确定集合中只有一个
}
}
- 程序执行命令:
gradle fileTask
- 程序执行结果:
14:08:55: 正在执行 'fileTask'…
> Task :fileTask
D:\project\gradle-01\src\config\database.properties
D:\project\gradle-01\src\config\dubbo.properties
D:\project\gradle-01\src\config\redis.properties
----------------------------
D:\project\gradle-01\src\config\dubbo.properties
----------------------------
【增加后的集合】[D:\project\gradle-01\src\config\database.properties, D:\project\gradle-01\src\config\dubbo.properties, D:\project\gradle-01\src\config\redis.properties]
【减少后的集合】D:\project\gradle-01\src\config\dubbo.properties
BUILD SUCCESSFUL in 251ms
1 actionable task: 1 executed
14:08:56: 执行完成 'fileTask'。
6.6 文件列表
- 以上的做法是将所有文件的完整路径进行单独的配置,这样的配置实在是太繁琐了,那么就可以考虑指定目录下的文件进行全部加载操作,修改
build.gradle
文件:
task fileTask { // 文件处理任务
doFirst {
File configFile = file('src/config') // 定位要使用的文件路径
def fileCollection = files(configFile.listFiles()) // 文件列表
fileCollection.each {File file ->
println file
}
}
}
- 程序执行命令:
gradle fileTask
- 程序执行结果:
14:16:05: 正在执行 'fileTask'…
> Task :fileTask
D:\project\gradle-01\src\config\database.properties
D:\project\gradle-01\src\config\dubbo.properties
D:\project\gradle-01\src\config\redis.properties
BUILD SUCCESSFUL in 54ms
1 actionable task: 1 executed
14:16:05: 执行完成 'fileTask'。
- 此时直接设置了一个加载的目录,随后就可以将此目录中的全部内容进行自动的加载,整个的程序实在是太容易了。
6.7 文件树
- 文件树指的是一个按照层次结构分布的文件集合,如:一个文件树可以描述一个目录的组成结构,而在 Gradle 里面可以直接通过 fileTree() 来生成这样的文件树,修改
build.gradle
文件:
task fileTask { // 文件处理任务
doFirst {
def tree = fileTree('src') // 对 src 目录进行列表
tree.exclude('**/*.java') // 不显示 *.java 源文件
tree.include ('**/*.properties','**/*.xml')
tree.visit { element -> // 查看整个文件树的全部内容
println element
}
}
}
- 程序执行命令:
gradle fileTask
- 程序执行结果:
14:26:52: 正在执行 'fileTask'…
> Task :fileTask
file 'D:\project\gradle-01\src\config'
file 'D:\project\gradle-01\src\config\database.properties'
file 'D:\project\gradle-01\src\config\dubbo.properties'
file 'D:\project\gradle-01\src\config\redis.properties'
file 'D:\project\gradle-01\src\main'
file 'D:\project\gradle-01\src\main\java'
file 'D:\project\gradle-01\src\main\java\com'
file 'D:\project\gradle-01\src\main\java\com\github'
file 'D:\project\gradle-01\src\main\java\com\github\fairy'
file 'D:\project\gradle-01\src\main\java\com\github\fairy\era'
file 'D:\project\gradle-01\src\main\java\com\github\fairy\era\service'
file 'D:\project\gradle-01\src\main\java\com\github\fairy\era\service\impl'
file 'D:\project\gradle-01\src\main\resources'
file 'D:\project\gradle-01\src\test'
file 'D:\project\gradle-01\src\test\java'
file 'D:\project\gradle-01\src\test\java\com'
file 'D:\project\gradle-01\src\test\java\com\github'
file 'D:\project\gradle-01\src\test\java\com\github\era'
file 'D:\project\gradle-01\src\test\java\com\github\era\fairy'
file 'D:\project\gradle-01\src\test\java\com\github\era\fairy\service'
file 'D:\project\gradle-01\src\test\resources'
BUILD SUCCESSFUL in 54ms
1 actionable task: 1 executed
14:26:52: 执行完成 'fileTask'。
6.8 源文件目录
- 在所有的构建工具创建的项目里面一般都会包含有源文件的目录,这些源文件的目录也可以直接在任务中进行访问,但是首先需要明确的定义出可能包含的源文件目录,修改
build.gradle
文件:
sourceSets { // 建立源代码的目录集合
main {
java {
srcDirs = ['src/main/java']
}
resources {
srcDirs = ['src/main/resources', 'src/config']
}
}
}
task fileTask { // 文件处理任务
doFirst {
sourceSets.main.allJava.each { File file ->
println file
}
println '-------------------'
sourceSets.main.resources.each { File file ->
println file
}
}
}
- 程序执行命令:
gradle fileTask
- 程序执行结果:
14:44:34: 正在执行 'fileTask'…
> Task :fileTask
D:\project\gradle-01\src\main\java\com\github\fairy\era\service\IMessageService.java
D:\project\gradle-01\src\main\java\com\github\fairy\era\service\impl\MessageServiceImpl.java
-------------------
D:\project\gradle-01\src\config\database.properties
D:\project\gradle-01\src\config\dubbo.properties
D:\project\gradle-01\src\config\redis.properties
BUILD SUCCESSFUL in 41ms
1 actionable task: 1 executed
14:44:34: 执行完成 'fileTask'。
- 在实际的项目开发过程之中,以上配置里面所使用的 sourceSets 环境配置项目,开发中一般都会进行定义。
6.9 文件拷贝
- 通过一系列的操作已经清楚了文件的基本流程,在以后进行项目打包输出控制的时候这样的操作是最为方便的,但是如果要想真正将其应用到项目里面还需要掌握文件拷贝的操作,将 config 目录移动到 src/main 下。
- 修改
build.gradle
文件:
sourceSets { // 建立源代码的目录集合
main {
java {
srcDirs = ['src/main/java']
}
resources {
srcDirs = ['src/main/resources', 'src/main/config']
}
}
}
task fileTask(type: Copy) { // 定义一个文件拷贝的子任务
from 'src/main/config' // [配置文件]定义要拷贝的程序来源
from 'src/main/resources' // [配置文件]定义要拷贝的程序来源
from 'src/main/java' // [程序文件]编译后的文件内容
into 'build/out' // 拷贝的目标路径
include '**/*.xml' // 包含的文件类型(匹配后缀)
include '**/*.properties' // 包含的文件类型(匹配后缀)
exclude '**/*.java' // 不包含源文件
}
- 程序执行命令:
gradle fileTask
- 程序执行结果:
- 在日后进行项目打包具体操作的时候,此类的路径的匹配操作一定会在项目中出现,尤其以 dubbo 为主。