为了快速的入门gradle,本章将从整体构建脚本的角度介绍:
- 什么是 setting 文件,它有什么作用;
- 说明什么是build 文件,它又有什么作用
- 我们可以创建多少个 build
- project 和task,他们有什么作用;又是什么关系,如何配置
- 项目的生命周期
- 项目发布
- 使用Gradle 创建SpringBoot 项目等
1、项目的生命周期
Gradle 项目的生命周期分为三大阶段: Initialization -> Configuration -> Execution. 每个阶段都有自己的职责,具体如下图所示:
Initialization 阶段
- 主要目的是初始化构建, 它又分为两个子过程,一个是执行 Init Script,另一个是执行 Setting Script。
- init.gradle 文件会在每个项目 build 之前被调用,用于做一些初始化的操作,它主要有如下作用:
- 配置内部的仓库信息(如公司的 maven 仓库信息);
- 配置一些全局属性;
- 配置用户名及密码信息(如公司仓库的用户名和密码信息)。
- Setting Script 则更重要, 它初始化了一次构建所参与的所有模块。
Configuration 阶段:
这个阶段开始加载项目中所有模块的 Build Script。所谓 “加载” 就是执行 build.gradle 中的语句, 根据脚本代码创建对应的 task, 最终根据所有 task 生成由 Task 组成的有向无环图(Directed Acyclic Graphs),如下:
从而构成如下有向无环树:
Execution 阶段:
这个阶段会根据上个阶段构建好的有向无环图,按着顺序执行 Task【Action 动作】。
2、settings 文件
首先对 settings 文件的几点说明:
1、作用:主要是在项目初始化阶段确定一下引入哪些工程需要加入到项目构建中,为构建项目工程树做准备。
2、工程树:gradle 中有工程树的概念,类似于 maven 中的project 与module。
3、内容:里面主要定义了当前 gradle 项目及子 project 的项目名称
4、位置:必须放在根工程目录下。
5、名字:为settings.gradle
文件,不能发生变化
6、对应实例:与 org.gradle.api.initialization.Settings 实例是一一对应的关系。每个项目只有一个settings 文件。
7、关注:作为开发者我们只需要关注该文件中的include 方法即可。使用相对路径【 : 】引入子工程。
8.一个子工程只有在setting 文件中配置了才会被 gradle 识别,这样在构建的时候才会被包含进去。案例如下所示:
//根工程项目名
rootProject.name = 'root'
//包含的子工程名称
include 'subject01'
include 'subject02'
include 'subject03'
//包含的子工程下的子工程名称
include 'subject01:subproject011'
include 'subject01:subproject012'
项目名称中 “:” 代表项目的分隔符, 类似路径中的 “/”. 如果以 “:” 开头则表示相对于 root project 。然后 Gradle 会为每个带有 build.gradle 脚本文件的工程构建一个与之对应的 Project 对象。
我的代码
settings.gradle
rootProject.name = 'groovy-root'
include 'groovy-subproject01'
include 'groovy-subproject02'
include 'groovy-subproject03'
3、Task
项目实质上是 Task 对象的集合。一个 Task 表示一个逻辑上较为独立的执行过程,比如编译Java 源代码,拷贝文件, 打包Jar 文件,甚至可以是执行一个系统命令。另外,一个 Task 可以读取和设置Project 的Property 以完成特定的操作。
1、任务入门
可参考官方文档:https://docs.gradle.org/current/userguide/tutorial_using_tasks.html
让我们来先看一个例子:
task A {
println "root taskA"
doFirst(){
println "root taskA doFirst"
}
doLast(){
println "root taskA doLast"
}
}
在文件所在的目录执行命令: gradle A。
提示 1 :task 的配置段是在配置阶段完成
提示 2 :task 的doFirst、doLast 方法是执行阶段完成,并且doFirst 在doLast 执行之前执行。
提示 3:区分任务的配置段和任务的行为,任务的配置段在配置阶段执行,任务的行为在执行阶段执行
2、任务的行为
案例如下:doFirst、doLast 两个方法可以在任务内部定义,也可以在任务外部定义
def map=new HashMap<String,Object>();
//action属性可以设置为闭包,设置task自身的行为
map.put("action",{println "taskD.."})
task(map,"a"){
description 'taskA description '
group "atguigu"
//在task内部定义doFirst、doLast行为
doFirst {
def name = 'doFirst..'
println name
}
doLast {
def name = 'doLast..'
println name
}
}
//在task外部定义doFirst、doLast行为
a.doFirst {
println it.description
}
a.doLast {
println it.group
}
测试:gradle a , 输出如下所示:
底层原理分析:无论是定义任务自身的 action,还是添加的doLast、doFirst 方法,其实底层都被放入到一个Action 的List 中了,最初这个 action List 是空的,当我们设置了 action【任务自身的行为】,它先将action 添加到列表中,此时列表中只有一个action,后续执行doFirst 的时候doFirst 在action 前面添加,执行 doLast 的时候doLast 在action 后面添加。doFirst 永远添加在actions List 的第一位,保证添加的Action 在现有的 action List 元素的最前面;doLast 永远都是在action List 末尾添加,保证其添加的Action 在现有的action List 元素的最后面。一个往前面添加,一个往后面添加,最后这个action List 就按顺序形成了doFirst、doSelf、doLast 三部分的 Actions,就达到 doFirst、doSelf、doLast 三部分的 Actions 顺序执行的目的。
提示 1:其中<<代表doLast,在gradle5.x 版本之后就废弃,不能使用了,如下所示:
task hello << {
println 'Hello world!'
}
我的代码
build.gradle
plugins {
id 'java'
}
group = 'top.qaqaq.groovy'
version = '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
testImplementation platform('org.junit:junit-bom:5.10.0')
testImplementation 'org.junit.jupiter:junit-jupiter'
}
test {
useJUnitPlatform()
}
def map = new HashMap<String, Object> ();
// action属性可以设置为闭包,设置task自身的行为
map.put("action", {println "task one.."})
task (map, "task1") {
// 任务的配置段: 在配置阶段执行
println "这是最简单的任务"
// 任务的行为: 在执行阶段执行,doFirst会在doLast执行之前执行
doFirst {
println "task1 doFirst"
}
doLast {
println "task1 doLast"
}
}
task1.doFirst {
println "task1 doFirst"
}
task1.doLast {
println "task1 doLast"
}
3、任务的依赖方式
Task 之间的依赖关系可以在以下几部分设置:
- 参数依赖
- 内部依赖
- 外部依赖
方式一:参数方式依赖
task A {
doLast {
println "TaskA.."
}
}
task 'B' {
doLast {
println "TaskB.."
}
}
//参数方式依赖: dependsOn后面用冒号
task 'C'(dependsOn: ['A', 'B']) {
doLast {
println "TaskC.."
}
}
方式二:内部依赖
task 'C' {
//内部依赖:dependsOn后面用 = 号
dependsOn= [A,B]
doLast {
println "TaskC.."
}
}
测试: gradle C
方式三:外部依赖
//外部依赖:可变参数,引号可加可不加
C.dependsOn(B,'A')
当然:task 也支持跨项目依赖
在subproject01 工程的 build.gradle 文件中定义:
task A {
doLast {
println "TaskA.."
}
}
在subproject02 工程的 build.gradle 文件中定义:
task B{
dependsOn(":subproject01:A") //依赖根工程下的subject01中的任务A :跨项目依赖。
doLast {
println "TaskB.."
}
}
测试:gradle B ,控制台显示如下
拓展 1:当一个 Task 依赖多个Task 的时候,被依赖的Task 之间如果没有依赖关系,那么它们的执行顺序是随机的,并无影响。
拓展 2:重复依赖的任务只会执行一次,比如:
A->B、C
B->C
任务A 依赖任务 B 和任务 C、任务 B 依赖C 任务。执行任务A 的时候,显然任务C 被重复依赖了,C 只会执行一次。
我的代码
父工程-父工程单独测试
build.gradle
plugins {
id 'java'
}
group = 'top.qaqaq.groovy'
version = '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
testImplementation platform('org.junit:junit-bom:5.10.0')
testImplementation 'org.junit.jupiter:junit-jupiter'
}
test {
useJUnitPlatform()
}
//def map = new HashMap<String, Object> ();
//// action属性可以设置为闭包,设置task自身的行为
//map.put("action", {println "task one.."})
//
//task (map, "task1") {
// // 任务的配置段: 在配置阶段执行
// println "这是最简单的任务"
// // 任务的行为: 在执行阶段执行,doFirst会在doLast执行之前执行
// doFirst {
// println "task1 doFirst"
// }
// doLast {
// println "task1 doLast"
// }
//}
//
//task1.doFirst {
// println "task1 doFirst"
//}
//task1.doLast {
// println "task1 doLast"
//}
task A {
doLast {
println "TaskA.."
}
}
task 'B' {
doLast {
println "TaskB.."
}
}
////参数方式依赖: dependsOn后面用冒号
//task 'C'(dependsOn: ['A', 'B']) {
// doLast {
// println "TaskC.."
// }
//}
//task 'C' (){
// //内部依赖:dependsOn后面用 = 号
// dependsOn= ['A','B']
// doLast {
// println "TaskC.."
// }
//}
task 'C' (){
doLast {
println "TaskC.."
}
}
//外部依赖
C.dependsOn=['A', 'B']
子工程-groovy-subproject01
build.gradle
plugins {
id 'java'
}
group = 'top.qaqaq.groovy'
version = '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
testImplementation platform('org.junit:junit-bom:5.10.0')
testImplementation 'org.junit.jupiter:junit-jupiter'
}
test {
useJUnitPlatform()
}
task A {
doLast {
println "TaskA.."
}
}
子工程-groovy-subproject02,执行子工程-groovy-subproject02,会调用子工程-groovy-subproject01
build.gradle
plugins {
id 'java'
}
group = 'top.qaqaq.groovy'
version = '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
testImplementation platform('org.junit:junit-bom:5.10.0')
testImplementation 'org.junit.jupiter:junit-jupiter'
}
test {
useJUnitPlatform()
}
task B{
dependsOn(":groovy-subproject01:A") //依赖根工程下的subject01中的任务A :跨项目依赖。
doLast {
println "TaskB.."
}
}
4、任务执行
任务执行语法:gradle [taskName…] [–option-name…]。
分类 | 解释 |
常见的任务(*) | gradle build : 构建项目:编译、测试、打包等操作gradle run :运行一个服务,需要application 插件支持,并且指定了主启动类才能运行gradle clean : 请求当前项目的 build 目录gradle init : 初始化 gradle 项目使用gradle wrapper :生成wrapper 文件夹的。gradle wrapper 升级wrapper 版本号: gradle wrapper --gradle-version=4.4 gradle wrapper --gradle-version 5.2.1 --distribution-type all :关联源码用 |
项目报告相关任务 | gradle projects : 列出所选项目及子项目列表,以层次结构的形式显示gradle tasks : 列出所选项目【当前 project,不包含父、子】的已分配给任务组的那些任务。gradle tasks --all :列出所选项目的所有任务。gradle tasks --group="build setup" :列出所选项目中指定分组中的任务。gradle help --task someTask :显示某个任务的详细信息gradle dependencies :查看整个项目的依赖信息,以依赖树的方式显示gradle properties 列出所选项目的属性列表 |
调试相关选项 | -h,–help: 查看帮助信息 -v, –version:打印 Gradle、 Groovy、 Ant、 JVM 和操作系统版本信息。 -S, –full-stacktrace:打印出所有异常的完整(非常详细)堆栈跟踪信息。 -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] :执行自定义任务 |
… |
更 详 细 请 参 考 官 方 文 档 : https://docs.gradle.org/current/userguide/command_line_interface.html#sec:command_line_executing_tasks
拓展:gradle 任务名是缩写: 任务名支持驼峰式命名风格的任务名缩写,如:connectTask 简写为:cT,执行任务 gradle cT。
拓展 1:前面提到的Gradle 指令本质:一个个的task[任务], Gradle 中所有操作都是基于任务完成的。
拓展 2:gradle 默认各指令之间相互的依赖关系:
相关解释:
我的代码
gradle run
:运行一个服务,需要application 插件支持,并且指定了主启动类才能运行
build.gradle中添加如下代码
plugins {
id 'java'
id 'application'
}
mainClassName='top/qaqaq/groovy/MainClass'
//字符编码,不是必须
tasks.withType(JavaCompile){
options.encoding="UTF-8"
}
gradle projects
: 列出所选项目及子项目列表,以层次结构的形式显示
build.gradle中添加如下代码
task 'B' {
group "abc" //给任务指定分组
doLast {
println "TaskB.."
}
}
5、任务定义方式
任务定义方式,总体分为两大类:一种是通过 Project 中的task()方法,另一种是通过tasks 对象的 create 或者register 方法。
task('A',{//任务名称,闭包都作为参数
println "taskA..."
})
task('B'){//闭包作为最后一个参数可以直接从括号中拿出来
println "taskB..."
}
task C{//groovy语法支持省略方法括号:上面三种本质是一种
println "taskC..."
}
def map=new HashMap<String,Object>();
map.put("action",{println "taskD.."}) //action属性可以设置为闭包
task(map,"D");
tasks.create('E'){//使用tasks的create方法
println "taskE.."
}
tasks.register('f'){ //注:register执行的是延迟创建。也即只有当task被需要使用的时候才会被创建。
println "taskF...."
}
当然:我们也可以在定义任务的同时指定任务的属性,具体属性有:
在定义任务时也可以给任务分配属性:定义任务的时候可以直接指定任务属性,也可以给已有的任务动态分配属性:
//①.F是任务名,前面通过具名参数给map的属性赋值,以参数方式指定任务的属性信息
task(group: "atguigu",description: "this is task B","F")
//②.H是任务名,定义任务的同时,在内部直接指定属性信息
task("H") {
group("atguigu")
description("this is the task H")
}
//③.Y是任务名,给已有的任务 在外部直接指定属性信息
task "y"{}
y.group="atguigu"
clean.group("atguigu") //案例:给已有的clean任务重新指定组信息
可以在 idea 中看到: 上面自定义的那几个任务和 gradle 自带的 clean 任务已经跑到:atguigu 组了。
6、任务类型
前面我们定义的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 | 组装 ZIP 归档文件。默认是压缩 ZIP 的内容。 |
提示 1:如果想看更详细的gradle 自带Task 类型,请参考官方文档: https://docs.gradle.org/current/dsl/index.html
提示 2: 官方文档在给出这些任务类型的时候,同时给出了案例代码,可以点进去上述官网地址中的某个类型中观看
具体使用例如:
tasks.register('myClean', Delete) {
delete buildDir
}
在命令行执行 gradle myClean 发现就可以将当前project 的 build 目录删除
当然除了gradle 自带的 task 类型,我们也可以自定义task 类型,如下所示:
拓展 4:自定义 Task 类型
def myTask=task MyDefinitionTask (type: CustomTask)
myTask.doFirst(){
println "task 执行之前 执行的 doFirst方法"
}
myTask.doLast(){
println "task 执行之后 执行的 doLast方法"
}
class CustomTask extends DefaultTask {
//@TaskAction表示Task本身要执行的方法
@TaskAction
def doSelf(){
println "Task 自身 在执行的in doSelf"
}
}
测试:gradle MyDefinitionTask
控制台输出:
我的代码
build.gradle
class CustomTask extends DefaultTask {
//@TaskAction表示Task本身要执行的方法
@TaskAction
def doSelf(){
println "Task 自身 在执行的in doSelf"
}
}
def myTask=task MyDefinitionTask (type: CustomTask)
myTask.doFirst(){
println "task 执行之前 执行的 doFirst方法"
}
myTask.doLast(){
println "task 执行之后 执行的 doLast方法"
}
7、任务的执行顺序
在 Gradle 中,有三种方式可以指定 Task 执行顺序:
1、dependsOn 强依赖方式
2、通过 Task 输入输出
3、通过 API 指定执行顺序
详细请参考官网:https://docs.gradle.org/current/dsl/org.gradle.api.Task.html
8、动态分配任务
gradle 的强大功能不仅仅用于定义任务的功能。例如,可以使用它在循环中注册同一类型的多个任务
4.times { counter ->
tasks.register("task$counter") {
doLast {
println "I'm task number $counter"
}
}
}
一旦注册了任务,就可以通过 API 访问它们。例如,您可以使用它在运行时动态地向任务添加依赖项。Ant 不允许这样的事情发生。
4.times { counter ->
tasks.register("task$counter") {
doLast {
println "I'm task number $counter"
}
}
}
tasks.named('task0') { dependsOn('task2', 'task3') }
构建 4 个任务,但是任务 0 必须依赖于任务 2 和 3,那么代表任务 2 和 3 需要在任务 0 之前优先加载。具体测试如下:
9、任务的关闭与开启
每个任务都有一个 enabled 默认为的标志 true。将其设置为 false 阻止执行任何任务动作。禁用的任务将标记为“跳过”。
task disableMe {
doLast {
println 'This task is Executing...'
}
enabled(true)//直接设置任务开启,默认值为true
}
//disableMe.enabled = false //设置关闭任务
我的代码(7、8、9)
build.gradle
4.times { counter ->
tasks.register("task$counter") {
doLast {
println "I'm task number $counter"
}
}
}
task2.enabled=false
tasks.named('task0') { dependsOn('task2', 'task3') }
10、任务的超时
每个任务都有一个 timeout 可用于限制其执行时间的属性。当任务达到超时时,其任务执行线程将被中断。该任务将被标记为失败。终结器任务仍将运行。如果 –continue 使用,其他任务可以在此之后继续运行。不响应中断的任务无法超时。Gradle 的所有内置任务均会及时响应超时
task a() {
doLast {
Thread.sleep(1000)
println "当前任务a执行了"
}
timeout = Duration.ofMillis(500)
}
task b() {
doLast {
println "当前任务b执行了"
}
}
在控制台使用: gradle a b 测试会发现执行a 的时候,由于a 执行超时,抛出异常,所以没有继续往下执行【b 也没执行】。然后在控制台使用: gradle a b –continue,测试会发现a 虽然执行失败,但是 b 还是执行了。
11、任务的查找
常用的任务查找方法有:
task atguigu {
doLast {
println "让天下没有难学的技术:尚硅谷"
}
}
//根据任务名查找
tasks.findByName("atguigu").doFirst({println "尚硅谷校区1:北京...."})
tasks.getByName("atguigu").doFirst({println "尚硅谷校区2:深圳...."})
//根据任务路径查找【相对路径】
tasks.findByPath(":atguigu").doFirst({println "尚硅谷校区3:上海...."})
tasks.getByPath(":atguigu").doFirst({println "尚硅谷校区4:武汉...."})
执行task: gradle atguigu,输出结果如下所示:
尚硅谷校区4:武汉….
尚硅谷校区3:上海….
尚硅谷校区2:深圳….
尚硅谷校区1:北京….
让天下没有难学的技术:尚硅谷
如果将如下代码放到子工程会报错
//根据任务名查找
tasks.findByName("atguigu").doFirst({println "尚硅谷校区1:北京...."})
tasks.getByName("atguigu").doFirst({println "尚硅谷校区2:深圳...."})
因为如上代码是根据任务名进行查找的
如果将如下代码放到子工程则执行成功
//根据任务路径查找【相对路径】
tasks.findByPath(":atguigu").doFirst({println "尚硅谷校区3:上海...."})
tasks.getByPath(":atguigu").doFirst({println "尚硅谷校区4:武汉...."})
因为如上代码是根据相对路径查找的,所以能找到正确的路径
我的代码
父工程的build.gradle
task atguigu {
doLast {
println "让天下没有难学的技术:尚硅谷"
}
}
//根据任务名查找
tasks.findByName("atguigu").doFirst({println "尚硅谷校区1:北京...."})
tasks.getByName("atguigu").doFirst({println "尚硅谷校区2:深圳...."})
//根据任务路径查找【相对路径】
tasks.findByPath(":atguigu").doFirst({println "尚硅谷校区3:上海...."})
tasks.getByPath(":atguigu").doFirst({println "尚硅谷校区4:武汉...."})
子工程的build.gradle
//根据任务名查找--报错,找的是子工程的atguigu任务
//tasks.findByName("atguigu").doFirst({println "尚硅谷校区1:北京...."})
//tasks.getByName("atguigu").doFirst({println "尚硅谷校区2:深圳...."})
//根据任务路径查找【相对路径】--找的是父工程的atguigu任务
tasks.findByPath(":atguigu").doFirst({println "尚硅谷校区3:上海...."})
tasks.getByPath(":atguigu").doFirst({println "尚硅谷校区4:武汉...."})
12、任务的规则
当我们执行、依赖一个不存在的任务时,Gradle 会执行失败,报错误信息。那我们能否对其进行改进,当执行一个不存在的任务时,不是报错而是打印提示信息呢?
task hello {
doLast {
println 'hello 尚硅谷的粉丝们'
}
}
tasks.addRule("对该规则的一个描述,便于调试、查看等"){
String taskName -> task(taskName) {
doLast {
println "该${taskName}任务不存在,请查证后再执行"
}
}
}
测试: 使用 gradle abc hello 进行测试,此时当 abc 任务不存在时,也不会报异常【不中断执行】而是提示自定义的规则信息,继续执行 hello 任务。此外,它还可以根据不同的规则动态创建需要的任务等情况。
我的代码
build.gradle
task hello {
doLast {
println 'hello 尚硅谷的粉丝们'
}
}
tasks.addRule("我的网站,qaqaq.top"){ //对任务规则的一些说明信息
String taskName -> task(taskName) {
println "the $taskName is not exist"
//可以写其它的业务逻辑
}
}
13、任务的 onlyIf 断言
断言就是一个条件表达式。Task 有一个 onlyIf 方法。它接受一个闭包作为参数,如果该闭包返回 true 则该任务执行, 否则跳过。这有很多用途,比如控制程序哪些情况下打什么包,什么时候执行单元测试,什么情况下执行单元测试的时候不执行网络测试等。具体案例如下所示:
task hello {
doLast {
println 'hello 尚硅谷的粉丝们'
}
}
hello.onlyIf { !project.hasProperty('fensi') }
测试:通过-P 为Project 添加fensi 属性
gradle hello -Pfensi
14、默认任务
Gradle 允许您定义一个或多个在没有指定其他任务时执行的默认任务。代码如下所示:
defaultTasks 'myClean', 'myRun'
tasks.register('myClean'){
doLast {
println 'Default Cleaning!'
}
}
tasks.register('myRun') {
doLast {
println 'Default Running!'
}
}
tasks.register('other') {
doLast {
println "I'm not a default task!"
}
}
测试及结果如下:
> gradle -q
Default Cleaning!
Default Running!
4、Gradle 中的文件操作
几种常见的文件操作方式:
- 本地文件
- 文件集合
- 文件树
- 文件拷贝
- 归档文件
各种文件操作类型的详细介绍如下所示:
1、本地文件
使用 Project.file(java.lang.Object)方法,通过指定文件的相对路径或绝对路径来对文件的操作,其中相对路径为相对当前project[根project 或者子project]的目录。其实使用 Project.file(java.lang.Object)方法创建的 File 对象就是 Java 中的 File 对象,我们可以使用它就像在 Java 中使用一样。示例代码如下:
//使用相对路径
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())
我的代码
build.gradle
//使用本地文件的操作方式:相对路径
File configFile=file('src/conf.xml');
configFile.createNewFile()
//使用本地文件的操作方式:绝对路径
configFile=file('D:\\Java\\Code\\study\\groovy-root/conf.xml');
configFile.createNewFile()
//使用本地文件的操作方式:new File
configFile=new File('conf.xml');
configFile.createNewFile()
2、文件集合
文 件 集 合 就 是 一 组 文 件 的 列 表 , 在 Gradle 中 , 文 件 集 合 用 FileCollection 接 口 表 示 。 我 们 可 以 使 用
Project.files(java.lang.Object[])方法来获得一个文件集合对象,如下代码创建一个 FileCollection 实例:
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 类型;同时还能使用+来添加一个集合,或使用-来删除集合。
我的代码
build.gradle
//方式2:文件集合方式
FileCollection fileCollection =files('src/text1.xml',['src/text2.xml','src/text3.xml'])
fileCollection.forEach { item->
println item.name
item.createNewFile()
}
//转换成Set集合
Set set =fileCollection.files; //方法一
for (final def i in set) {
println i.exists()
}
Set set =fileCollection as Set; //方法二
for (final def i in set) {
println i.exists()
}
//转换成List列表
List list =fileCollection as List;
for (final def i in list) {
println i.exists()
}
def union = fileCollection + files('src/text4.xml') // 添加或者删除一个集合
def minus = fileCollection - files('src/text3.xml')
union.forEach(){File it ->
println it.name
}
minus.forEach(){File it ->
println it.name
}
3、文件树
文件树是有层级结构的文件集合,一个文件树它可以代表一个目录结构或一 ZIP 压缩包中的内容结构。文件树是从文件集合继承过来的,所以文件树具有文件集合所有的功能。我们可以使用Project.fileTree(java.util.Map)方法来创建文件树对象, 还可以使用过虑条件来包含或排除相关文件。示例代码如下:
tree = fileTree('src/main').include('**/*.java')// 第一种方式:使用路径创建文件树对象,同时指定包含的文件
//第二种方式:通过闭包创建文件树:
tree = fileTree('src/main') {
include '**/*.java'
}
tree = fileTree(dir: 'src/main', include: '**/*.java') //第三种方式:通过路径和闭包创建文件树:具名参数给map传值
tree = fileTree(dir: 'src/main', includes: ['**/*.java', '**/*.xml', '**/*.txt'], exclude: '**/*test*/**')
tree.each {File file -> // 遍历文件树的所有文件
println file
println file.name
}
我的代码
build.gradle
//4.3、文件树
ConfigurableFileTree configurableFileTree = fileTree('src/main');
configurableFileTree.exclude("**/*.java").forEach { item->{
println item.name
}}
4、文件拷贝
我们可以使用 Copy 任务来拷贝文件,通过它可以过虑指定拷贝内容,还能对文件进行重命名操作等。Copy 任务必须指定一组需要拷贝的文件和拷贝到的目录,这里使用CopySpec.from(java.lang.Object[])方法指定原文件;使用CopySpec.into(java.lang.Object)方法指定目标目录。示例代码如下
task copyTask(type: Copy) {
from 'src/main/resources'
into 'build/config'
}
from()方法接受的参数和文件集合时files()一样。当参数为一个目录时,该目录下所有的文件都会被拷贝到指定目录下(目录自身不会被拷贝);当参数为一个文件时,该文件会被拷贝到指定目录;如果参数指定的文件不存在,就会被忽略; 当参数为一个 Zip 压缩文件,该压缩文件的内容会被拷贝到指定目录。
into()方法接受的参数与本地文件时 file()一样。 示例代码如下
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'
}
在拷贝文件的时候还可以添加过虑条件来指定包含或排除的文件,示例如下:
task copyTaskWithPatterns(type: Copy) {
from 'src/main/webapp'
into 'build/explodedWar'
include '**/*.html'
include '**/*.jsp'
exclude { details -> details.file.name.endsWith('.html') }
}
在拷贝文件的时候还可以对文件进行重命名操作,示例如下:
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()方法的使用方式:
task copyMethod {
doLast {
copy {
from 'src/main/webapp'
into 'build/explodedWar'
include '**/*.html'
include '**/*.jsp'
}
}
}
或者使用project 对象的copy 方法:
copy {
//相对路径或者绝对路径
from file('src/main/resources/ddd.txt') //file也可以换成new File()
into this.buildDir.absolutePath
}
执行gradle build 指令即可。去 build 目录的本地磁盘查看,就能看到。
我的代码
build.gradle
task copyTask(type: Copy) {
// 拷贝src/main/webapp目录下所有的文件
from 'src/main/webapp'
// 拷贝单独的一个文件
from 'src/staging/index.html'
// 从Zip压缩文件中拷贝内容
from zipTree('src/main/qaqaq.zip')
// 拷贝到的目标目录
into 'build/explodedWar'
}
task copyTaskWithPatterns(type: Copy) {
from 'src/main/'
// include('**/*.java')
exclude('**/*.java')
// 拷贝到的目标目录
into 'build/explodedWar'
}
task rename(type: Copy) {
from 'src/main/java/top/qaqaq/aa.txt'
rename { String fileName ->
fileName.replace('aa', 'qaqaq')
}
// 拷贝到的目标目录
into 'build/explodedWar'
}
copy {
//相对路径或者绝对路径
from file('src/main/resources/ddd.txt') //file也可以换成new File()
into this.buildDir.absolutePath
}
5、归档文件
通常一个项目会有很多的 Jar 包,我们希望把项目打包成一个 WAR,ZIP 或 TAR 包进行发布,这时我们就可以使用
Zip,Tar,Jar,War 和Ear 任务来实现,不过它们的用法都一样,所以在这里我只介绍Zip 任务的示例。首先,创建一个 Zip 压缩文件,并指定压缩文件名称,如下代码所示:
apply plugin: 'java'
version=1.0
task myZip(type: Zip) {
from 'src/main'
into 'build' //保存到build目录中
baseName = 'myGame'
}
println myZip.archiveName
执行命令gradle -q myZip,输出结果为:
> gradle -q myZip
myGame-1.0.zip
最后,我们可以使用 Project.zipTree(java.lang.Object)和 Project.tarTree(java.lang.Object)方法来创建访问 Zip 压缩包的文件树对象,示例代码如下:
// 使用zipTree
FileTree zip = zipTree('someFile.zip')
// 使用tarTree
FileTree tar = tarTree('someFile.tar')
在这里,我们介绍了 Gradle 对本地文件、文件集合、文件树、文件拷贝和归档文件的操作方式。更详细的请参考官方文档:https://docs.gradle.org/current/userguide/working_with_files.html
我的代码
build.gradle
task myZip(type: Zip) {
from 'src/main'
into 'build' //保存到build目录中
archiveBaseName = 'myGame'
}
5、Dependencies
在 Gradle 进阶说明-Dependencies 中进行撰写,请移步如下链接查看
Gradle 进阶说明-Dependencies – RichieZhang (qaqaq.top)