Skip to content

第 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 生态的、支持增量构建和强大依赖管理的开源构建工具。

这句话信息量很大,逐词拆开看:

含义通俗类比
构建工具把源码编译成可发布产物的自动化流水线厨房里的"炒菜机器人":放进食材 → 出菜
基于 DSLBuild 脚本是 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 全方位对比

维度MavenGradle谁赢
描述方式XMLGroovy / Kotlin DSLGradle 大胜(灵活)
模板代码海量极少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 这么慢?

首次启动慢是真的。原因:

  1. 启动 JVM(Gradle 跑在 JVM 上)—— 大约 1-2 秒
  2. 编译 build.gradle.kts —— 大约 3-5 秒(Groovy DSL 几乎瞬时)
  3. 解析依赖 —— 视项目和网络
  4. 执行 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 个核心区别:

  1. 描述方式:Maven 用 XML(声明式),Gradle 用 Groovy/Kotlin DSL(脚本式,可编程) —— 灵活度高一个量级;
  2. 增量构建:Maven 几乎没有,每次基本全量;Gradle 通过 input/output 指纹实现真正的增量;
  3. 构建缓存:Maven 没有,Gradle 有本地 + 远程构建缓存,可以跨电脑、跨 CI 复用编译产物。

辅助点:Gradle 兼容 Maven Central 仓库,所以依赖生态共享,不用从头建。


Q2. Google 为什么选 Gradle 作为 Android 构建工具?

:Android 工程的复杂度(多模块 × 多 buildType × 多 productFlavor × 多 ABI)让 Maven 力不从心:

  1. 变体管理:Android 一个项目可能产出十几个 APK,Maven 的 profile 模型撑不住;
  2. 扩展性:Android Gradle Plugin 每年要加新功能(Compose、Hilt、KSP),Gradle 的可编程 DSL 让 Google 能快速演进;
  3. 增量构建:Android 工程动辄百个 module,没有增量根本没法用;
  4. 生态势能: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 个阶段:

  1. Initialization(初始化):读 settings.gradle.kts,决定有哪些 Project(多模块项目这一步建立项目层级);
  2. Configuration(配置):执行所有 Project 的 build.gradle.kts,构造出所有 Task 对象和它们之间的依赖关系(一个有向无环图 DAG);
  3. Execution(执行):从用户指定的 Task 开始反向追踪 DAG,按拓扑序依次执行需要的 Task

⚠️ 关键点:dependencies { ... }tasks.register { ... } 这些都在配置阶段执行,不是执行阶段。这是初学者最易混淆的点。


Q6. 为什么必须用 ./gradlew 而不是全局 gradle

:3 个原因:

  1. 版本一致性:wrapper 锁定项目使用的 Gradle 版本,避免"同事电脑上 8.5、我电脑上 7.2"导致行为不一致;
  2. 零安装:新成员/CI 拉项目后无需手动装 Gradle,./gradlew 会自动下载锁定版本;
  3. 可重现构建:CI 平台和本地一致,避免"在我电脑上能跑"的悲剧。

⚠️ wrapper 相关文件(gradle/wrapper/*gradlewgradlew.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,理由:

  1. 类型安全:写错属性名 IDE 立即标红,不用等运行时;
  2. IDE 提示完整:Groovy DSL 在 IDE 里很多地方提示不出来,要靠记;
  3. 重构友好:改 task 名 / 属性名,IDE 能跟踪所有引用;
  4. 未来方向: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 scopeGradle 配置说明
compile(已废弃)implementation编译期 + 运行期可见,不传递给消费者
compile + 暴露api同 implementation 但会传递给消费者
providedcompileOnly只编译期可见,不打进 jar(如 Servlet API)
runtimeruntimeOnly只运行期需要,不参与编译(如 JDBC 驱动)
testtestImplementation仅测试代码可见

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")

模块间引用:

MavenGradle
<dependency><groupId>com.example</groupId><artifactId>lib</artifactId></dependency>implementation(project(":lib"))

8. 常用命令

用途MavenGradle
清理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

总结

维度MavenGradle
学习曲线平缓,背几个标签就能写较陡,要理解生命周期
灵活度低(要扩展必须开发插件)极高(脚本本身是代码)
啰嗦度高(XML)低(DSL)
性能一般,无增量极快(增量 + 缓存 + Daemon)
何时用 Maven项目简单、团队不熟 Gradle/
何时用 GradleAndroid、复杂工程、性能敏感几乎所有现代 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 ↗