Gradle学习笔记
写在前面
Gradle是一个开源的构建自动化工具,足够灵活,可以构建几乎任何类型的软件。Gradle对你要构建什么或如何构建它几乎没有任何假设。这使得Gradle特别灵活。
安装
官网下载
Gradle Wrapper
gradle wrapper的优点之一就是可以自定义下载的gradle的版本,如果是团队协作的话,这个功能就非常方便,简单设置即可统一团队的构建工具版本。
gradle-wrapper.properties文件内容
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
- distributionBase: Gradle 解包后存储的父目录;
- distributionPath:
distributionBase
指定目录的子目录。distributionBase+distributionPath
就是 Gradle 解包后的存放的具体目录; - distributionUrl: Gradle 指定版本的压缩包下载地址;
- zipStoreBase: Gradle 压缩包下载后存储父目录;
- zipStorePath:
zipStoreBase
指定目录的子目录。zipStoreBase+zipStorePath
就是 Gradle 压缩包的存放位置。
基本概念
项目
项目(Projects)是gradle构建的东西,项目会包含一个构建脚本,一般在项目根目录下,叫做build.gradle或者build.gradle.kts。构建脚本为项目定义任务、依赖项、插件和其他配置。单次构建可以包含一个或多个项目,每个项目可以包含子项目。
每一个构建都是由一个或多个 projects 构成的。一个 project 到底代表什么依赖于你想用 Gradle 做什么。举个例子,一个 project 可以代表一个 JAR 或者一个网页应用。它也可能代表一个发布的 ZIP 压缩包,这个 ZIP 可能是由许多其他项目的 JARs 构成的。但是一个 project 不一定非要代表被构建的某个东西。它可以代表一件**要做的事,比如部署你的应用.
任务
任务(Tasks)包含执行某些工作的逻辑,比如编译代码、运行测试或部署软件。在大多数用例中,您将使用现有已存在的任务。Gradle提供了一些实现了许多常见构建系统需求的任务,例如可以运行测试的内置Java Test 任务。插件提供更多类型的任务。
任务的组成:
- 操作(Actions):执行某些操作的工作片段,如复制文件或编译源代码
- 输入(Inputs):Actions使用或操作的值、文件和目录
- 输出(Outputs):Actions修改或生成的文件和目录
每一个 project 是由一个或多个 tasks 构成的. 一个 task 代表一些更加细化的构建. 可能是编译一些 classes, 创建一个 JAR, 生成 javadoc, 或者生成某个目录的压缩文件.
插件
插件允许您在构建中引入新的概念,而不仅仅是任务、文件和依赖项配置。例如,大多数语言插件都将源集的概念添加到构建中。
插件提供了一种跨多个项目重用逻辑和配置的方法。使用插件,您可以编写一次任务并在多个构建中使用它。或者,您可以在一个地方存储常见配置,如日志记录、依赖项和版本管理。这减少了构建脚本中的重复。使用插件对构建过程进行适当的建模可以极大地提高易用性和效率。
构建生命周期
Gradle在构建生命周期的三个构建阶段评估和执行构建脚本:
- 初始化阶段:设置生成的环境并确定哪些项目将参与其中。
- 配置阶段:构造并配置生成的任务图。根据用户要运行的任务确定需要运行哪些任务以及运行顺序。
- 执行阶段:运行在配置阶段结束时选择的任务。
构建
构建(Build)是Gradle项目中任务集合的执行。您可以通过命令行界面(CLI)或IDE指定任务选择器来运行生成。Gradle配置构建并选择要运行的任务。Gradle基于请求的任务及其依赖关系运行最小的完整任务集。
项目结构
- gradle:gradle-wrapper存放位置
- src:与maven目录一致
- build.gradle:gradle项目构建文件
- gradlew:gradle命令行工具
- settings.gradle:多模块项目配置文件
命令
-q
比如以下命令
gradle -q hello
-q 代表 quite 模式. 它不会生成 Gradle 的日志信息 (log messages), 所以用户只能看到 tasks 的输出. 它使得的输出更加清晰. 你并不一定需要加入这个选项.
列出项目所有任务
gradle tasks
列出项目的所有属性
gradle properties
注意
<<
task hello << {println 'Hello world!'
}
这个代码会报错,因为在build.gradle脚本中的"<<"符号已经被弃用。
解决方法是使用doLast
task hello {doLast {println 'Hello world!'}
}
项目API
project
简介
对于构建脚本中每个项目,Gradle 都创建了一个 Project 类型的对象用来关联此项目. 当构建脚本执行时,它会去配置所关联的 Project 对象.
-
构建脚本中每个被调用的方法(这些方法并未在构建脚本中定义)都被委托给当前 Project 对象(使用 Project 对象引用方法)。
-
构建脚本中每个被操作的属性(这些属性并未在构建脚本中定义)都被委托给当前 Project 对象(使用 Project 对象引用属性).
比如说以下build.gradle脚本
println name
println project.name
使用 gradle -q check 命令输出结果:
> gradle -q check
projectApi
projectApi
第一个输出使用的是自动委托 ( auto-delegation ),因为当前属性并没有在构建脚本中定义.
第二个语句使用了项目一个属性,这个属性在任何构建脚本中都可用,它的返回值是被关联的 Peoject 对象的目录名。
标准项目属性
以下是常见属性
Name | Type | Default Value |
---|---|---|
project | Project | Project 实例对象 |
name | String | 项目目录的名称 |
path | String | 项目的绝对路径 |
description | String | 项目描述 |
projectDir | File | 包含构建脚本的目录 |
build | File | projectDir/build |
group | Object | 未具体说明 |
version | Object | 未具体说明 |
ant | AntBuilder | Ant实例对象 |
注意,我们的构建脚本只是个很简单的 Groovy 代码 ,不过它会再调用 Gradle API,Project 接口通过调用 Gradle API 让我们可以操作任何事情
脚本 API
简介
当 Gradle 执行一个脚本时,它会将这个脚本编译为实现了 Script 的类. 也就是说所有的属性和方法都是在 Script 接口中声明的,由于你的脚本实现了 Script 接口,所以你可以在自己的脚本中使用它们.
变量
在 Gradle 构建脚本中有两种类型的变量可以声明:局部变量 ( local ) 和 扩展属性 ( extra ) .
局部变量
局部变量使用关键字 def 来声明,其只在声明它的地方可见 . 局部变量是 Groovy 语言的一个基本特性.
build.gradle
def dest = "dest"task copy(type: Copy) {form "source"into dest
}
扩展属性
在 Gradle 领域模型中所有被增强的对象能够拥有自己定义的属性. 这包括,但不仅限于 projects , tasks , 还有 source sets . Project 对象可以添加,读取,更改扩展的属性. 另外,使用 ext 扩展块可以一次添加多个属性.
build.gradle
apply plugin: "java"// 一个 ext 扩展块向 Project 对象添加了两个扩展属性.
ext {springVersion = "3.1.0.RELEASE"emailNotification = "build@master.org"
}// 名为 purpose 的属性被添加到每个 source set,然后设置 ext.purpose 等于 null ( null值是被允许的 ). 当这些扩展属性被添加后,它们就像预定义的属性一样可以被读取,更改值.
sourceSets.all { ext.purpose = null }sourceSets {main {purpose = "production"}test {purpose = "test"}plugin {purpose = "production"}}task printProperties << {println springVersionprintln emailNotificationsourceSets.matching { it.purpose == "production" }.each { println it.name }}
}
插件
在Gradle中一般有两种类型的插件,脚本插件和二进制插件.脚本插件是额外的构建脚本,它会进一步配置构建,通常实行声明的方式操纵的构建.尽管他们可以外部化并且从远程位置访问,它们通常还是会在构建内部中使用.二进制插件是实现了Plugin接口的类,并且采用编程的方式来操纵构建.二进制插件可以驻留在构建脚本,项目层级内或外部的插件jar.
脚本插件
build.gradle
apply from: 'other.gradle'
脚本插件可以从本地文件系统或在远程位置的脚本中应用.文件系统的位置是相对于项目目录,而远程脚本位置的是由一个HTTP URL
指定的.多个脚本插件(两种形式之一)可以被应用到给定的构建。
二进制插件
build.gradle
apply plugin: 'java'
插件可以使用插件ID应用.插件的id作为给定的插件的唯一标识符.核心插件注册一个可以用作插件的id的短名称.在上述情况下,我们可以使用简称java
的插件以应用JavaPlugin.
插件DSL
build.gradle
plugins {id 'java'
}
要从插件门户应用一个社区插件,必须使用插件的完全限定id
新插件{}
块不支持任意Groovy代码.被限制的原因是为幂等(每次产生相同的结果)和无副作用(为了Gradle随时执行的安全).
形式是:
plugins{id «plugin id» version «plugin version»
}
«plugin id»
和«plugin version»
必须是常量,字面量,字符串.其他语句都是不允许的;他们的存在会导致编译错误.
插件{}
块也必须在构建脚本的顶部声明.它不能被嵌套在另一个结构(例如,if语句或for循环).
插件{}
块目前只能在一个项目的构建脚本中使用.他不能在脚本插件,settings.gradle
和出书画脚本中使用.
不能与subjects{},allprojects{}等结合使用,每个项目都必须在自己的构建脚本中的plugins{}块中声明应用的插件.
Java插件
Java 插件引入了许多任务到项目当中
依赖项
api
api
关键字表示该依赖项将会被暴露给模块的公共接口。如果一个模块依赖于另一个模块,并且在其公共接口中使用了该依赖项,那么该依赖项应该使用api
关键字声明。这意味着,如果一个模块依赖于该模块,并且使用了该依赖项,那么它也会自动依赖于该依赖项。
implementation
implementation
关键字表示该依赖项仅在模块的内部使用,并不会暴露给其他模块。如果一个模块依赖于另一个模块,但不会在其公共接口中使用该依赖项,那么该依赖项应该使用implementation
关键字声明。这意味着,其他模块依赖于该模块时,不会自动依赖于该依赖项。
api与implementation区别
以下列模块A的配置为例,模块A依赖support模块和support2模块。
dependencies {api(project(":support"))implementation(project(":support2"))
}
如果模块B依赖模块A,那么模块B则会同时依赖support模块,但不会依赖support2模块。support2模块只在A模块内部使用,并不会传递给其他模块。
dependencies {implementation(project(":A"))
}
实践
Gradle使用约定优先于配置的方法来构建基于JVM的项目,该方法借鉴了Apache Maven的几个约定。特别是,它对源文件和资源使用相同的默认目录结构,并且与Maven兼容的存储库一起工作。
创建普通Java项目
参考
- https://docs.gradle.org/current/samples/sample_building_java_applications.html
1.创建项目文件夹
在目标目录创建一个空文件夹,比如叫做vertx4study
2.执行初始化任务
进入vertx4study文件夹,执行以下命令
gradle init
可能会出现以下界面
$ gradle initSelect type of project to generate:1: basic2: application3: library4: Gradle plugin
Enter selection (default: basic) [1..4] 2Select implementation language:1: C++2: Groovy3: Java4: Kotlin5: Scala6: Swift
Enter selection (default: Java) [1..6] 3Select build script DSL:1: Groovy2: Kotlin
Enter selection (default: Groovy) [1..2] 1Select test framework:1: JUnit 42: TestNG3: Spock4: JUnit Jupiter
Enter selection (default: JUnit 4) [1..4] 4Project name (default: demo): vertx4study
Source package (default: demo): org.example.vertx4studyBUILD SUCCESSFUL
2 actionable tasks: 2 executed
生成的项目目录如下
3.运行应用
在项目根目录下打开CMD执行命令
gradlew run
运行结果
创建多模块Java项目
参考
- https://docs.gradle.org/current/samples/sample_building_java_applications_multi_project.html
1.创建项目文件夹
在目标目录创建一个空文件夹,比如叫做vertx4study
2.执行初始化任务
进入vertx4study文件夹,执行以下命令
gradle init
可能会出现以下界面
D:\project\java\vertx4study>gradle init
Starting a Gradle Daemon, 1 incompatible and 1 stopped Daemons could not be reused, use --status for detailsSelect type of project to generate:1: basic2: application3: library4: Gradle plugin
Enter selection (default: basic) [1..4] 2Select implementation language:1: C++2: Groovy3: Java4: Kotlin5: Scala6: Swift
Generate multiple subprojects for application? (default: no) [yes, no] yesSelect build script DSL:1: Groovy2: Kotlin
Enter selection (default: Groovy) [1..2] 2Project name (default: vertx4study): vertx4study
Source package (default: vertx4study): org.example.vertx4study
Enter target version of Java (min. 7) (default: 11): 11
Generate build using new APIs and behavior (some features may change in the next minor release)? (default: no) [yes, no]> Task :init
Get more help with your project: https://docs.gradle.org/8.1.1/samples/sample_building_java_applications_multi_project.htmlBUILD SUCCESSFUL in 1m 25s
2 actionable tasks: 2 executed
生成的项目目录如下
3.运行应用
在项目根目录下打开CMD执行命令
gradlew run
运行结果
4.项目结构说明
settings.gradle.kts
D:\project\java\vertx4study\settings.gradle.kts
plugins {// Apply the foojay-resolver plugin to allow automatic download of JDKsid("org.gradle.toolchains.foojay-resolver-convention") version "0.4.0"
}rootProject.name = "vertx4study"
include("app", "list", "utilities")
定义了项目包含三个子项目
- app
- list
- utilities
buildSrc
D:\project\java\vertx4study\buildSrc
buildSrc文件夹中定义了约定插件,使得子项目之间可以重用共享构建逻辑和配置。
buildSrc下的java-common-conventions.gradle.kts
D:\project\java\vertx4study\buildSrc\src\main\kotlin\org.example.vertx4study.java-common-conventions.gradle.kts
java-common-conventions定义了一些配置,这些配置应该由我们所有的Java项目共享,而与它们是表示一个库还是实际的应用程序无关。
首先,我们应用Java插件来获得构建Java项目的所有功能。
plugins {java
}
然后,我们声明一个repositories库作为外部依赖的源
repositories {mavenCentral()
}
定义依赖约束以及所有子项目共享的标准依赖,并将JUnit 5设置为测试框架其他共享设置,如编译器标志或JVM版本兼容性,也可以在这里设置。
类似maven的父项目的pom.xml定义的dependencyManagement标签,子项目中的dependencies标签下的依赖就不需要定义版本了。
dependencies {constraints {implementation("org.apache.commons:commons-text:1.10.0") }testImplementation("org.junit.jupiter:junit-jupiter:5.9.1")
}tasks.named<Test>("test") {useJUnitPlatform()
}
app/build.gradle.kts
D:\project\java\vertx4study\app\build.gradle.kts
plugins {id("org.example.vertx4study.java-application-conventions")
}dependencies {implementation("org.apache.commons:commons-text")implementation(project(":utilities"))
}application {// Define the main class for the application.mainClass.set("org.example.vertx4study.app.App")
}
app子项目的构建脚本中指定了项目插件、依赖和主类。
问题
找不到插件
比如执行 gradle build 的时候出现以下异常
org.gradle.api.plugins.UnknownPluginException: Plugin [id: 'org.gradle.kotlin.kotlin-dsl', version: '4.0.7'] was not found in any of the following sources:
可以尝试使用
gradle build --rerun-tasks
这样就不会使用缓存了。
IDEA控制台中文乱码
1.检查settings的设置
2.Help→Etit Custom VM Options
增加配置,然后重启IDEA。
-Dfile.encoding=UTF-8
3.在build.gradle(.kts)里设置编码
subprojects {tasks.withType(JavaCompile::class.java) {options.encoding = "UTF-8"}
}
4.打开 IntelliJ IDEA 在本地的安装目录,打开idea64.exe.vmoptions,在最后一行添加
-Dfile.encoding=UTF-8
The end.