2. 多模块项目的目录组织规范
好,咱们直接切入正题。
Gradle的脚本文件,说白了就是Groovy代码。你写build.gradle的时候,其实就是在写Groovy。很多人一开始觉得Gradle难,我告诉你,难的不是Gradle本身,而是你对Groovy不熟。一旦你掌握了Groovy的基础语法,Gradle的那些配置在你眼里就是一层窗户纸。
我自己带团队的时候,发现很多新人卡在第2周——明明知道要配依赖、要配插件,但就是看不懂脚本里那些花括号和箭头是干嘛的。嗯,今天我们就把这层纸捅破。
1. 变量声明:def 还是 类型?
Groovy是动态语言。你可以写def name = "小明",也可以直接写String name = "小明"。有什么区别?我个人习惯,在Gradle脚本里一律用def。为什么?因为脚本里变量类型没那么重要,写类型反而啰嗦。
// 动态类型
def projectName = "my-app"
def versionCode = 1
// 你也可以指定类型,但一般不这么干
String description = "这是一个示例项目"
2. 字符串:单引号、双引号、三引号
字符串是Gradle脚本里出现频率最高的东西。我刚开始用Groovy的时候,常被这三种引号搞晕。来,一次说清楚。
| 引号类型 | 是否支持插值 | 适用场景 |
|---|---|---|
'单引号' | 否 | 纯字符串,如任务名、常量 |
"双引号" | 是(${}) | 需要拼接变量或表达式 |
'''三单引号''' | 否 | 多行字符串(如Shell脚本嵌入) |
"""三双引号""" | 是 | 多行模板,比如DSL块内的描述 |
def name = "Gradle"
def greeting = "Hello, ${name}!" // 输出: Hello, Gradle!
def multi = """这是第一行
这是第二行
版本号: ${version}""" // 多行模板
${}插值。曾经有个同事在build.gradle里写'版本号: ${version}',结果输出了字面量${version},排查了半天。
3. 集合:List 和 Map
Gradle里的集合操作比你想象的重要。你要配置依赖、配置插件、配置仓库,背后全是集合操作。先看两个最常用的。
列表(List)
// 定义
def deps = ["spring-core", "spring-beans", "spring-context"]
// 添加元素
deps << "spring-aop" // 左移操作符,很Groovy
deps.add("spring-jdbc") // 也能用Java方式
// 遍历
deps.each { dep ->
println "依赖: $dep"
}
// 过滤
def webDeps = deps.findAll { it.contains("web") }
字典(Map)
// 定义
def config = [
group: "com.example",
version: "1.0.0",
description: "示例项目"
]
// Groovy里用点号访问键
println config.group // 输出: com.example
// 也支持下标
println config["version"] // 输出: 1.0.0
// 动态添加
config.javaVersion = "11"
你看,Groovy的集合操作比Java简洁太多了。尤其是那个左移操作符<<,我特别爱用,写起来就像在“把东西推进去”。
4. 闭包——Groovy的灵魂
闭包是Groovy的精髓,也是Gradle DSL的核心。你写的每一个dependencies { }、android { },其实都是在传闭包。
闭包说白了就是一个代码块,可以赋值给变量,也可以当作参数传递。
// 定义一个简单的闭包
def myClosure = { String name ->
println "你好, $name!"
}
// 调用
myClosure("Groovy") // 输出: 你好, Groovy!
// 如果只有一个参数,可以用it
def printIt = { println "参数是: $it" }
printIt(42) // 输出: 参数是: 42
闭包的另一个重要特性:它可以访问外部变量。这个特性在Gradle脚本里特别有用——你可以在闭包里直接使用脚本里定义的变量。
def projectName = "my-module"
def printName = {
println "当前项目: $projectName" // 访问外部变量
}
printName() // 输出: 当前项目: my-module
dependencies { implementation 'xxx' } 中的dependencies是一个方法,它接收一个闭包作为参数。闭包内部调用的implementation又是另一个方法——这就是DSL的嵌套结构。
5. DSL风格——Gradle脚本为什么可以这样写?
很多人第一次看到build.gradle会觉得“这不是JSON也不是XML,它到底是什么语法?”
答案就是:Groovy的DSL(领域特定语言)风格。说白了就是利用闭包、方法调用省略括号、属性赋值省略等号等特性,让代码看起来像配置文件。
// 常规Java写法
project.configure(project.dependencies, new Action<DependencyHandler>() {
void execute(DependencyHandler deps) {
deps.add("implementation", "com.google.guava:guava:30.1-jre");
}
});
// Gradle DSL写法——省略括号、闭包作为参数
dependencies {
implementation 'com.google.guava:guava:30.1-jre'
}
你仔细看:dependencies后面没有括号,直接跟了一个花括号闭包。闭包内部又调用了implementation方法,同样省略了括号。这是Groovy允许的——如果你调用方法时,最后一个参数是闭包或者Map,就可以把括号省掉。
还有,属性赋值也可以省略等号:
// 正常写法
project.setGroup("com.example")
// DSL写法
group = "com.example" // 实际上调用了setGroup()
// 在build.gradle里直接写
group "com.example" // 也可以,因为Groovy把方法调用当成属性设置
这种灵活性一开始可能让人困惑,但用熟了你会觉得很自然。我在项目里经常看到新人写group = "com.example",其实两种写法都可以,我建议统一用一种风格,团队里别混用。
本章核心知识图谱
下面这张图帮你把Groovy语法和Gradle的关系理清楚。看一遍,你就明白这些零散知识点是怎么串起来的。
小结一下
- 变量:用
def,别想太多。 - 字符串:双引号能插值,单引号纯文本,多行用三引号。
- 集合:List用
<<添加,Map用点号取值——超级方便。 - 闭包:Gradle脚本的一切都是闭包。记住
it是默认参数,方法最后的闭包可以省括号。 - DSL风格:本质上就是方法调用+闭包的语法糖,别被它吓到。
build.gradle里写了一个很大的闭包,里面嵌套了好几个闭包,结果一个变量作用域搞错了,折腾了一下午。记住:闭包内的变量引用是“写时绑定”,而不是“运行时绑定”。如果你在循环里创建闭包要特别小心。更简单的做法:尽量保持闭包短小,别写太长。
好了,Groovy的基础够用了。再往后,你就大胆去看Gradle的构建脚本吧——看到{ }就默念“这是闭包”,看到省略括号的方法调用就默念“这是DSL”。熟练了之后,你甚至会觉得这种写法比纯Java或Kotlin DSL更直观。