1. 项目结构

目录介绍:
- build.gradle:项目编译时要读取的配置文件,build.gradle有两个,一个是全局的,一个是在模块里面。全局的build.gradle主要设置的是声明仓库源,gradle的版本号说明等。
- gradlew:linux下的gradle环境脚本。可以执行gradle指令,比如./greadle build。
- gradlew.bat:windows下的gradle环境脚本。可以执行gradle指令。
- settings.gradle:包含一些必要设置,例如,任务或项目之间的依赖关系等,无论有多少子模块,该文件都只有一个,且在根目录中。
- gradle:包含wrapper文件夹及其两个子文件,可以自动安装gradle环境。也就是如果本地没有安装gradle环境,则可以使用这里的。
Gradle-Wrapper是为了简化Gradle的安装和部署,目的是为了使任意的gradle项目都不需要单独安装环境,项目会自动识别有无gradle环境。如果在本地没有找到与gradle-wrapper.properties中版本相同的Gradle。IDEA就会自动帮你下载一个gradle环境。gradle/wrapper/gradle-wrapper.properties内容解析:
properties
#greadistributionBase和distributionPath配合使用,指定gradle解压后的存放位置。
#GRADLE_USER_HOME表示用户目录,可以在环境变量中配置,如果未配置,则使用默认的:
#windows系统默认是:C:\window\<user-name>\.gradle\
#linux系统:$HOME/.gradle
greadistributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
#指定某个版本的gradle的下载地址。
distributionUrl=https://services.gradle.org/distributions/gradle-9.2.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
#zipStoreBase和zipStorePath配合使用,指定下载的gradle.zip文件的存放位置。
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

这里的 包装器 指的就是使用gradle-wrapper.properties指定的gradle来构建项目。
Gradle遵循COC(约定优于配置)的理念,默认情况下提供了与Maven项目相同的项目结构配置:
java
project root
src/main/java
src/main/resource
src/test/java
src/test/resource
src/main/webapp(web工程)
2. 常用命令
执行 gradle build 构建项目,会产生一个build目录,里面有打包好的jar包。
执行 gradle build -x test 跳过测试构建项目。
bin
PS D:\Project\gradle-demo> gradle build
Java HotSpot(TM) 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended
BUILD SUCCESSFUL in 12s
7 actionable tasks: 7 executed
PS D:\Project\gradle-demo>
执行 gradle clean 命令会删除build目录。
bin
PS D:\Project\gradle-demo> gradle clean
BUILD SUCCESSFUL in 800ms
1 actionable task: 1 executed
PS D:\Project\gradle-demo>
执行 gradle -v 可以查看版本,用的是本地安装的gradle,配置环境变量的那个gradle。
bin
PS D:\Project\gradle-demo> gradle -v
执行 ./gradlew -v 这里用的是项目中wrapper下的那个gradle。
bin
PS D:\Project\rcp> ./gradlew -v
执行 gradle -h 可以查看帮助。
bin
PS D:\Project\gradle-demo> gradle -h
3. build.gradle
java
plugins {
id 'java'
id 'org.springframework.boot' version '4.0.0'
id 'io.spring.dependency-management' version '1.1.7'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
description = 'gradle-demo'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
//配置国内下载源
maven{
url 'https://maven.aliyun.com/repository/public/'
}
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-webmvc'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-webmvc-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
tasks.named('test') {
useJUnitPlatform()
}
- group、name、version:group是module所在组;name是module的名字,同一个组里name具有唯一性;version是module的版本号;group、name、version三者构成了该module的唯一性。
- apply :在module中应用一个插件。和plugins一个意思。
- dependencies:用来声明module所依赖的jar包或其他module。
- repositories:用来声明仓库,告诉程序到哪个仓库去查找对应的module、jar包等依赖。
- task:用来声明module的任务,其对应org.gradle.api.Task。
依赖管理
Gradle依赖的粒度控制相较于Maven也更加精细,maven只有compile、provided、test、runtime四种scope,而gradle有以下几种scope:
- implementation:默认的scope。implementation的作用域会让依赖在编译和运行时均包含在内,但是不会暴露在类库使用者的编译时。举例,如果我们的类库包含了gson,那么其他人使用我们的类库时,编译时不会出现gson的依赖。
- api:和implementation类似,都是编译和运行时都可见的依赖。但是api允许我们将自己类库的依赖暴露给我们类库的使用者。
- compileOnly 和runtimeOnly:这两种顾名思义,一种只在编译时可见,一种只在运行时可见,而runtimeOnly和Maven的provided比较接近。
- testImplementation:这种依赖在测试编译时和运行时可见,类似于Maven的test作用域。
- testCompileOnly 和testRuntimeOnly:这两种类似于compileOnly和runtimeOnly,但是作用于测试编译时和运行时。
- annotationProcessor:用来声明"只在编译期运行的注解处理器依赖",不会进入运行时。Java在编译阶段可以运行一类特殊的程序,叫注解处理器,用来:扫描源码中的注解、生成新代码(.java 文件)或在编译期做校验、直接报错。Gradle用annotationProcessor来告诉编译器:这些依赖,是给"编译器"用的,不是给程序运行用的。
通过简短精悍的依赖配置和多种多样的作用与选择,Gradle可以为我们提供比Maven更加优秀的依赖管理功能。
4. Groovy 语法
Groovy是一种基于JVM的动态语言,语法和Java相似,最终也是要编译.class文件在JVM上运行。Groovy完全兼容Java,在此基础上添加了很多动态类型和灵活特性,比如支持闭包,支持DSL(领域特定语言),是一门非常灵活的动态脚本语言。
要执行Groovy的脚本,必须安装Groovy环境,或者使用Java的ClassLoader来调用。
4.1. 变量、方法、类
1、Groovy中使用def关键字来定义变量,可以不指定变量类型,默认访问修饰符是public。
java
def a = 1;
def int b = 1;
def c = "hello world!";
2、方法使用返回类型或def关键字定义,方法可以接收任意数量的参数,这些参数可以不申明类型,如果不提供可见性修饰符,则该方法为public。如果指定了方法返回类型,可以不需要def关键字来定义方法。如果不使用return,则方法的返回值为最后一行代码的执行结果。
java
def add(int a, int b){
println a + b
}
int minus(a, b){
return a - b
}
int minus(a, b){
a - b
}
省略:
- 语句后面的分号可以省略。
- 方法的括号可以省略。
- 参数类型可以省略。
return可以省略。
3、Groovy类非常类似Java类。
java
def p = new Person()
p.increaseAge 5
println p.age
class Person{
String name
integer age = 10
def increaseAge(Integer years){
this.age += years
}
}
区别:
- 默认类的修饰符为public。
- 没有可见性修饰符的字段会自动生成对应的setter和getter方法。
- 类不需要与它的源文件有相同的名称,但还是建议采用相同的名称。
4.2. for、switch
1、Groovy支持Java的for(int i=0; i<N; i++)形式的循环语句,另外还支持for in loop形式,支持遍历范围、列表、Map、数组和字符串等多种类型。
java
//遍历范围
def x = 0;
for(i in 0..3){
x += i
}
assert x == 6
//遍历列表
def x = 0
for(i in [0,1,2,3]){
x += i
}
assert x == 6
//遍历Map中的值
def map = ['a':1, 'b':2, 'c':3]
x = 0
for(v in map.values()){
x += v
}
assert x == 6
2、Groovy中的switch语句不仅兼容Java代码,还可以处理更多的case表达式。case表达式可以是字符串、列表、范围、Integer等等。
java
def x = 16
def result = ""
switch(x){
case "ok":
result = "found ok"
case [1, 2, 4, 'list']:
result = "list"
break
case 10..19:
result = "range"
break
case Integer:
result = "integer"
break
default:
result = "default"
}
assert result == "range"
4.3. 数据类型
Groovy中的数据类型主要有以下几种:
- Java中的基本数据类型。
- Groovy中的容器类。
- 闭包
4.3.1. 字符串
在Groovy中有两种字符串类型,普通字符串String(java.lang.String)和插值字符串GString(groovy.lang.GString)。
在Groovy中单引号字符串和双引号字符串都可以定义一个字符串常量,只不过单引号字符串不支持插值。
双引号字符串可以支持插值,插值指的是替换字符串中的占位符,占位符表达式是${}或者以$为前缀。
三引号字符串了可以保留文本的换行和缩进符,不支持插值。
java
def name = '我是chian人'
println "hello ${name}"
println "hello $name"
def str = '''中国人和美国人
一起吃火锅
好不好?'''
String是不可变的,GString是可变的,GString和String即使有相同的字面量,它们的hashCode也可能不同,因此应该避免使用GString作为Map的Key。
java
//当双引号字符串中包含插值表达式时,字符串类型为GString。下面断言为true
assert "one: ${1}".hashCode() != "one: 1".hashCode
4.3.2. List
Groovy在Java集合的基础上进行了增强和简化,Groovy的List对应Java中的List接口,默认的实现类为Java中的ArrayList。可以使用as操作符显式指定List的实现类为java.util.LinkedList。使用[]获取List中具有正索引或负索引的元素。
java
def number = [1, 2, 3]
assert number instanceof List
def linkedList = [1, 2, 3] as LinkedList
assert linkedList instanceof java.util.LinkedList
def number = [1, 2, 3, 4]
assert number[1] == 2
//-1表示列表末尾的第一个元素
assert number[-1] == 4
//在列表末尾追加一个元素5
number << 5
assert number[4] == 5
assert number[-1] ==5
4.3.3. Map
Groovy中创建Map同样使用[],需要同时指定键和值,默认的实现为java.util.LinkedHashMap。
java
def name = [one: 'aaa', tow: "bbb", three: "ccc"]
assert name['one'] == 'aaa'
assert name.two == 'bbb'
Map中的键关联问题:
java
def key = 'name'
//使用字符串key作为键
def person = [key: '张三']
assert person.containKey('key')
//使用变量key作为键
person = [(key): '李四']
assert person.contailsKey('name')
4.4. 闭包
Groovy中的闭包是一个开放的、匿名的、可以接受参数和返回值的代码块。
定义闭包
java
{ [closureParameters -> ] statements }
闭包分为两个部分,分别是参数列表部分[closureParameters -> ]和语句部分statements。
参数列表部分是可选的,如果闭包只有一个参数,则参数名是可选的,Groovy会隐式指定it作为参数名。
java
{ println it} //使用隐式参数it的闭包
当需要指定参数列表时,需要->将参数列表和闭包体分离。
java
{ it -> println it}
{ String a, String b ->
println "${a} is ${b}"
}
闭包是groovy.lang.Cloush类的一个实例。这使得闭包可以赋值给变量或字段。
java
//将闭包赋值给一个变量
def println = {it -> println it}
assert println instanceof Closure
//将闭包赋值给Closuer类型变量
Closuer do = { println 'do!'}
调用闭包
java
def code = {123}
assert code() == 123 //闭包当做方法调用
assert code.call() == 123 //显式调用call方法
def isOddNumber = { int i -> i%2 != 0}
assert isOddNumber(3) == true //调用带参数的闭包
闭包作为方法参数
java
//无参数闭包
def test1(Closure closure){
println "test1 start..."
closuer(); //调用闭包
println "test1 end..."
}
test1{prinln "hahaha"}
//有参数闭包
def test2(Closuer closure){
int a = 100;
int b = 200;
println "test2 starte"
closure(a,b);//调用闭包
println "test2 end"
}
test2 { x,y ->{
println("x:" + x)
println("y:" + y)
}
}
5. 构建过程
任何由Gradle构建的项目都遵循这个过程,分为三个阶段:
- Initialization:初始化阶段。按顺序执行init.gradle -> settings.gradle脚本,生成Gradle、Setting、Project对象。
- Configuration:编译阶段,也叫配置阶段。按顺序执行root目录下的build.gradle -> 子项目build.gradle脚本,生成Task执行流程图。
- Execution:执行阶段。按照Task执行图顺序运行每一个Task,完成一个个步骤,生成最终的APK文件或JAR包文件。

整个构建过程,官方在一些节点都设置有hook钩子函数,以便我们在构建过程中添加自己的逻辑。影响构建过程。hook钩子函数可以理解为监听函数,另外也可以设置Linsener监听函数。
5.1. Task
Task是Gradle执行的基本单元。Gradle构建项目是先把所有.gradle脚本都执行一遍,编译生成对应的Gradle、Setting、Project对象。然后根据配置,生成一张Task组成的有向无环图。
最终Gradle会按照图上一个个Task的关联按顺序挨个执行。每个Task都完成一个特定功能,所有Task都执行一遍,则整个项目的构建任务就完成了。
创建Task
java
task hello{
println "hello world"
}
task (hello2){
println "hello world2"
}
task ('hello3'){
println "hello world3"
}
task ("hello4"){
println "hello world4"
}
Task的Action
Task内部的Action不是唯一的,而是一个集合,可以往里面添加各种 Action,看下面Task中的声明:
java
//在Action队列头部添加action
Task.doFirst(Action<? supper Task> action);
Task.doFirst(Closure action);
//在Action队列头部添加action
Task.doLast(Action<? supper Task> action);
Task.doLast(Closure action);
//删除所有的action
Task deleteAllAction();
doFirst和doLast接受的都是闭包。
- doFirst添加的action最先执行。
- task自身的action在中间执行,无法在task外部添加,需要在自定义task时写。
- doLast添加的action最后执行。
java
task speak{
println("This is AA! -- 配置阶段")
doFirst{
println("This is doFirst -- inner -- 执行阶段")
}
doLast{
println("This is doLast -- inner -- 执行阶段")
}
}
speak.doFirst{
println("This is doFirst -- outer -- 执行阶段")
}
speak.doLast{
println("This is doLast -- outer -- 执行阶段")
}
执行任务:
java
gradle speak
执行结果:
java
> Configure project :
This is AA! -- 配置阶段
> Task :speak
This is doFirst -- outer -- 执行阶段
This is doFirst -- inner -- 执行阶段
This is doLast -- inner -- ִ执行阶段
This is doLast -- outer -- ִ执行阶段
Task依赖
Task依赖是指可以在Task之间指定执行顺序,重点理解dependsOn。
- A.dependsOn B --> 执行A的时候先执行B
- A.mustRunAfter B --> 同时执行A和B,需要先执行B再执行A。若执行关系不成立则报错。
- A.shouldRunAfter B --> 同mustRunAfter,区别:执行关系不成立但是不报错。
java
task s1{
doLast{
println("This is s1")
}
}
task s2{
doLast{
println("This is s2")
}
}
s1.dependOn s2
或者
java
task s1{
doLast{
println("This is s1")
}
}
task s2{
dependOn s1
doLast{
println("This is s2")
}
}
自定义Task
Gradle中的Task都继承自org.gradle.api.DefaultTask,自定义Task也需要继承这个类,自己写方法,然后加上@TaskAction注解,表示这个方法是Task中的action。可以加多个,按倒序执行。
自定义任务:
java
class MyTask extends DefaultTask{
@TaskAction
def ss1(){
println("This is MyTask action1")
}
@TaskAction
def ss2(){
println("This is MyTask action2")
}
}
tasks.register('speak', MyTask) {
println("This is AA")
doFirst {
println("This is doFirst")
}
doLast {
println("This is doLast")
}
}
执行结果:
java
PS D:\Project\gradle-demo> gradle speak
This is AA
> Task :speak
This is doFirst
This is MyTask action2
This is MyTask action1
This is doLast
系统自带Task
Gradle提供了很多task给我们使用,比如copy、delete等。
设置默认Task
意思是脚本中我们不调用该Task,设置的默认Task也会执行。
java
defaultTasks 'aaa', 'bbb'
tasks.register('aaa') {
doLast {
println("Default aaa task")
}
}
tasks.register('bbb') {
doLast {
println("Default bbb task")
}
}
tasks.register('other') {
doLast {
println("not a default task")
}
}
执行结果
java
PS D:\Project\gradle-demo> gradle -q
Default aaa task
Default bbb task
PS D:\Project\gradle-demo>
5.2. Plugin
什么是插件
插件只是一组任务,几乎所有的任务,如编译任务,设置域对象,设置源文件等都由插件处理。
Gradle本身只是提供了基本的核心功能,其他特性比如编译Java源码等能力都需要通过插件实现。
应用插件
两种方式:
java
plugins {
id 'java'
}
或者
java
apply plugin: 'java'
插件都有自己的短名称,上述两种写法都使用了短名称去应用JavaPlugin。我们还可以用完全的名称:
java
apply plugin: org.gradle.api.plugins.JavaPlugin
由于Gradle的默认导入,还可以这样写:
java
apply plugin: JavaPlugin
插件的应用是幂等的,如果一个插件已经被应用,再次应用不会有任何效果。
一个插件是任何实现了Plugin接口的类,Gradle提供了核心插件作为其发行包的一部分,所以核心插件可以像上述方式一样简单应用,但是对于第三方插件,需要进行配置使插件在构建路径中可用。
核心插件:docs.gradle.org/current/use...


第三方插件:plugins.gradle.org/
插件都做了什么
把插件应用到项目中可以用来扩展项目功能。包括:
- 将任务添加到项目(如编译、测试)。
- 使用有用的默认设置对已添加的任务进行预配置。
- 向项目中添加依赖配置。
- 通过扩展对现有类型添加新的属性和方法。
Java插件
Gradle中的Java插件,提供了诸如编译、测试、打包等功能。
Java插件为工程定义了许多默认值。如Java源文件位置。如果遵循这些默认规则,那么无需在脚本文件中书写太多代码。当然,Gradle也允许自定义项目中一些规则。
Java插件向项目中添加了大量任务,如下所示:


对于每个添加到该项目中的源集。Java插件将添加以下编译任务。

Java插件还增加了大量的任务构成该项目的生命周期:

Java项目常用的任务
Java项目(使用了Java插件)执行gradle build后,会默认执行下列任务:
java
gradle build
:compileJava
:processResources
:classes
:jar
:assemble
:cpmilerTestJava
:processTestResources
:testClasses
:test
:check
:build
BUILD SUCCESSFUL
Total time: 1.12 secs
clean:删除build目录以及所有构建完成的文件。
assemble:编译并打包jar文件,但不会执行单元测试。
test:编译并执行单元测试。
check:编译并测试代码。