1.Gradle 入门
1.1 Gradle 简介
Gradle 是一款Google 推出的基于JVM
、通用灵活的项目构建工具
,支持Maven,JCenter 多种第三方仓库;支持传递性依赖管理、废弃了繁杂的xml 文件
,转而使用简洁
的、支持多种语言
(例如:java、groovy 等)的build 脚本文件。
官网地址: https://gradle.org/
1.2 Gradel 目录结构
1.3 Gradle 常用命令
1.4 Gradle 修改maven源方法
认识init.d 文件夹
我们可以在gradle 的init.d
目录下创建以.gradle
结尾的文件,.gradle
文件可以实现在build 开始之前
执行,所以你可以在这个文件配置一些你想预先加载
的操作。
在init.d 文件夹创建init.gradle 文件
allprojects {
repositories {
mavenLocal()
maven { name "Alibaba" ; url "https://maven.aliyun.com/repository/public" }
maven { name "Bstek" ; url "https://nexus.bsdn.org/content/groups/public/" }
mavenCentral()
}
buildscript {
repositories {
maven { name "Alibaba" ; url 'https://maven.aliyun.com/repository/public' }
maven { name "Bstek" ; url 'https://nexus.bsdn.org/content/groups/public/' }
maven { name "M2" ; url 'https://plugins.gradle.org/m2/' }
}
}
}
拓展1:启用init.gradle 文件的方法有:
1.在命令行指定文件,例如:gradle --init-script yourdir/init.gradle -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序号依次执行这些文件,如果给定目录下存在多个init脚本,会按拼音a-z顺序执行这些脚本,每个init脚本都存在一个对应的gradle实例,你在这个文件中调用的所有方法和属性,都会委托给这个gradle实例,每个init脚本都实现了Script接口。
拓展2:仓库地址说明
-
mavenLocal(): 指定使用maven本地仓库,而本地仓库在配置maven时settings文件指定的仓库位置。如E:/repository,gradle查找jar包顺序如下:USER_HOME/.m2/settings.xml >> M2_HOME/conf/settings.xml >> USER_HOME/.m2/repository
-
maven { url 地址},指定maven仓库,一般用私有仓库地址或其它的第三方库【比如阿里镜像仓库地址】。
-
mavenCentral():这是Maven的中央仓库,无需配置,直接声明就可以使用。
-
jcenter():JCenter中央仓库,实际也是是用的maven搭建的,但相比Maven仓库更友好,通过CDN分发,并且支持https访问,
在新版本中已经废弃了,替换为了mavenCentral()。
-
总之, gradle可以通过指定仓库地址为本地maven仓库地址和远程仓库地址相结合的方式,避免每次都会去远程仓库下载依赖库。这种方式也有一定的问题,如果本地maven仓库有这个依赖,就会从直接加载本地依赖,如果本地仓库没有该依赖,那么还是会从远程下载。但是下载的jar不是存储在本地maven仓库中,而是放在自己的缓存目录中,默认在USER_HOME/.gradle/caches目录,当然如果我们配置过GRADLE_USER_HOME环境变量,则会放在GRADLE_USER_HOME/caches目录,那么可不可以将gradle caches指向maven repository。我们说这是不行的,caches下载文件不是按照maven仓库中存放的方式。
拓展3:阿里云仓库地址请参考:https://developer.aliyun.com/mvn/guide
1.5 Wrapper 包装器介绍
Gradle Wrapper 实际上就是对Gradle 的一层包装,用于解决实际开发中可能会遇到的不同的项目需要不同版本的Gradle
问题。例如:把自己的代码共享给其他人使用,可能出现如下情况:
- 1.对方电脑没有安装gradle
- 2.对方电脑安装过gradle,但是版本太旧了
这时候,我们就可以考虑使用Gradle Wrapper 了。这也是官方建议使用Gradle Wrapper 的原因。实际上有了Gradle Wrapper 之后,我们本地是可以不配置Gradle 的,下载Gradle 项目后,使用gradle 项目自带的wrapper 操作也是可以的。
那如何使用Gradle Wrapper 呢?
- 项目中的gradlew、gradlew.cmd脚本用的就是wrapper中规定的gradle版本。参见源码
而我们上面提到的gradle指令用的是本地gradle,所以gradle指令和gradlew指令所使用的gradle版本有可能是不一样的。gradlew、gradlew.cmd的使用方式与gradle使用方式完全一致,只不过把gradle指令换成了gradlew指令。
当然,我们也可在终端执行gradlew 指令时,指定指定一些参数,来控制Wrapper 的生成,比如依赖的版本等,如下:
具体操作如下所示:
- gradle wrapper --gradle-version=4.4:升级wrapper版本号,只是修改gradle.properties中wrapper版本,未实际下载
- gradle wrapper --gradle-version 5.2.1 --distribution-type all :关联源码用
GradleWrapper 的执行流程:
- 1.当我们第一次执行./gradlew build 命令的时候,gradlew 会读取gradle-wrapper.properties 文件的配置信息
- 2.准确的将指定版本的gradle 下载并解压到指定的位置(GRADLE_USER_HOME目录下的wrapper/dists目录中)
- 3.并构建本地缓存(GRADLE_USER_HOME目录下的caches目录中),下载再使用相同版本的gradle就不用下载了
- 4.之后执行的./gradlew 所有命令都是使用指定的gradle 版本。如下图所示:
gradle-wrapper.properties 文件解读:
注意:前面提到的GRALE_USER_HOME
环境变量用于这里的Gradle Wrapper
下载的特定版本的gradle 存储目录。如果我们没有配置过GRALE_USER_HOME
环境变量,默认在当前用户家目录下的.gradle 文件夹
中。
那什么时候选择使用gradle wrapper、什么时候选择使用本地gradle?
- 下载别人的项目或者使用操作以前自己写的不同版本的gradle项目时:用Gradle wrapper,也即:gradlew什么时候使用本地gradle?新建一个项目时: 使用gradle指令即可。
1.6 Gradle 对测试支持
测试任务自动检测并执行测试源集中的所有单元测试。测试执行完成后会生成一个报告。支持JUnit 和TestNG 测试。
默认测试目录及标准输出
Junit 使用
Gradle 对于Junit4.x 支持
dependencies {
testImplementation group: 'junit' ,name: 'junit', version: '4.12'
}
test {
useJUnit()
}
Gradle 对于Junit5.x 版本支持
dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
}
test {
useJUnitPlatform()
}
注意:无论是Junt4.x 版本还是Junit5.x 版本,我们只需在build.gradle 目录下执行gradle test 指令,gradle 就会帮我们执行所有的加了@Test 注解的测试,并生成测试报告。
包含和排除特定测试
test {
enabled true
useJUnit()
include 'com/**'
exclude 'com/abc/**'
}
gradle 在junit 中的批量测试,可以设置包含或者排除某些特定测试。
2.Gradle 进阶
2.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.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 对象。
2.3 Task
项目实质上是Task 对象的集合。一个Task 表示一个逻辑上较为独立的执行过程,比如编译Java 源代码,拷贝文件,
打包Jar 文件,甚至可以是执行一个系统命令。另外,一个Task 可以读取和设置Project 的Property 以完成特定的操作。
2.3.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:区分任务的配置段和任务的行为,任务的配置段在
配置阶段
执行,任务的行为在执行阶段
执行
3.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 元素的最后面。一个往前面添加,一个往后面添加,最后这个actionList 就按顺序形成了doFirst、doSelf、doLast 三部分的Actions,就达到doFirst、doSelf、doLast 三部分的Actions 顺序执行的目的。
提示1:其中<<
代表doLast,在gradle5.x 版本之后就废弃,不能使用了,如下所示:
如下所示:
task hello << {
println 'Hello world!'
}
2.3.3 任务的依赖方式
Task 之间的依赖关系可以在以下几部分设置:
- 1.参数依赖
- 2.内部依赖
- 3.外部依赖
方式一:参数方式依赖
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.."
}
}
方式三:外部依赖
//外部依赖:可变参数,引号可加可不加
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.."
}
}
拓展1:当一个Task 依赖多个Task 的时候,被依赖的Task 之间如果没有依赖关系,那么它们的执行顺序是随机的,并无影响。
拓展2:重复依赖的任务只会执行一次,比如:
A->B、C
B->C
任务A 依赖任务B 和任务C、任务B 依赖C 任务。执行任务A 的时候,显然任务C 被重复依赖了,C 只会执行一次。
2.3.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] :执行自定义任务 |