- Gradle 到底负责什么,和 Maven、Ant 有什么区别。
- 一个 Gradle 项目里
settings.gradle、build.gradle、gradle.properties分别干什么。 - Task、Plugin、Dependency、Repository 这些核心概念如何配合完成一次构建。
- Groovy DSL 和 Kotlin DSL 的写法差异在哪里。
- Gradle Wrapper、Toolchain、User Home、增量构建分别解决什么问题。
- 如何诊断构建问题(调试参数、日志级别)。
1. Gradle 是什么
Gradle 是一个构建自动化工具。构建不是简单的"编译代码",而是一组稳定、可重复的工程动作:
- 下载依赖。
- 编译源码。
- 处理资源。
- 执行测试。
- 生成构建产物。
- 发布产物。
- 执行代码检查、安全扫描、版本生成等自动化动作。
Gradle 的核心特点是"声明式配置 + 可编程扩展"。你可以像 Maven 一样声明依赖和插件,也可以用 Groovy 或 Kotlin 写逻辑扩展构建流程。
Gradle 解决的核心问题
| 问题 | Gradle 怎么解决 |
|---|---|
| 依赖管理复杂 | 声明式依赖 + 自动解析传递依赖 |
| 构建步骤繁琐 | Task 图 + 插件标准化常用流程 |
| 多模块协作困难 | 原生多模块支持 |
| 构建太慢 | 增量构建 + 构建缓存 + 并行执行 |
| 团队规范不统一 | Convention Plugin 统一配置 |
2. Gradle 和 Maven、Ant 的区别
| 对比项 | Ant | Maven | Gradle |
|---|---|---|---|
| 配置语言 | XML | XML | Groovy DSL 或 Kotlin DSL |
| 构建模型 | 命令式 task | 固定生命周期 | 有向无环图(DAG)task |
| 生命周期 | 完全自定义 | 固定生命周期更强 | task 图更灵活 |
| 扩展能力 | 完全自由但无规范 | 插件扩展为主 | 插件、task、脚本、构建逻辑都可扩展 |
| 性能能力 | 无内置缓存 | 相对传统 | 增量构建、构建缓存、配置缓存支持更强 |
| 依赖管理 | 无内置(需 Ivy) | 内置 Maven 仓库 | 内置,支持 Maven/Ivy/自定义仓库 |
| 学习成本 | 低入门、低天花板 | 入门较稳定 | 入门简单,深入后需要理解模型 |
核心理解:Maven 更像"按固定流程填表",Gradle 更像"给你默认流程,也允许你改造流水线"。Ant 则是"完全空白画布,你自己定义所有步骤"。
3. 使用 gradle init 初始化项目
Gradle 内置 init 命令,可以交互式创建项目骨架,是学习 Gradle 最快的起点。
bash
mkdir my-project && cd my-project
gradle init
gradle init 会提示选择:
- 项目类型:
application、library、Gradle plugin、basic - 实现语言:
Java、Kotlin、Groovy、Scala、C++、Swift - DSL 语言:
Groovy或Kotlin - 测试框架:
JUnit Jupiter、Spock、TestNG等
非交互模式(适合脚本):
bash
gradle init \
--type java-application \
--dsl groovy \
--test-framework junit-jupiter \
--package com.example \
--project-name my-app \
--no-incubating
生成后的典型结构:
text
my-app/
├── settings.gradle # 项目声明
├── build.gradle # 构建脚本
├── gradle.properties # 构建参数
├── gradle/
│ ├── wrapper/ # Gradle Wrapper
│ └── libs.versions.toml # Version Catalog
├── gradlew # Unix 启动脚本
├── gradlew.bat # Windows 启动脚本
└── app/
└── src/
├── main/java/
└── test/java/
4. Gradle 项目基本结构
text
project-root/
├── settings.gradle # 项目声明
├── build.gradle # 构建脚本
├── gradle.properties # 构建参数
├── gradle/
│ └── libs.versions.toml # Version Catalog(Gradle 7.4+)
└── src/
├── main/java
└── test/java
settings.gradle
settings.gradle 描述"这个构建里有哪些项目"。单模块工程通常只设置根项目名,多模块工程会通过 include 注册子模块。
groovy
pluginManagement {
repositories {
gradlePluginPortal()
mavenCentral()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
mavenCentral()
}
}
rootProject.name = 'hello-gradle'
include 'app'
Kotlin DSL 版本(settings.gradle.kts):
kotlin
pluginManagement {
repositories {
gradlePluginPortal()
mavenCentral()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
mavenCentral()
}
}
rootProject.name = "hello-gradle"
include("app")
build.gradle
build.gradle 描述"项目如何构建"。它通常包含插件、依赖、任务、测试、打包等配置。
groovy
plugins {
id 'java'
id 'application'
}
repositories {
mavenCentral()
}
dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter:5.10.2'
}
application {
mainClass = 'com.example.Main'
}
test {
useJUnitPlatform()
}
gradle.properties
gradle.properties 用于放构建参数,例如 JVM 参数、并行构建、缓存开关。
properties
org.gradle.jvmargs=-Xmx2g -Dfile.encoding=UTF-8
org.gradle.parallel=true
org.gradle.caching=true
org.gradle.warning.mode=all
常用参数说明:
| 参数 | 作用 | 推荐值 |
|---|---|---|
org.gradle.jvmargs |
Gradle 守护进程 JVM 参数 | -Xmx2g -Dfile.encoding=UTF-8 |
org.gradle.parallel |
并行构建 | true(多模块项目) |
org.gradle.caching |
构建缓存 | true |
org.gradle.warning.mode |
警告显示级别 | all |
org.gradle.daemon |
是否使用 Daemon | true(默认) |
org.gradle.configuration-cache |
配置缓存 | true(Gradle 8.x) |
5. Groovy DSL vs Kotlin DSL
Gradle 支持两种 DSL:Groovy(.gradle)和 Kotlin(.gradle.kts)。
语法对比
| 特性 | Groovy DSL | Kotlin DSL |
|---|---|---|
| 文件扩展名 | .gradle |
.gradle.kts |
| 字符串 | 单引号或双引号 | 双引号 |
| 插件 id | id 'java' |
id("java") |
| 属性赋值 | mainClass = '...' |
mainClass.set("...") 或 = "..." |
| IDE 支持 | 有限 | IntelliJ 完整补全 |
| 类型安全 | 动态类型 | 静态类型 |
完整对比示例
Groovy DSL(build.gradle):
groovy
plugins {
id 'java'
id 'application'
}
group = 'com.example'
version = '1.0.0'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'com.google.guava:guava:33.2.1-jre'
testImplementation 'org.junit.jupiter:junit-jupiter:5.10.2'
}
application {
mainClass = 'com.example.Main'
}
test {
useJUnitPlatform()
}
Kotlin DSL(build.gradle.kts):
kotlin
plugins {
id("java")
id("application")
}
group = "com.example"
version = "1.0.0"
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(17))
}
}
repositories {
mavenCentral()
}
dependencies {
implementation("com.google.guava:guava:33.2.1-jre")
testImplementation("org.junit.jupiter:junit-jupiter:5.10.2")
}
application {
mainClass.set("com.example.Main")
}
tasks.test {
useJUnitPlatform()
}
推荐:新项目优先使用 Kotlin DSL,有更好的 IDE 自动补全和类型检查。
6. Gradle Wrapper
Wrapper 是团队项目中非常重要的机制。它把 Gradle 版本固定在项目里,避免每个人本机 Gradle 版本不同导致构建结果不同。
生成 Wrapper:
bash
gradle wrapper --gradle-version 8.8
生成后通常会出现:
text
gradlew
gradlew.bat
gradle/wrapper/gradle-wrapper.properties
gradle/wrapper/gradle-wrapper.jar
gradle-wrapper.properties 内容:
properties
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
团队成员直接执行:
bash
./gradlew clean test
升级 Wrapper 版本:
bash
./gradlew wrapper --gradle-version 8.10
# 提交 gradle-wrapper.properties 即可
最佳实践:
gradlew、gradlew.bat、gradle/wrapper/目录都要提交到 Git。- 不要把
gradle-wrapper.jar加入.gitignore。
7. Gradle User Home(缓存目录)
Gradle 会把下载的依赖、Wrapper、构建缓存存放在 GRADLE_USER_HOME 目录,默认为:
text
~/.gradle/
├── caches/ # 依赖缓存、构建缓存
├── wrapper/ # Gradle 各版本二进制
├── gradle.properties # 全局属性(所有项目生效)
└── init.d/ # 全局 init 脚本
查看缓存大小:
bash
du -sh ~/.gradle/caches
清理旧缓存(Gradle 8.x 内置):
bash
gradle --stop # 停止 Daemon
./gradlew clean # 清理构建输出
rm -rf ~/.gradle/caches/modules-*/files-* # 清理依赖缓存(谨慎)
全局 ~/.gradle/gradle.properties(对所有项目生效):
properties
# 所有项目通用代理配置
systemProp.http.proxyHost=proxy.example.com
systemProp.http.proxyPort=8080
systemProp.https.proxyHost=proxy.example.com
systemProp.https.proxyPort=8080
8. Task:Gradle 的最小执行单元
Gradle 中所有构建动作最终都会落到 task 上。例如:
compileJava:编译 Java 源码。processResources:处理资源文件。test:执行测试。jar:生成 jar 包。build:执行完整构建。
查看所有 task:
bash
gradle tasks
gradle tasks --all # 包含隐藏 task
查看某个 task 依赖了哪些 task:
bash
gradle build --dry-run
自定义一个 task:
groovy
tasks.register('helloGradle') {
group = 'learning'
description = 'Print a simple Gradle message'
doLast {
println 'Hello Gradle'
}
}
运行:
bash
gradle helloGradle
Task 依赖关系
dependsOn:A 执行前必须先执行 B。
groovy
tasks.register('packageAll') {
dependsOn 'compileJava', 'processResources'
doLast {
println 'Packaging...'
}
}
mustRunAfter:如果 A 和 B 都被请求,A 必须在 B 之后运行(不强制执行 B)。
groovy
tasks.named('test') {
mustRunAfter tasks.named('lint')
}
finalizedBy:A 执行完后(不管成功还是失败)必须执行 B。
groovy
tasks.named('test') {
finalizedBy tasks.named('generateReport')
}
shouldRunAfter:建议顺序,不强制。常见于 CI 排序优化。
9. 增量构建(UP-TO-DATE)
Gradle 会追踪每个 task 的输入和输出。如果自上次执行以来输入和输出都没变化,task 就会跳过,标记为 UP-TO-DATE。
bash
gradle compileJava # 第一次:EXECUTED
gradle compileJava # 第二次:UP-TO-DATE(跳过)
输出示例:
text
> Task :compileJava UP-TO-DATE
> Task :processResources UP-TO-DATE
> Task :classes UP-TO-DATE
如果想强制重新执行(忽略缓存和增量):
bash
gradle compileJava --rerun
自定义 task 要声明输入输出才能享受增量构建:
groovy
abstract class GenerateBuildInfo extends DefaultTask {
@Input
abstract Property<String> getVersionName()
@OutputFile
abstract RegularFileProperty getOutputFile()
@TaskAction
void generate() {
outputFile.get().asFile.text = "version=${versionName.get()}\nbuildTime=${new Date()}"
}
}
tasks.register('generateBuildInfo', GenerateBuildInfo) {
versionName = project.version.toString()
outputFile = layout.buildDirectory.file('generated/build-info.properties')
}
10. Plugin:复用构建能力
Plugin 是 Gradle 构建能力的封装。比如 java 插件会自动添加 Java 编译、测试、打包相关 task。
groovy
plugins {
id 'java'
}
应用 java 插件后,Gradle 会自动提供:
compileJavaprocessResourcesclassestestjarassemblecheckbuild
常用核心插件:
| 插件 ID | 作用 |
|---|---|
java |
Java 编译、测试、打包 |
java-library |
Java 库(支持 api 配置) |
application |
Java 可执行程序(提供 run task) |
maven-publish |
发布到 Maven 仓库 |
base |
基础 task(clean、assemble、check、build) |
11. Java Toolchain(工具链)
Java Toolchain 允许你在 build.gradle 中声明所需的 JDK 版本,Gradle 会自动查找或下载对应版本。好处是不依赖机器上默认的 JDK。
groovy
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
Kotlin DSL:
kotlin
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(17))
}
}
也可以对单个 task 指定:
groovy
tasks.withType(JavaCompile).configureEach {
javaCompiler = javaToolchains.compilerFor {
languageVersion = JavaLanguageVersion.of(17)
}
}
查看 Gradle 发现的 Toolchain:
bash
gradle -q javaToolchains
为什么要用 Toolchain?
- 团队成员本机 JDK 版本可能不同。
- CI 服务器 JDK 版本可能随系统升级变化。
- Toolchain 能保证编译 JDK 版本稳定,和 Wrapper 一样是可重复构建的关键。
12. Dependency 和 Repository
Repository 负责告诉 Gradle 去哪里找依赖,Dependency 负责声明项目需要什么依赖。
groovy
repositories {
mavenCentral()
}
dependencies {
implementation 'com.google.guava:guava:33.2.1-jre'
testImplementation 'org.junit.jupiter:junit-jupiter:5.10.2'
}
常见依赖范围:
| 依赖范围 | 含义 | 典型用途 |
|---|---|---|
implementation |
编译和运行需要,但不暴露给下游模块 | 大多数业务依赖 |
api |
编译和运行需要,并暴露给下游模块 | 公共 API 模块 |
compileOnly |
编译需要,运行不打包 | 注解、Servlet API |
runtimeOnly |
编译不需要,运行需要 | 数据库驱动 |
testImplementation |
测试编译和运行需要 | JUnit、Mockito |
testCompileOnly |
仅测试编译需要 | 测试注解处理器 |
testRuntimeOnly |
仅测试运行需要 | JUnit Platform Launcher |
13. Gradle 构建生命周期
一次 Gradle 构建大致分为三个阶段:
| 阶段 | 作用 | 例子 |
|---|---|---|
| Initialization | 确定参与构建的项目 | 读取 settings.gradle |
| Configuration | 配置项目和 task | 读取各模块 build.gradle |
| Execution | 执行被请求的 task | 执行 test、build、run |
常见误区是把复杂逻辑直接写在配置阶段。配置阶段越重,所有命令都会变慢,即使你只是执行一个简单 task。
groovy
// ❌ 错误:println 在配置阶段执行,任何命令都会触发
println "Configuring project..."
tasks.register('hello') {
// ❌ 错误:这里也在配置阶段
println "Setting up hello task..."
doLast {
// ✅ 正确:doLast 在执行阶段
println "Hello from execution phase"
}
}
14. 调试参数和日志级别
Gradle 提供丰富的调试参数,遇到问题时是排查的第一手段。
| 参数 | 作用 |
|---|---|
--stacktrace |
显示完整异常堆栈 |
--info |
显示 INFO 级别日志 |
--debug |
显示 DEBUG 级别日志(非常详细) |
--scan |
生成在线 Build Scan 报告 |
--profile |
生成本地 HTML 性能报告 |
--dry-run |
只打印会执行的 task,不真正执行 |
--rerun |
强制重新执行指定 task |
--refresh-dependencies |
强制重新下载依赖 |
-x taskName |
跳过指定 task |
--continue |
某个 task 失败后继续执行其他 task |
--parallel |
并行执行(也可在 properties 中设) |
常用组合:
bash
# 构建失败排查
./gradlew clean build --stacktrace
# 依赖下载失败排查
./gradlew build --refresh-dependencies --info
# 构建变慢排查
./gradlew build --profile
./gradlew build --scan
# 只看 task 执行计划
./gradlew build --dry-run
# 跳过测试
./gradlew build -x test
15. 入门实操
实操 1:查看 Gradle 版本
bash
gradle --version
验证点:
- 能看到 Gradle 版本。
- 能看到 JVM 版本。
- 能看到操作系统信息。
实操 2:查看项目 task
进入本仓库 demo:
bash
cd demo/gradle-multi-module-demo
gradle tasks
gradle tasks --all
验证点:
- 能看到
application、build、verification等分组。 - 能看到自定义 task
printProjectInfo。 --all能看到所有内部 task。
实操 3:执行完整构建
bash
gradle clean build
验证点:
BUILD SUCCESSFUL。app/build/libs、service/build/libs、common/build/libs下出现构建产物。
实操 4:查看 task 执行计划
bash
gradle build --dry-run
验证点:
- 输出每个将要执行的 task 前缀为
:模块:task名。 - 不会真正编译或执行测试。
实操 5:体验增量构建
bash
gradle compileJava # 第一次:EXECUTED
gradle compileJava # 第二次:UP-TO-DATE
验证点:
- 第二次执行时看到
UP-TO-DATE,构建几乎立即完成。
实操 6:使用调试参数排查
bash
gradle build --info 2>&1 | head -60
验证点:
- 能看到每个 task 的详细状态(UP-TO-DATE / EXECUTED / SKIPPED)。
16. 常见问题
问题 1:为什么有时命令是 gradle,有时是 ./gradlew
gradle 使用本机安装的 Gradle,./gradlew 使用项目固定的 Gradle 版本。真实团队项目优先使用 ./gradlew。
问题 2:为什么依赖下载很慢
常见原因:
- 仓库访问慢。
- 公司网络代理限制。
- 依赖版本不存在。
- 动态版本导致频繁检查远程仓库。
建议优先使用固定版本,企业内部项目可以接入 Nexus、Artifactory 或公司 Maven 镜像。
问题 3:为什么执行 gradle build 会先跑测试
build 生命周期包含 check,而 check 默认依赖 test。如果临时跳过测试可以执行:
bash
gradle build -x test
但在 CI 中不建议长期跳过测试。
问题 4:Groovy DSL 和 Kotlin DSL 能混用吗
同一个项目中,settings.gradle 和 build.gradle 必须保持一致(都用 Groovy 或都用 Kotlin)。buildSrc 或 build-logic 中可以选择不同的 DSL,互不影响。
问题 5:为什么修改 gradle.properties 不生效
- 检查文件位置:项目根目录下的
gradle.properties只对当前项目生效。 - 全局配置放在
~/.gradle/gradle.properties。 - 修改后需要重启 Gradle Daemon:
gradle --stop。
问题 6:UP-TO-DATE 但结果不对
说明 task 的输入声明不完整,Gradle 没有把某个影响输出的输入纳入追踪。常见原因:
- task 依赖了环境变量或系统时间但没声明为
@Input。 - task 读取了某个文件但没声明为
@InputFile。
解决方法:用 --info 查看 task 的 UP-TO-DATE 判断依据,补充缺失的输入输出声明。