依赖类型
[java]
compileOnly
runtimeOnly
implementation
testCompileOnly
testRuntimeOnly
testImplementation
[war]
providedCompile
[java-library]
api
compileOnlyApi
[Deprecated]
compile -> 裂变为两个 api(能完全替代compile) implementation
runtime -> runtimeOnly (在运行时会将该库添加到 build 的 output 中去)
provided -> compileOnlycompileOnly: 由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
compile: 编译范围依赖在所有的 classpath 中可用,同时它们也会被打包. 在gradle 7.0 已经移除
runtime: runtime 依赖在运行和测试系统的时候需要, 在编译的时候不需要, 比如mysql 驱动包。在 gradle 7.0 已经移除
api: java-library 插件提供支持, 这些依赖项可以传递性地导出给使用者, 用于编译时和运行时。取代老版本中被移除的 compile
compileOnlyApi: java-library 插件提供支持,在声明模块和使用者在编译时需要的依赖项, 但在运行时不需要See the documentation.
example:
compileOnly "org.projectlombok:lombok:1.18.22"
annotationProcessor "org.projectlombok:lombok:1.18.22"
testCompileOnly "org.projectlombok:lombok:1.18.22"
testAnnotationProcessor "org.projectlombok:lombok:1.18.22"
testImplementation "org.springframework.boot:spring-boot-starter-test:2.6.3"1 本地依赖
implementation files('../lib/DmDialect-for-hibernate5.3.jar', '../lib/DmJdbcDriver18.jar')
implementation fileTree(dir: '../lib/', includes: ['*.jar'], excludes: [''])
2 项目依赖 (必须在setting.gradle 中依赖)
implementation project(":subProject")
3 直接依赖
implementation group: 'org.apache.logging.log4j', name: 'log4j', version: '2.17.2'- 以下都是对于当前项目而言
implement代表作为代码成为自己的一部分api代表作为jar包成为自己的一部分, 别人能筛出来
(如果有冲突会中断并提示)
configurations.all() { Configuration configuration ->
configuration.resolutionStrategy.failOnVersionConflict()
}(api 和 implementation 的区别: 对本模块没区别, 对引用的模块, api会传递具体的依赖坐标, implement 则不会知道子模块的依赖了哪些坐标)
dependencies {
implementation 'org.slf4j:slf4j-api:1.4.0!!' # 强制制定 to enforce draw version
api 'org.slf4j:slf4j-api:+' # 动态版本声明 会遍历所有的仓库, 最后拿出最新的版本
api 'org.slf4j:slf4j-api:latest.integeration' # 动态版本声明
}groovy 语法

- 单引号纯文本、双引号可加入$变量、三引号可以换行
- 类型转换:当需要时,类型之间会自动发生类型转换: 字符串(String)、基本类型(如int) 和类型的包装类 (如Integer)
- 类说明:如果在一个groovy 文件中没有任何类定义,它将被当做 script 来处理,也就意味着这个文件将被透明的转换为一个 Script 类型的类,1. 这个自动转换得到的类将使用原始的 groovy 文件名作为类的名字。2. groovy 文件的脚本内容被打包进 run 方法,另外在新产生的类中被加入一个 main 方法以进行外部执行该脚本
- script => extends Script 类型的类, script 可以包含类, 但不能有文件名相同的类定义
- class => implements GroovyObject
- 后面还会用到一个关键接口
Plugin<Project>用来 apply 脚本插件 - Groovy 类与 Java 类之间的主要区别是:
- 没有可见性修饰符的类或方法自动是公共的(可以使用一个特殊的注释来实现包的私有可见性)。
- 没有可见性修饰符的字段将自动转换为属性,不需要显式的 getter 和 setter 方法。
- 如果属性声明为 final,则不会生成 setter。
- 一个源文件可能包含一个或多个类 (但是如果一个文件不包含类定义的代码,则将其视为脚本)。脚本只是具有一些特殊约定的类,它们的名称与源文件相同(所以不要在脚本中包含与脚本源文件名相同的类定义)。
// Book.groovy
def num1=1;
def num2= 2;
// 不引起歧义的地方, 可以不加花括号
println "$num1 + $num2 = ${num1+num2}"
class Book {... // 上面有脚本了, 就不可以定义同文件名的类; ps: 默认就是 public 访问修饰符Groovy 的闭包 (关键语法)
- 定义: 类似于 lambda 表达式, 传递时使用匿名内部类对象的方式
- 特点: 可以先声明,然后设置代理来执行
Closure closure = {
sayHello()
}
// 测试准备
class Foo {
void sayHello() {
println("Hello!!!")
}
}
def foo = new Foo()
// 开始测试, 可以看出 delegate 是一个成员变量, 而且类型是匿名的接口
closure.delegate = foo
closure()- 调用特性:
- 可以外置 (闭包对象在最后一个参数时)
- 可以外置 + 省略括号 (仅有一个参数且为闭包时)
def running(Closure closure) {
println("start~")
closure()
println("end.")
}
running({ println("running...") })
// 闭包可以后置
running() {println("running...")}
def caculate(Closure closure) {
def num1 = 10
def num2 = 15
closure(num1, num2)
}
caculate() {k,v -> println("$k + $v = ${k+v}")}
// 括号可以省略, 也就是最熟悉的 Gradle 方式
caculate {k,v -> println("$k + $v = ${k+v}") }Gradle 的构建流程

- 生成一个
Settings对象,执行settings.gradle对这个对象进行配置 - 使用
Settings对象生成工程结构,创建Project对象 - 对所有
Project执行对应的build.gradle进行配置
脚本文件 build.gradle 的执行方式
总结一下,build.gradle 就是一个 Groovy 格式的脚本文件, 构建的时候会顺序执行, 但是打开 build.gradle 可能还是一头雾水, 一个个字符和大括号?以最长使用的 dependencies 举例
dependencies {
// This dependency is found on compile classpath of this component and consumers.
implementation 'com.google.guava:guava:26.0-jre'
// Use JUnit test framework
testImplementation 'junit:junit:4.12'
}同时 implementation 也可以这样写
implementation('com.google.guava:guava:26.0-jre')implementation 其实就是函数. Groovy 中函数调用可以使用空格加参数的形式调用
void foo(String params1, int param2) {
println("param1 = $params1, param2 = $param2")
}
foo "hello", 996Gradle 和 Groovy 的关系
Gradle 支持 Kotlin 或 Groovy 两种语言编写, 两者的关系为实现关系. Groovy 是 Gradle 的一种 DSL ( Domain Specific Language i.e., 领域专用语言)
Gradle 的 DSL 专门用于配置项目的构建不能用于其他工作. 语法还是 Groovy 自身的语法, 但调用只能依靠 Gradle 规定的 API 方式或者特定的一套规范. 强调一个 Specific 专用 (约定)
与 DSL (HTML、Makefile、LaTeX、AWK) 相对应的是可以做任何工作的通用语言 (Java 、C/C++)
gradle 命令
命令task
gradle tasks
gradle build
gradle run
gradle tasks --all :列出所选项目的所有任务。
gradle tasks --group="build setup":列出所选项目中指定分组中的任务
gradle dependencies :查看整个项目的依赖信息,以依赖树的方式显示
gradle properties 列出所选项目的属性列表命令参数
--max-workers: 设置 Gradle 可以使用的woker 数。默认值是处理器数
--parallel, --no-parallel: 并行执行项目。有关此选项的限制,请参阅并行项目执行。默认设置为关闭(off)
-Dorg.gradle.logging.level=(quiet,warn,lifecycle,info,debug):
通过 Gradle 属性设置日志记录级别。
-q, --quiet: 只能记录错误信息
-w, --warn: 设置日志级别为 warn
-i, --info: 将日志级别设置为 info
-d, --debug:登录调试模式 (包括正常的堆栈跟踪)
-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]: 执行自定义任务, 任务名支持驼峰式风格的任务名缩写, 如: connectTask 简写为: cT 执行 gradle cTTask 任务
任务定义
task C {
task(map,"D");
tasks.create('E') { // 使用tasks的create方法
tasks.register('f') { // register 执行的是延迟创建闭包里面可以指定任务属性: type (DefaultTask) overwrite (is cover exist?) dependsOn action description group
task(group: "abc", description: "this is task B", "F") // in front two is map, last is name
task("H") { group("abc") description("this is the task H") } // internal
task "y" {} // external
y.group="abc"
clean.group("abc") // case: 给已有的 clean 任务重新指定分组利用已有类型
tasks.register('myClean', Delete) {
delete buildDir
}
task copyTask(type: Copy) {
from 'src/main/webapp' // 拷贝src/main/webapp目录下所有的文件
from 'src/staging/index.html' // 拷贝单独的一个文件
from zipTree('src/main/assets.zip') // 从Zip压缩文件中拷贝内容
from tarTree('someFile.tar')
include '**/*.html'
exclude { details -> .endsWith('.html') }
rename { String fileName -> fileName.replace('-staging-', '') }
into 'build/explodedWar' // 拷贝到的目标目录
}利用自己写的类型:
MyXXX extends DefaultTask { // add @TaskAction annotation can add gradle action
def myTask = task MyDefinitionTask(type: CustomTask)groovy 循环 counter is 1, 2, 3, 4
4.times {
counter -> tasks.register("task$counter") {
doLast {
println "I'm task number $counter"
}
}
}tasks 方法
// 根据任务名查找
tasks.findByName("abc").doFirst({ ... })
tasks.getByName("abc")
// 根据任务路径查找【相对路径】
tasks.findByPath(":abc")
tasks.getByPath(":abc")
// 任务规则
tasks.addRule("对该规则的一个描述,便于调试、查看等") {
String taskName -> task(taskName) {
doLast { print "$taskName" }
}
}
// 任务规则-断言
taskName.onlyIf { !project.hasProperty('abc') } // true 则该任务执行, 否则跳过
// 项目默认执行 task
defaultTasks 'myClean', 'myRun'扩展 Task 任务
引用插件带来的 Task
bootJar {
mainClass = 'org.spring.boot.App'
}
// 实际继承 Task, 本身就是 Task
// public interface BootArchive extends Task {
// @Input
// Property<String> getMainClass();@SpringBootTest 内的 @Test 失效 (报错信息: > No tests found for given includes: [xxxx])
加入下面内容解决:
test { // tasks.test {
useJUnitPlatform()
}
// public class Test extends AbstractTestTask implements JavaForkOptions, PatternFilterable {文件集合
def collection = files('src/test1.txt',new File('src/test2.txt'),['src/test3.txt', 'src/test4.txt'])
Set set1 = collection.files // 把文件集合转换为java中的Set类型
Set set2 = collection as Set
List list = collection as List// 把文件集合转换为java中的List类型文件树
由文件集合继承过来, 具有文件集合所有的功能
tree = fileTree('src/main').include('**/*.java')
tree = fileTree('src/main') { include '**/*.java' // 闭包
tree = fileTree(dir: 'src/main', includes: ['**/*.java', '**/*.xml', '**/*.txt'], exclude: '**/*test*/**') // 具名参数传值给mapGradle 中的依赖分别为直接依赖,项目依赖,本地jar 依赖
项目依赖: implementation project(‘:subject01’)
文件依赖: implementation fileTree(dir: ‘libs’, include: [‘*.jar’])
直接依赖: 版本可以++, api 相当于把子build里面的依赖复制了一份
依赖排除
dependencies {
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' // 排除之后,使用手动的引入即可
}不允许依赖传递
dependencies {
implementation('org.hibernate:hibernate-core:3.6.3.Final'){
transitive(false) // 不允许依赖传递,一般不用
}
}强制使用某个版本
implementation('org.slf4j:slf4j-api:1.4.0!!')我们可以先查看当前项目中到底有哪些依赖冲突:
configurations.all() { // 下面我们配置,当 Gradle 构建遇到依赖冲突时,就立即构建失败
Configuration configuration -> // 当遇到版本冲突时直接构建失败
configuration.resolutionStrategy.failOnVersionConflict()
}gradle 插件
Why: 代码重用、减少功能类似代码编写; 高程度的模块化、自动化; 可插拔式的的扩展
How: 添加task、添加依赖配置、拓展属性和方法、对项目进行一些约定(Java插件约定目录结构)
分类: 脚本插件和
:gradle/gradle-wrapper.properties 配置
a daemon i a computer program that runs as a background prcess, rather than being under the direct control of an interactive user.
— Wikipedia
Gradle runs on the java virtual machine and uses several supporting libraries that require a non-trivial initialization time. As a result, it can sometimes seem a little slow to start. the solution to this problem is the gradle daemon
ext
It’s the overriding of get() and set() by ExtraPropertiesExtension that is the key to making the configuration syntax for never before defined properties work.
ext is shorthand for project.ext, and is used to define extra properties for the project object. (It’s also possible to define extra properties for many other objects.) When reading an extra property, the ext. is omitted (e.g. println project.springVersion or println springVersion). The same works from within methods. It does not make sense to declare a method named ext.
apply
means Project.apply(). apply是Gradle project中提供的方法,用于 配置项目中的插件
void apply(Map<String, ?> var1);apply plugin: 'org.springframework.boot'
apply from: './repository.gradle'gradle 脚本
gradle 脚本分为
- 脚本插件 (apply from)
- 二进制插件 (apply plugin)
- 脚本插件: 模块化基础 (库版本号管理、构建版本管理)
- 定义脚本 common.gradle (ext {…})
- 引入脚本 (apply from: ‘./common.gradle’)
- 二进制插件分为:
- 内部插件 (自带, 又叫核心插件, 实现 org.gradle.api.Plugin 接口, 都有id)
- 第三方插件
- 自定义插件 (构建过程中默认执行)
内部插件和第三方插件
- Differences: 内部插件 vs 第三方插件
- 第三方插件需要先引入依赖:
classpath, 再apply
- Similarities:
- 支持 plugins DSL 的方式 (
plugins { id: 'java'})
groovy 支持 DSL(Domain Specific Languages 领域特定语言)和其它简洁的语法
plugins { id 'org.springframework.boot' version '2.4.1' }
- 支持 apply plugin 方式(闭包 和 map 两种方式)
- 3 种写法
//使用方式1:Map具名参数,全类名
apply plugin:org.gradle.api.plugins.JavaPlugin
//使用方式2 org.gradle.api.plugins默认导入, 所以可以使用简写
apply plugin:JavaPlugin
//使用方式3:插件的id 核心插件,无需事先引入
apply plugin: 'java'- 第三方插件引入的传统方式 buildscript , 必须写在
build.gradle文件的最前端
// 使用传统的应用方式 buildscript 必须写在最前面
// 这是因为脚本从上到下执行, 当遇到需要的class没有时, 会去classpath去找
buildscript {
ext {
springBootVersion = "2.3.3.RELEASE"
}
repositories {
mavenLocal()
maven { url 'http://maven.aliyun.com/nexus/content/groups/public' }
jcenter()
}
// 此处先引入插件
dependencies {
// 老式的必须定义 classpath, 后面就不用写版本号了
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
//再应用插件
apply plugin: 'org.springframework.boot' //社区插件,需要事先引入,不必写版本号
// 如果是第三方插件已经被托管在 https://plugins.gradle.org/ 网站上,就可以不用在 buildscript 里配置 classpath自定义插件
buildSrc 项目 (只能在本工程使用)
buildSrc 是Gradle 默认的插件目录,编译 Gradle 的时候会自动识别这个目录,将其中的代码编译为插件
package com.xxx.xx
import org.gradle.api.Plugin
import org.gradle.api.Project
class Text implements Plugin<Project>{
@Override
void apply(Project project) {
project.task("abc"){
doLast{
println("自定义插件")
}
}
}
}在 properties 文件中指明我们实现插件的全类名 implementation-class=com.xxx.xx.Text

如果需要在别的项目中使用, 可以上传自定义插件到私库
引入一个插件需要关注的5个方面
- Usage Tasks
- Project_layout
- Source_sets
- Dependency_management
- 插件的引用名
- 主要功能(任务) gradle tasks 对比前后任务的多少
- 工程目录结构 sourceSets 可以改变约定的目录结构
sourceSets {
main {
java {
srcDirs = ['src/java']
}
resources {
srcDirs = ['src/resources']
}
}
test {
...
}
}- 依赖管理
- 常用属性和方法:
Directory properies: ${testResultsDirName}
方法:
sourceCompatibility(JavaVersion.VERSION_1_8) // Compatibility 兼容性
targetCompatibility(JavaVersion.VERSION_1_8)build.gradle 文件

- define: 本质是一个脚本文件, 作为项目构建的入口
- 每个 build 文件都有一个对应的 Project 实例; 对 build.gradle 文件配置,本质就是设置 Project 实例的属性和方法。
由于每个 project 都会有一个 build 文件,那么 Root Project 也不列外. Root Project 可以获取到所有 Child Project, 所以在 Root Project 的 build 文件中我们可以对 Child Project 统一配置, 比如应用的插件、依赖的 maven 中心仓库等
- 属性 (自带属性 + 自定义属性 ext)
- 自带属性
sourceCompatibility=1.8
targetCompatibility=1.8
compileJava.options.encoding='UTF-8' // 源码解码字符集
compileTestJava.options.encoding='UTF-8'
tasks.withType(JavaCompile) { // 源码编码字符集
options.encoding = "UTF-8"
}
tasks.withType(Javadoc) {
options.encoding = "UTF-8"
}- 自定义属性 ext
- 方法
// 传统方式 buildscript, 必须写在 build.gradle 文件的最前端
buildscript // 获取脚本依赖插件
apply // 应用插件
task // 定义任务
plugins // 应用插件
dependencies // 设置当前project依赖信息
repositories // 设置当前project仓库信息
allprojects // 设置所有project [root project+child project] 信息
subprojects // 设置所有child project信息
sourceSets // 配置此项目的源集
publishing // 配置发布插件添加的 PublishingExtension
configurations // 配置此项目的依赖项配置
artifacts // 配置此项目的已发布构件gradle.properties 一般放 系统属性、环境变量、项目属性、JVM相关配置信息
gradle.properties 文件中的属性会自动在项目运行时加载
# 案例:加快构建速度
## 设置此参数主要是编译下载包会占用大量的内存,可能会内存溢出
org.gradle.jvmargs=-Xms4096m -Xmx8192m
## 开启gradle缓存
org.gradle.caching=true
## 开启并行编译org.gradle.parallel=true
## 启用新的孵化模式
org.gradle.configureondemand=true
## 开启守护进程org.gradle.daemon=trueorg.gradle.jvmargs 的默认参数 §configuring_jvm_memory

发布
apply plugin: 'java-library'
javadoc.options.encoding='UTF-8'
java {
// 带源码和javadoc的发布: 需要'java-library'插件支持: 它是java的升级版,java插件的功能java-library都有
withJavadocJar()
withSourcesJar()
}
publishing {
publications {
myLibrary(MavenPublication) {
// 指定GAV坐标信息
groupId = 'org.gradle.sample'
artifactId = 'library'
version = '1.1'
from components.java//发布jar包
// from components.web // 发布war包, 需引入war插件 ( apply plugin: 'war' )
}
}
repositories {
// 本地仓库位于USER_HOME/.m2/repository
mavenLocal()
// 发布项目到私服中
maven {
name = 'myRepo' // name 属性可选,表示仓库名称,url必填
// 发布地址:可以是本地仓库或者maven私服
// url = layout.buildDirectory.dir("repo")
// change URLs to point to your repos, e.g. http://my.org/repo
def releasesRepoUrl = layout.buildDirectory.dir('repos/releases')
def snapshotsRepoUrl = layout.buildDirectory.dir('repos/snapshots')
url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
// 认证信息:用户名和密码
// credentials {
// username = 'joe'
// password = 'secret'
// }
}
}
}执行发布指令:
generatePomFileForPubNamePublication: 生成 pom 文件
publishPubNamePublicationToRepoNameRepository: 发布指定项目 (PubName) 到指定仓库, 如果没有仓库名,默认为 maven
publishPubNamePublicationToMavenLocal: 将指定项目 (PubName) 发布复制到 本地 Maven 仓库中包括 POM 文件和其他元数据
publish: 发布到 repositories 中指定的仓库 (如 Maven 私服)
publishToMavenLocal: 执行所有发布任务中的操作发布到本地 maven 仓库 [默认在用户家目录下的.m2/repository]Gradle 生命周期
简述
- Initialzation
- init.gradle
- Setting Script
- Configuration
- build.gradle
- 根据 task 组成的有向无环图 Directed Acyclic Graphs, 生成有向无环树
- Execution
- 按照上面构建好的有向无环图, 顺序执行 Task (Action动作)
生命周期中的Hook
钩子函数都是由 gradle 自动回调完成的, 利用钩子可以实现我们想要的功能
执行过程对象实例:
- Gradle 初始化构建, 全局单例
- Project pom.xml 模块唯一对应
- Settings 项目唯一, 一般只用到 include 方法
- Task 前面的有向无环图基于 Task 对象
Hook穿插在生命周期的各个阶段:
- Initialzation
- settings.gradle Gradle#settingsEvaluated
- build.gradle Gradle#projectsLoaded
- Configuration
- allprojects: Gradle#beforeProject and Project#beforeEvaluate
- subprojects: Gradle#beforeProject and Project#beforeEvaluate
- SubProjectA: Gradle#beforeProject and Project#beforeEvaluate
- EXECUTE build.gradle 配置段脚本
- SubProjectA: Gradle#afterProject and Project#afterEvaluate
- ALL READY:
- Gradle#projectsEvaluated
- Gradle#taskGraph#whenReady
- Execution
- TaskExecutionGraph#beforeTask
- EXECUTE Task Action
- TaskExecutionGraph#afterTask
- ALL EXECUTED: Gradle#buildFinish
查看所有生命周期
// 1.settingsEvaluated钩子函数,在初始化阶段完成
gradle.settingsEvaluated {
println "settingsEvaluated"
}
// 2.projectsLoaded钩子函数,在初始化阶段完成
gradle.projectsLoaded {
println "projectsLoaded"
}
// 声明一个变量:表示当前项目名,在每次执行某个项目的beforeEvaluate方法时先给projectName变量赋值
// 这样方便在:gradle.beforeProject和afterProject两个钩子函数使用。
def projectName = ""
gradle.addProjectEvaluationListener(new ProjectEvaluationListener() {
// 3.执行各个project的beforeEvaluate:在配置阶段完成
@Override
void beforeEvaluate(Project project) {
projectName =
println "${} Project beforeEvaluate"
}
// 5.执行各个project的afterEvaluate:在配置阶段完成
@Override
void afterEvaluate(Project project, ProjectState projectState) {
println "${} Project afterEvaluate"
}
});
gradle.beforeProject {// 4.执行各个project的beforeProject:在配置阶段完成
println "${projectName} beforeProject..."
}
gradle.afterProject {// 6.执行各个project的afterProject:在配置阶段完成
println "${projectName} afterProject..."
}
// 7.所有工程的 build.gradle 执行完毕后,回调 Gradle 对象的 projectsEvaluated 方法:在配置阶段完成
def rootProjectName = rootProject.getName()
gradle.projectsEvaluated {
println "${rootProjectName} projectsEvaluated..."
}
// 8.配置阶段完毕后,回调 TaskExecutionGraph 对象的 whenReady 方法:在配置阶段完成
gradle.taskGraph.whenReady {
println "${rootProjectName} taskGraph whenReady..."
}
// 9.在当前Task执行之前,会回调 TaskExecutionGraph 对象的 beforeTask方法:在执行阶段完成
gradle.taskGraph.beforeTask { task ->
println "this is the task ${} of the project ${task.getProject().name} beforeTask.."
}
// 10.在当前Task执行之后,会回调 TaskExecutionGraph 对象的 afterTask方法:在执行阶段完成
gradle.taskGraph.afterTask { task ->
println "this is the task ${} of the project ${task.getProject().name} afterTask.."
}
// 11.当所有的 Task 执行完毕后,会回调 Gradle 对象的 buildFinish 方法:在执行阶段完成
gradle.buildFinished {
println "${rootProjectName} buildFinished..."
}
gradle.taskGraph.addTaskExecutionGraphListener(new TaskExecutionGraphListener() {
// 生成有向无环图
@Override
void graphPopulated(TaskExecutionGraph taskExecutionGraph) {
taskExecutionGraph.allTasks.forEach(task -> {
// 核心逻辑:通过taskExecutionGraph获得所有的task
taskExecutionGraph.allTasks.forEach(releaseTask -> {
println "diy print: " + releaseTask.getProject().name + ":" + release
})
})
}
})利用Hook的实例
- 计算 Gradle 构建过程中各个阶段的耗时 (不包含 init.gradle 初始化时间)
def projectName = rootProject.getName() // 定义项目名
long beginOfSetting = System.currentTimeMillis() // 初始化阶段开始时间
def beginOfConfig // 配置阶段开始时间
def configHasBegin = false // 配置阶段是否开始了,只执行一次
def beginOfProjectConfig = new HashMap() // 存放每个build.gradle 执行之前的时间
def beginOfTaskExecute // 执行阶段开始时间
gradle.projectsLoaded { // 初始化阶段执行完毕
println "${projectName}工程 初始化总耗时 ${System.currentTimeMillis() - beginOfSetting} ms"
}
// build.gradle 执行前
gradle.beforeProject { Project project ->
if (!configHasBegin) {
configHasBegin = true
beginOfConfig = System.currentTimeMillis()
}
beginOfProjectConfig.put(project, System.currentTimeMillis())
}
// build.gradle 执行后
gradle.afterProject { Project project ->
def begin = beginOfProjectConfig.get(project)
if ( == projectName) {
println "根工程${projectName} 配置阶段耗时:${System.currentTimeMillis() - begin} ms"
} else {
println "子工程${} 配置阶段耗时:${System.currentTimeMillis() - begin} ms"
}
}
gradle.taskGraph.whenReady {// 配置阶段完毕
println "整个${projectName}项目在配置阶段总耗时:${System.currentTimeMillis() - beginOfConfig} ms"
beginOfTaskExecute = System.currentTimeMillis()
}
// 执行阶段开始
gradle.taskGraph.beforeTask { Task task ->
task.doFirst {
task.ext.beginOfTask = System.currentTimeMillis()
}
task.doLast {
println "${}在执行阶段耗时:${System.currentTimeMillis() - task.ext.beginOfTask} ms"
}
}
gradle.buildFinished {// 执行阶段完毕
println " 执行阶段总耗时:${System.currentTimeMillis() - beginOfTaskExecute} ms"
println " 整个构建过程耗时:${System.currentTimeMillis() - beginOfSetting} ms"
}- 查看 task 有向无环图
gradle.taskGraph.addTaskExecutionGraphListener(new TaskExecutionGraphListener() {
// 生成有向无环图
@Override
void graphPopulated(TaskExecutionGraph taskExecutionGraph) {
taskExecutionGraph.allTasks.forEach(task -> {
// 核心逻辑:通过taskExecutionGraph获得所有的task
taskExecutionGraph.allTasks.forEach(releaseTask -> {
println "diy print:" + releaseTask.getProject().name + ":" + release
})
})
}
})脚手架
plugins {
id 'java'
id 'org.springframework.boot' version '2.6.3'
id 'io.spring.dependency-management' version '1.0.15.RELEASE'
}
group = 'com.xxx.xx'
version = '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
tasks.named('test') {
// useJUnit() for junit four
useJUnitPlatform() // for junit jupiter
}or
buildscript {
repositories {
maven { url 'https://maven.aliyun.com/repository/public' }
}
dependencies {
classpath 'org.springframework.boot:spring-boot-gradle-plugin:2.4.1'
}
}
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'includeBuild vs include
includeBuild 'file/path' // for setting.gradle
// includeBuild 不需要修改项目名称 (no need to change project name: findProject(':old')?.name='new')
include 'project:project' // for build.gradle, no leading ":"
findProject(':project:project')?.name = 'new-name' // change to shorten the name-
?.(safe navigation operator)
It was first used by Groovy 1.0 in 2007 and is currently supported in languages such as C#, Swift, TypeScript, Ruby, Kotlin, Rust, JavaScript, and others. There is currently no common naming convention for this operator, but safe navigation operator is the most widely used term.
Project dependency case
The tree would look like this :
settings.gradle
module1/build.gradle
module2/build.gradleAnd your are declaring a dependency in module1/build.gradle like this :
dependencies{
implementation(project("com.domain:module2))
}The dependency will be resolved only if both projects are declared as sub-modules of a common root project.
It means you have a root settings.gradle like this :
root = "rootProject"
include(":module1")
include(":module2")Composite build case
The projects do not need to have common “umbrella” root project. Each project is a totally independent project.
One project can simply declare a dependency on the other project (without even the target project knowing about it).
Tree :
project1/build.gradle
project1/settings.gradle
project2/build.gradle
project2/settings.gradleIn project1/settings.gradle :
root = "project1"
includeBuild("../project2") // No more ':' as it is not a moduleIn project2/settings.gradle :
root = "project2"In project1/build.gradle like this :
dependencies{
implementation("com.domain:project2")
}
















