主题
第 1 章 Gradle 是什么 & 为什么这么火
学习目标:能用一句话向产品经理讲清楚「Gradle 到底是个什么玩意儿」;能向面试官讲清楚「为啥 Android 选 Gradle 而不是 Maven」;理解 Gradle 三大杀手锏 —— DSL 脚本、增量构建、依赖管理 —— 各自解决了什么痛点。
1.1 一段「家谱」:构建工具的进化史
1.1.1 没有构建工具的"石器时代"
20 多年前的程序员是怎么编译代码的?
┌──────────────────────────────────────────────────────┐
│ 1995 年的 Java 项目,命令行编译流程: │
│ │
│ $ javac -d out src/com/foo/A.java src/com/foo/B.java │
│ $ jar cf app.jar -C out . │
│ $ java -cp app.jar:lib/log4j.jar com.foo.Main │
│ │
│ 改 1 个文件 → 全部重新敲 │
│ 导一个新依赖 → 手动下载 .jar → 放 lib/ → 加 -cp │
└──────────────────────────────────────────────────────┘📌 生活化类比:这相当于做菜每次都得「先去地里挖菜 → 自己劈柴 → 自己生火」,连一道番茄炒蛋都要 2 小时。
1.1.2 构建工具的进化(30 秒版本)
1976 ── Make (Stuart Feldman, Bell Labs) ┐
├─ 第一个真正意义上的构建工具 │
├─ 看文件时间戳决定要不要重编译 │
└─ 但是:依赖管理为零、跨平台为零 │
│ C 时代
2000 ── Apache Ant (Java 时代第一波) ┐
├─ XML 描述任务 │
├─ Java 语言友好、跨平台 │
└─ 但是:依赖管理 = 把 .jar 放 lib/ 文件夹 │ Java 早期
│
2002 ── Apache Ivy (给 Ant 加依赖管理) ┐
└─ 算是补丁式的方案 │
│
2004 ── Apache Maven ┐
├─ "约定优于配置" + 标准化项目结构 │ Java 标准时代
├─ Central 仓库:transitive 依赖自动拉 │ (XML 全盛)
└─ 但是:XML 啰嗦、扩展难、不灵活 ┘
│
2007 ── ★ Gradle 项目启动 (Hans Dockter) │
2012 ── Gradle 1.0 正式发布 │
2013 ── 🎉 Google I/O 宣布 Android Studio + Gradle 标配 │ Gradle 时代
2016 ── Kotlin DSL 正式推出 (1.0) │
2018 ── Gradle 5.0 引入 Build Cache │
2019 ── Gradle 6.0 引入依赖能力 (capabilities) │
2021 ── Gradle 7.0 Configuration Cache 稳定 │
2024 ── Gradle 8.x,启动速度大幅优化,K2 + KSP2 全面支持1.1.3 为什么要造 Gradle?
故事很有意思。Gradle 的作者 Hans Dockter 之前是 Maven 重度用户,写得头很大。痛点 4 条:
┌────────────────────────────────────────────────────────┐
│ Maven 的 4 个痛 │
├────────────────────────────────────────────────────────┤
│ 1. XML 太啰嗦:500 行 pom.xml 就为了配 3 个插件 │
│ 2. 扩展能力差:想加点自定义逻辑要写 Mojo 插件 │
│ (等于"改 build 脚本"先去开发个 Java 项目) │
│ 3. 多模块项目难:parent pom + 子 pom + 配置继承乱七八糟 │
│ 4. 增量构建几乎没有,改一行重跑全套 │
└────────────────────────────────────────────────────────┘他调研了一圈:
- 继续 Ant:太底层,啥都得自己写
- 继续 Maven:扩展性硬伤,XML 反人类
- 造一个全新的:Build by Convention,但让脚本是真正的代码,需要扩展时随便加几行
最终结论是 "那不如自己造一个"。2007 年立项,2012 年发布 1.0。语言选了 Groovy(动态、简洁、跑在 JVM、对 Java 程序员友好)。
1.1.4 Google 为什么钦定它?
2013 年 Google I/O 大会震惊业界 —— 安卓官方构建工具不再是 Ant 或 Maven,Gradle 成为 Android Studio 一等公民。背后原因:
原因 1:Android 工程"魔鬼般"的复杂度
──────────────────────────────────────────────
一个 Android 项目要:
- 多模块(app、library、feature)
- 多变体(debug、release、internal、prod、free、paid)
- 多产物(APK、AAB、AAR、APK 拆分按 ABI / 屏幕密度)
- 每个变体不同的依赖、签名、混淆配置
Maven 的"多 pom 继承"在这种复杂度下基本爆炸。
原因 2:Gradle 的扩展性匹配 Android 的演进速度
──────────────────────────────────────────────
AGP(Android Gradle Plugin)每年要加新功能(Jetpack Compose、
App Bundle、Kotlin、KSP 等等)。用 Gradle 可以快速演进;
用 Maven 估计要被 Google 自己扩死。于是 Gradle 一举成为「Android 强制标配 + Java/Kotlin 后端可选 + 多语言通吃」的多面手。
1.2 Gradle 是什么:一句话讲清
Gradle 是一款基于 Groovy / Kotlin DSL、面向 JVM 生态的、支持增量构建和强大依赖管理的开源构建工具。
这句话信息量很大,逐词拆开看:
| 词 | 含义 | 通俗类比 |
|---|---|---|
| 构建工具 | 把源码编译成可发布产物的自动化流水线 | 厨房里的"炒菜机器人":放进食材 → 出菜 |
| 基于 DSL | Build 脚本是 Groovy / Kotlin 代码,不是 XML/JSON | 食谱可以写 if/for,不是死板的清单 |
| 面向 JVM 生态 | 主要服务 Java / Kotlin / Android 项目 | 也能扩展到 C++/Swift/Python,但 JVM 是主战场 |
| 增量构建 | 没改的部分不重新跑 | 上顿没吃完的米饭不用再煮一遍 |
| 依赖管理 | 自动下载 + 管理第三方库 + 解决冲突 | 自动跑超市买菜,还能解决"两包同样的菜留新鲜的" |
| 开源 | Apache 2.0 | / |
1.2.1 它是怎么跑起来的?
构建脚本
┌────────────────────┐ ┌────────────────────┐
│ settings.gradle.kts │ │ build.gradle.kts │
│ (声明:包含哪些子项目)│ │ (声明:编译/依赖/任务) │
└─────────┬──────────┘ └──────────┬──────────┘
│ │
▼ ▼
┌──────────────────────────────────────────────┐
│ Gradle (JVM 进程) │
│ ① Initialization 阶段:读 settings │
│ ② Configuration 阶段:执行所有 build 脚本 │
│ → 构造 Task 对象图(DAG) │
│ ③ Execution 阶段:按 DAG 顺序执行用户要的 Task │
└─────────────────────┬────────────────────────┘
│
┌─────────────────────────┼─────────────────────────┐
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ compileJava │ ──────► │ test │ ───────► │ jar │
│ (编译源码) │ │ (跑单测) │ │ (打包 jar) │
└──────────────┘ └──────────────┘ └──────────────┘
每个 Task 有 input/output 指纹,不变就跳过关键事实:Gradle 不是把 build.gradle 当配置文件读 —— 而是把它当 Groovy/Kotlin 代码执行,运行的副作用就是"构造出一个 Task 对象图",然后按图执行。理解这一点,是理解 Gradle 的钥匙。
1.3 Gradle 三大杀手锏
讲了那么多背景,我们看 Gradle 真正"打动人心"的 3 个特性。也是面试官最爱问的"为什么 Gradle 比 Maven 好"。
1.3.1 杀手锏 1:DSL 脚本 —— 可编程的构建
痛点:Maven 的 XML 地狱
xml
<!-- Maven pom.xml 一段 —— 想给某个 profile 加个自定义动作?只能写 Mojo 插件 -->
<profiles>
<profile>
<id>release</id>
<build>
<plugins>
<plugin>
<groupId>com.example</groupId>
<artifactId>my-custom-plugin</artifactId>
<version>1.0.0</version>
<executions>
<execution>
<id>generate-stuff</id>
<phase>compile</phase>
<goals><goal>generate</goal></goals>
<configuration>
<inputDir>${project.basedir}/src/proto</inputDir>
<outputDir>${project.build.directory}/generated</outputDir>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<!-- 30 行 XML 才说"在 release profile 里跑一下我那个 plugin" -->Gradle 怎么救你
kotlin
// build.gradle.kts —— Kotlin DSL,3 行搞定相同需求
tasks.register<Copy>("generateStuff") {
from("src/proto")
into(layout.buildDirectory.dir("generated"))
}
tasks.named("compileJava") { dependsOn("generateStuff") }更厉害的是,它就是代码。可以写 if、for、函数:
kotlin
val features = listOf("auth", "billing", "notification")
features.forEach { feature ->
tasks.register("build-$feature") {
doLast { println("Building module: $feature") }
}
}
// 一段 forEach 就生成了 3 个 task,Maven 同样需求要 3 段 XML 复制粘贴📌 生活化类比:Maven 像是"必须按公司的固定模板填报销单",Gradle 像是"给你 Excel 让你自己写公式",灵活度不在一个量级。
1.3.2 杀手锏 2:增量构建 —— 没改就别重跑
痛点:Maven 的"全量编译"
Maven 的世界观:
$ mvn compile → 我不知道你改了什么,全部重新编译一遍吧
$ mvn test → 我也不知道你改了什么,全部测试再跑一遍
一个 100 模块项目,改一行代码 → 等 5 分钟Gradle 怎么救你
Gradle 把每个 Task 的 输入(input)和输出(output)都做了指纹(hash):
第一次构建 compileJava:
┌─────────────────────────────────────────────┐
│ input = src/main/java/**/*.java 的 hash │
│ output = build/classes/**/*.class 的 hash │
│ → 计算 → 编译 → 写入磁盘 │
└─────────────────────────────────────────────┘
第二次构建(你只改了 src/main/java/Foo.java):
┌─────────────────────────────────────────────┐
│ input 指纹变了 │
│ → 编译 → 写新的 output │
│ 其他所有 Task:input 没变 → SKIP │
└─────────────────────────────────────────────┘实际效果:
$ ./gradlew build
> Task :compileJava UP-TO-DATE ← 没变,跳过
> Task :processResources UP-TO-DATE ← 没变,跳过
> Task :classes UP-TO-DATE
> Task :jar UP-TO-DATE
> Task :test UP-TO-DATE
> Task :check UP-TO-DATE
> Task :build UP-TO-DATE
BUILD SUCCESSFUL in 800ms📌 生活化类比:Maven 像是"每天上班都把家里所有东西打包带走",Gradle 像是"只带今天用得到的,剩下的留家里"。100 个东西改了 1 个 → Gradle 只处理那 1 个。
1.3.3 杀手锏 3:依赖管理 —— 跨项目自动拉 + 自动解冲突
痛点:Ant 的"手动放 jar"
1. 去网上搜 "log4j-1.2.17.jar"
2. 下载到本地
3. 拷贝到项目的 lib/ 目录
4. 在 build.xml 里加 <classpath><pathelement location="lib/log4j-1.2.17.jar"/></classpath>
5. 这个库依赖了 commons-logging?再走一遍 1-4 步
6. commons-logging 又依赖了 commons-lang?再走一遍 1-4 步
...
一个项目可能要手动放 30 个 jar。换电脑?再来一遍。Gradle 怎么救你
kotlin
// build.gradle.kts
repositories {
mavenCentral() // 告诉 Gradle 去哪里下载
}
dependencies {
implementation("org.apache.logging.log4j:log4j-core:2.20.0")
// 一行搞定。Gradle 会自动:
// 1. 下载 log4j-core-2.20.0.jar
// 2. 解析 pom.xml,发现它依赖 log4j-api → 自动下载
// 3. 放进本地缓存(~/.gradle/caches)
// 4. 同事拉项目时复用缓存,不用重新下
}更牛的是冲突自动解决:
你的项目依赖:
├─ moduleA → guava:30.0
└─ moduleB → guava:32.0
Gradle 默认策略:用最高版本(32.0)→ 自动选定
想强制用 30.0?一行 force:
configurations.all {
resolutionStrategy.force("com.google.guava:guava:30.0")
}📌 生活化类比:手动 jar 像是"想吃西红柿炒鸡蛋要先种西红柿、养鸡、生火";Gradle 依赖管理像是"开美团外卖,下单 → 食材送到门口 → 厨师都给你配好了"。
1.4 Gradle vs Maven 全方位对比
| 维度 | Maven | Gradle | 谁赢 |
|---|---|---|---|
| 描述方式 | XML | Groovy / Kotlin DSL | Gradle 大胜(灵活) |
| 模板代码 | 海量 | 极少 | Gradle 大胜 |
| 增量构建 | 无 | input/output 指纹 | Gradle 大胜 |
| 构建缓存 | 无 | 本地 + 远程 | Gradle 大胜 |
| 并行任务 | 弱 | 任务级并行 + 项目级并行 | Gradle 略胜 |
| 学习曲线 | 平缓 | 较陡 | Maven 略胜 |
| 上手速度 | 快 | 慢 | Maven 胜 |
| 生态成熟度 | 30 年历史 | 15 年历史 | Maven 略胜 |
| Java 后端默认 | 是 | 越来越多用 | 平手 |
| Android 默认 | ❌ | ✅ Google 钦定 | Gradle 大胜 |
| 错误信息友好度 | 还行 | 著名啰嗦 | Maven 略胜 |
| 性能(热构建) | 一般 | 极快(增量+缓存) | Gradle 大胜 |
1.4.1 一段配置看尽差距
需求:定义一个 Java 项目,编译用 JDK 17,依赖 Spring Boot Web 3.2.0,跑 JUnit 5 测试。
Maven 版本(pom.xml):
xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>1.0.0</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>3.2.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>3.2.0</version>
</plugin>
</plugins>
</build>
</project>Gradle 版本(build.gradle.kts):
kotlin
plugins {
java
id("org.springframework.boot") version "3.2.0"
id("io.spring.dependency-management") version "1.1.4"
}
group = "com.example"
version = "1.0.0"
java { toolchain { languageVersion = JavaLanguageVersion.of(17) } }
repositories { mavenCentral() }
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}
tasks.test { useJUnitPlatform() }同一个项目,Maven 36 行 XML,Gradle 16 行 Kotlin,可读性还高得多。
1.5 Gradle 适用场景全景图
★ Gradle 全场景能力雷达 ★
Android 构建 ⭐⭐⭐⭐⭐
│
Java 后端 │ Kotlin 项目
⭐⭐⭐⭐ ─────┼──── ⭐⭐⭐⭐⭐
│
───────── │ ─────────
│
多模块 │ CI/CD 流水线
工程 │ ⭐⭐⭐⭐
⭐⭐⭐⭐ │
│
C++ / Swift 项目
⭐⭐1.5.1 主战场 1:Android 开发
- 强制标配:Android Studio = Gradle + Android Gradle Plugin (AGP)
- 特点:变体管理(build types + product flavors)、签名、混淆、APK 拆分等深度集成
- 命令:
./gradlew assembleDebug/./gradlew installRelease
1.5.2 主战场 2:Java / Kotlin 后端
- Spring Boot:从 2.0 开始官方同时支持 Maven + Gradle,
start.spring.io默认就提供 Gradle - Ktor / Micronaut / Quarkus:原生 Gradle 优先
- 优势:构建快、Kotlin DSL 类型安全、自定义任务方便(如生成代码、跑数据库迁移)
1.5.3 主战场 3:多模块工程
- 典型场景:一个仓库里 50 个 module(domain、data、ui、feature-x、feature-y…)
- Convention Plugin 模式:把"所有 Android library 都需要的配置"抽到 buildSrc/build-logic 里复用
- Composite Build:把另一个 Git 项目当源码依赖进来调试
1.5.4 跨界场景:非 JVM 语言
- C++:原生 plugin 支持
- Swift / Objective-C:通过插件支持
- Kotlin Multiplatform:iOS / JS / Native 全靠 Gradle 编排
1.6 学 Gradle 之前你需要知道的 5 件事
1.6.1 你需要 Java / Kotlin 基础吗?
不严格需要语言精通,但要有一点编程基础。 Gradle 脚本 ≈ "受限的 Groovy / Kotlin":基本就是赋值、调方法、传闭包/Lambda。
| 概念 | 类比 |
|---|---|
tasks.register("hello") { ... } | 调用一个方法 register,传两个参数(字符串 + 闭包) |
dependencies { implementation(...) } | 给 dependencies 这个对象配置内部状态 |
project.version = "1.0" | 给 project 对象的 version 属性赋值 |
1.6.2 项目里有哪些核心文件?
my-project/
├── settings.gradle.kts ← ① 入口:定义项目名、包含哪些子模块
├── build.gradle.kts ← ② 主脚本:依赖、插件、任务都写这里
├── gradle.properties ← ③ 全局属性(如内存大小、Kotlin 版本)
├── gradle/
│ ├── wrapper/
│ │ ├── gradle-wrapper.jar ← ④ Wrapper 启动器(必须提交到 git)
│ │ └── gradle-wrapper.properties ← ⑤ 锁定 Gradle 版本
│ └── libs.versions.toml ← ⑥ Version Catalog(统一管理依赖版本)
├── gradlew ← ⑦ Unix 启动脚本
├── gradlew.bat ← ⑧ Windows 启动脚本
└── src/main/...1.6.3 Groovy DSL 还是 Kotlin DSL?
┌─────────────────────────────────────────────────────────┐
│ Groovy DSL (.gradle) Kotlin DSL (.gradle.kts) │
│ ──────────────────────── ───────────────────────── │
│ + 脚本短一些 + IDE 智能提示完整 │
│ + 老项目很多 + 类型安全,错误提前到编译期 │
│ - IDE 提示弱 + 重构友好(改属性名能跟踪) │
│ - 错误运行时才发现 - 脚本稍长(要写括号、引号) │
│ - 首次启动稍慢(要编译 .kts) │
└─────────────────────────────────────────────────────────┘
小白建议:直接学 Kotlin DSL(本笔记主线)
面试 / 接老项目:Groovy DSL 也得能看懂1.6.4 为什么 Gradle 这么慢?
首次启动慢是真的。原因:
- 启动 JVM(Gradle 跑在 JVM 上)—— 大约 1-2 秒
- 编译 build.gradle.kts —— 大约 3-5 秒(Groovy DSL 几乎瞬时)
- 解析依赖 —— 视项目和网络
- 执行 Task —— 视任务量
热启动很快:Gradle Daemon 把 JVM 留在后台,第二次跑只剩 200-500ms。第 9 章会讲怎么把构建优化到极致。
1.6.5 学完后你需要继续学的方向
┌──────────────────────────────────────────────────────────┐
│ 方向 A:Android → AGP / Gradle Managed Devices │
│ 方向 B:后端 → Spring Boot Plugin / Jib(容器化) │
│ 方向 C:工程化 → Convention Plugin / Composite Build │
│ 方向 D:性能 → Develocity(Build Scan、远程缓存) │
└──────────────────────────────────────────────────────────┘1.7 章末小结
记住这张图,第 1 章你就毕业了:
★ 第 1 章核心知识图谱 ★
│
┌─────────────────────────┼──────────────────────────┐
│ │ │
┌────▼─────┐ ┌────▼──────┐ ┌────▼────┐
│ 是什么 │ │ 三大杀手锏 │ │ 适用场景 │
├──────────┤ ├───────────┤ ├─────────┤
│ JVM 工具 │ │ DSL 脚本 │ │ Android │
│ DSL 驱动 │ │ 增量构建 │ │ 后端 │
│ 多语言 │ │ 依赖管理 │ │ 多模块 │
│ 可扩展 │ │ │ │ CI/CD │
└──────────┘ └───────────┘ └─────────┘🎤 1.8 章末面试题(10 道高频题)
Q1. Gradle 跟 Maven 的本质区别是什么?
答:3 个核心区别:
- 描述方式:Maven 用 XML(声明式),Gradle 用 Groovy/Kotlin DSL(脚本式,可编程) —— 灵活度高一个量级;
- 增量构建:Maven 几乎没有,每次基本全量;Gradle 通过 input/output 指纹实现真正的增量;
- 构建缓存:Maven 没有,Gradle 有本地 + 远程构建缓存,可以跨电脑、跨 CI 复用编译产物。
辅助点:Gradle 兼容 Maven Central 仓库,所以依赖生态共享,不用从头建。
Q2. Google 为什么选 Gradle 作为 Android 构建工具?
答:Android 工程的复杂度(多模块 × 多 buildType × 多 productFlavor × 多 ABI)让 Maven 力不从心:
- 变体管理:Android 一个项目可能产出十几个 APK,Maven 的 profile 模型撑不住;
- 扩展性:Android Gradle Plugin 每年要加新功能(Compose、Hilt、KSP),Gradle 的可编程 DSL 让 Google 能快速演进;
- 增量构建:Android 工程动辄百个 module,没有增量根本没法用;
- 生态势能:JetBrains(Kotlin 母公司)和 Google 都押注 Gradle,形成正循环。
Q3. Gradle 解决了 Maven 的哪些核心痛点?
答:
| 痛点 | Maven 现状 | Gradle 解法 |
|---|---|---|
| XML 啰嗦 | pom.xml 动辄数百行 | DSL,相同需求短 50% |
| 扩展难 | 写 Mojo 插件要单独工程 | tasks.register { ... } 几行就能扩展 |
| 增量构建 | 几乎没有 | input/output 指纹机制 |
| 多模块继承 | parent pom 嵌套混乱 | settings.gradle + Convention Plugin 优雅 |
| 构建慢 | 全量编译 | Daemon + Build Cache + Configuration Cache |
Q4. build.gradle.kts 是配置文件吗?
答:它是 Kotlin 代码,不是配置文件。Gradle 启动时会把它当 Kotlin 脚本编译并执行,执行的副作用就是"配置好了一个 Project 对象 + 一组 Task 对象"。理解这一点是从"复制粘贴工程师"进阶的分水岭。
Q5. Gradle 的构建生命周期分哪几个阶段?每个阶段做什么?
答:3 个阶段:
- Initialization(初始化):读
settings.gradle.kts,决定有哪些 Project(多模块项目这一步建立项目层级); - Configuration(配置):执行所有 Project 的
build.gradle.kts,构造出所有 Task 对象和它们之间的依赖关系(一个有向无环图 DAG); - Execution(执行):从用户指定的 Task 开始反向追踪 DAG,按拓扑序依次执行需要的 Task。
⚠️ 关键点:
dependencies { ... }、tasks.register { ... }这些都在配置阶段执行,不是执行阶段。这是初学者最易混淆的点。
Q6. 为什么必须用 ./gradlew 而不是全局 gradle?
答:3 个原因:
- 版本一致性:wrapper 锁定项目使用的 Gradle 版本,避免"同事电脑上 8.5、我电脑上 7.2"导致行为不一致;
- 零安装:新成员/CI 拉项目后无需手动装 Gradle,
./gradlew会自动下载锁定版本; - 可重现构建:CI 平台和本地一致,避免"在我电脑上能跑"的悲剧。
⚠️ wrapper 相关文件(
gradle/wrapper/*、gradlew、gradlew.bat)必须提交到 Git。
Q7. Gradle 增量构建的原理是什么?
答:每个 Task 声明自己的 input(如源代码目录)和 output(如 .class 文件目录)。Gradle 在每次执行后会把它们的 hash 指纹 存入 .gradle/ 目录。下次执行时:
- input 和 output 指纹都没变 → 标
UP-TO-DATE,直接跳过; - input 变了 → 重新执行 Task,更新 output 指纹;
- output 被外部改了 → 也会重新执行(保护正确性)。
这种"声明式输入输出 + 指纹比对"的机制叫 incremental build。Build Cache 在此基础上更进一步:连指纹相同时的 output 都能从远程缓存里拉。
Q8. Gradle 跟 Ant 是什么关系?
答:Gradle 内部支持 Ant Task,可以直接调用任何 Ant 任务(ant.echo("Hello"))。但 Gradle 的设计哲学是"约定优于配置 + 可编程脚本",跟 Ant 的"完全自由 + 啥都自己写"截然不同。
历史上 Ant 是 90% Java 项目的构建工具,被 Maven 和 Gradle 联手干掉。今天看到 Ant 项目基本是要重写或迁移的"古董"。
Q9. Groovy DSL 和 Kotlin DSL 该选哪个?
答:新项目首选 Kotlin DSL,理由:
- 类型安全:写错属性名 IDE 立即标红,不用等运行时;
- IDE 提示完整:Groovy DSL 在 IDE 里很多地方提示不出来,要靠记;
- 重构友好:改 task 名 / 属性名,IDE 能跟踪所有引用;
- 未来方向:Gradle 官方文档默认双语,但优先级在向 Kotlin DSL 倾斜。
老项目继续用 Groovy 也 OK:迁移成本不低(要重写每个 .gradle),不必为迁而迁。但新模块强烈建议直接用 .gradle.kts。
Q10. 如果你已经会 Maven,学 Gradle 大概要多久?
答:
- 基础上手(能写 build.gradle、跑 build/test/run):1-2 天
- 流畅写脚本(含自定义 Task / 调依赖配置):1 周
- 写出"地道 Gradle"(含生命周期理解、Provider/Property API):1 个月
- 多模块 + 性能优化精通(含 Convention Plugin、Build Cache、Configuration Cache):3 个月
💡 关键点:会写 Gradle 不等于会"写好的 Gradle"。前期容易"用 Maven 思维写 Gradle",比如把所有配置都堆
subprojects {}、滥用afterEvaluate {}、不声明 input/output 等。靠多读优秀开源项目(NowInAndroid、Spring 官方示例)能加速成长。
下一章 → 第 2 章 · 环境搭建 & Wrapper & 第一个项目 →
🎬 可视化演示
演示加载缓慢或样式异常?点此在新标签页打开 ↗
💻 示例代码
txt
/*
* 第 1 章 · Gradle 是什么 — 第一个 build.gradle.kts 示例
*
* 这是一个最小可跑的 Java 项目构建脚本,对照注释体会 Gradle DSL 的"声明 + 可编程"特性。
*
* 运行方式:
* 1. 进入本目录
* 2. 把这个文件叫 build.gradle.kts,并放一个 settings.gradle.kts 到同目录
* 3. ./gradlew build —— 编译 + 测试 + 打 jar
* 4. ./gradlew tasks —— 查看所有可用任务
* 5. ./gradlew hello —— 跑下面那个自定义的 hello 任务
* 6. ./gradlew greet —— 跑批量生成的任务之一
*
* 没有 wrapper?看第 2 章会教你怎么生成。
*/
// ============= 1. 应用插件 =============
// 插件给项目加上"能力":java 插件让本项目可以编译 Java 代码
plugins {
java
}
group = "com.example.gradle.intro"
version = "1.0.0"
// ============= 2. 配置 Java 编译 =============
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
// ============= 3. 配置仓库 =============
// 告诉 Gradle 去哪儿下载依赖。mavenCentral 是公认的全球最大 Java 库仓库
repositories {
mavenCentral()
}
// ============= 4. 声明依赖 =============
dependencies {
// 主代码用的依赖
implementation("com.google.guava:guava:32.1.3-jre")
// 测试代码才用的依赖
testImplementation("org.junit.jupiter:junit-jupiter:5.10.0")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}
// ============= 5. 配置已有任务 =============
tasks.test {
useJUnitPlatform()
}
// ============= 6. 自定义一个 Task =============
// 注册一个 "hello" 任务:跑 ./gradlew hello 时打印一句话
tasks.register("hello") {
group = "intro"
description = "第 1 章演示:打印 Hello Gradle"
doLast {
println("===========================================")
println(" Hello Gradle! 这就是一个最简单的 Task")
println(" 当前项目:" + project.name + ",版本:" + project.version)
println("===========================================")
}
}
// ============= 7. 用循环批量生成任务(DSL = 代码) =============
// 这就是 Gradle 比 Maven 强的核心:build 脚本是 Kotlin 代码
val features = listOf("auth", "billing", "notification")
features.forEach { feature ->
tasks.register("greet-" + feature) {
group = "intro"
description = "对 " + feature + " 模块说 Hello"
doLast {
println("👋 Hello, " + feature + " module!")
}
}
}
// ============= 8. 一个聚合任务 =============
// 跑 ./gradlew greet 会依次跑 greet-auth、greet-billing、greet-notification
tasks.register("greet") {
group = "intro"
description = "向所有 feature 模块打招呼"
dependsOn(features.map { "greet-" + it })
}markdown
# Maven vs Gradle 对照表(第 1 章配套阅读)
> 这是一份"看到 Maven 配置,想知道 Gradle 怎么写"的速查表。可以在迁移老项目时直接对照。
---
## 1. 项目坐标 & JDK 版本
### Maven
```xml
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>1.0.0</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>Gradle (Kotlin DSL)
kotlin
group = "com.example"
version = "1.0.0"
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}2. 依赖仓库
Maven
xml
<repositories>
<repository>
<id>central</id>
<url>https://repo.maven.apache.org/maven2</url>
</repository>
</repositories>Gradle
kotlin
repositories {
mavenCentral()
// 可选:Google Maven、私服等
// google()
// maven("https://my-private-repo.example.com")
}3. 依赖声明
| Maven scope | Gradle 配置 | 说明 |
|---|---|---|
compile(已废弃) | implementation | 编译期 + 运行期可见,不传递给消费者 |
compile + 暴露 | api | 同 implementation 但会传递给消费者 |
provided | compileOnly | 只编译期可见,不打进 jar(如 Servlet API) |
runtime | runtimeOnly | 只运行期需要,不参与编译(如 JDBC 驱动) |
test | testImplementation | 仅测试代码可见 |
Maven
xml
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>6.1.1</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.0</version>
<scope>test</scope>
</dependency>
</dependencies>Gradle
kotlin
dependencies {
implementation("org.springframework:spring-core:6.1.1")
testImplementation("org.junit.jupiter:junit-jupiter:5.10.0")
}4. 依赖排除(exclude)
Maven
xml
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>6.1.1</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>Gradle
kotlin
implementation("org.springframework:spring-core:6.1.1") {
exclude(group = "commons-logging", module = "commons-logging")
}5. 强制版本
Maven
xml
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>32.0.0-jre</version>
</dependency>
</dependencies>
</dependencyManagement>Gradle
kotlin
configurations.all {
resolutionStrategy.force("com.google.guava:guava:32.0.0-jre")
}
// 或更现代的写法
dependencies {
implementation("com.google.guava:guava") {
version { strictly("32.0.0-jre") }
}
}6. 插件应用
Maven
xml
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>3.2.0</version>
</plugin>
</plugins>
</build>Gradle
kotlin
plugins {
id("org.springframework.boot") version "3.2.0"
}7. 多模块
Maven
xml
<!-- 根 pom.xml -->
<modules>
<module>app</module>
<module>lib</module>
</modules>Gradle
kotlin
// settings.gradle.kts
include("app", "lib")模块间引用:
| Maven | Gradle |
|---|---|
<dependency><groupId>com.example</groupId><artifactId>lib</artifactId></dependency> | implementation(project(":lib")) |
8. 常用命令
| 用途 | Maven | Gradle |
|---|---|---|
| 清理 | mvn clean | ./gradlew clean |
| 编译 | mvn compile | ./gradlew classes |
| 测试 | mvn test | ./gradlew test |
| 打包 | mvn package | ./gradlew jar 或 ./gradlew assemble |
| 完整构建 | mvn install | ./gradlew build |
| 看依赖树 | mvn dependency:tree | ./gradlew dependencies |
| 看任务列表 | mvn help:describe -Dplugin=… | ./gradlew tasks |
| 跑应用 | mvn spring-boot:run | ./gradlew bootRun |
| 跳过测试 | mvn install -DskipTests | ./gradlew build -x test |
| 离线构建 | mvn -o install | ./gradlew --offline build |
| 调试输出 | mvn -X | ./gradlew --debug |
9. 自定义任务
Maven
必须开发独立的 Mojo Plugin(一个完整的 Java 项目),发布到本地或远程仓库才能用。约 200 行代码起步。
Gradle
当场就能写:
kotlin
tasks.register("countLines") {
doLast {
val total = fileTree("src/main/java")
.matching { include("**/*.java") }
.sumOf { it.readLines().size }
println("Total Java lines: $total")
}
}
// 跑:./gradlew countLines总结
| 维度 | Maven | Gradle |
|---|---|---|
| 学习曲线 | 平缓,背几个标签就能写 | 较陡,要理解生命周期 |
| 灵活度 | 低(要扩展必须开发插件) | 极高(脚本本身是代码) |
| 啰嗦度 | 高(XML) | 低(DSL) |
| 性能 | 一般,无增量 | 极快(增量 + 缓存 + Daemon) |
| 何时用 Maven | 项目简单、团队不熟 Gradle | / |
| 何时用 Gradle | Android、复杂工程、性能敏感 | 几乎所有现代 JVM 项目 |
```txt [settings.gradle.kts]
/*
* settings.gradle.kts —— Gradle 项目的"入口"
*
* 它告诉 Gradle:本项目叫什么、包含哪些子模块。
* 即使是单模块项目,这个文件也是必须的。没有它 Gradle 会向上层目录查找。
*/
rootProject.name = "ch01-intro-demo"
// 单模块项目,这里不需要 include 任何子模块
// 多模块项目会写:include("app", "lib", "feature-a")build.gradle.kts ↗ · MavenVsGradle.md ↗ · settings.gradle.kts ↗