Article
第一章:Gradle 入门与基础概念
1.1 什么是 Gradle
| 概念名称 | 说明 | 注意事项 |
|---|---|---|
| Gradle | 一个基于 Groovy 或 Kotlin DSL 的自动化构建工具,用于编译、测试、打包和部署项目。它结合了 Ant 的灵活性和 Maven 的约定优于配置理念。 | Gradle 不是仅限于 Java 的工具,广泛用于 Android、Groovy、Scala、Kotlin 等项目构建。 |
| 构建自动化 | 指通过脚本自动完成编译、依赖管理、测试、打包、部署等重复性任务的过程。 | 手动构建易出错且效率低,Gradle 提供声明式和可扩展的方式来实现自动化。 |
| DSL(领域特定语言) | Gradle 使用 Groovy 或 Kotlin 编写的 DSL 来定义构建逻辑,比 XML 更简洁、更具表达力。 | 不同于通用编程语言,DSL 专为构建任务设计,学习成本较低但需熟悉语法结构。 |
| 构建脚本(build.gradle) | Gradle 的核心配置文件,使用 DSL 编写,定义项目行为、任务、依赖等。 | 默认使用 Groovy 语法(.gradle),也支持 Kotlin(.kts),初学者建议从 Groovy 入门。 |
1.2 Gradle 与 Maven、Ant 的对比
| 对比维度 | Gradle | Maven | Ant |
|---|---|---|---|
| 配置方式 | 基于 Groovy/Kotlin DSL(代码式) | 基于 XML(声明式) | 基于 XML(过程式) |
| 灵活性 | 高,可通过代码完全控制构建逻辑 | 中等,插件机制较固定 | 高,但需手动编写所有逻辑 |
| 学习曲线 | 中等,需了解 DSL 和编程概念 | 低,结构固定易于上手 | 高,需掌握 XML 和构建流程 |
| 构建性能 | 高,支持增量构建、缓存、并行执行 | 一般,全量构建较多 | 低,无内置优化机制 |
| 依赖管理 | 强大,支持动态版本、传递依赖、冲突解决 | 强大,基于中央仓库机制 | 弱,需手动管理 |
| 插件机制 | 灵活,可编写自定义插件并动态应用 | 丰富,生态成熟但扩展受限 | 需手动集成,不够模块化 |
| 默认约定 | 支持”约定优于配置”,也可自定义 | 强调”约定优于配置” | 无默认结构,完全自定义 |
| 脚本可读性 | 高,DSL 接近自然语言 | 低,XML 冗长且嵌套深 | 低,逻辑分散不易维护 |
| 多项目构建支持 | 优秀,原生支持复杂项目结构 | 支持,但配置繁琐 | 支持,需大量脚本编写 |
| 使用场景 | Android 开发、复杂项目、CI/CD 流水线 | Java 企业项目、标准 Maven 项目 | 遗留系统、高度定制化构建 |
注意事项:
- Maven 适合标准化项目,Gradle 更适合需要灵活性和高性能的现代项目。
- Ant 已逐渐被取代,但在某些遗留系统中仍有使用。
- Gradle 兼容 Maven 仓库,可无缝使用现有依赖。
1.3 Gradle 的核心特性(DSL、增量构建、依赖管理等)
| 特性名称 | 说明 | 注意事项 |
|---|---|---|
| 基于 DSL 的构建脚本 | 使用 Groovy 或 Kotlin 编写构建逻辑,语法简洁,支持编程结构(条件、循环、函数等)。 | 初学者需理解闭包和委托机制,避免过度复杂化脚本。 |
| 增量构建(Incremental Build) | Gradle 能识别任务输入输出变化,仅重新执行受影响的任务,大幅提升构建速度。 | 必须正确标注任务的 @Input 和 @Output,否则无法判断是否可跳过。 |
| 任务依赖管理 | 支持显式定义任务依赖(dependsOn),自动调度执行顺序。 | 避免循环依赖,否则构建失败。 |
| 强大的依赖管理 | 支持本地/远程仓库(Maven、Ivy)、动态版本、排除传递依赖、依赖配置(configurations)。 | 动态版本(如 1.+)可能导致构建不一致,生产环境建议锁定版本。 |
| 多项目构建 | 支持将大型项目拆分为多个子项目,统一管理依赖和配置。 | 需在 settings.gradle 中声明子项目,并合理设计项目间依赖。 |
| 插件系统 | 提供标准化方式扩展功能(如 Java 插件、Android 插件),支持自定义插件开发。 | 官方插件稳定,第三方插件需评估兼容性和维护状态。 |
| 构建缓存 | 支持本地和远程缓存,复用任务输出,减少重复构建。 | 需启用缓存功能(—build-cache),并确保任务幂等性。 |
| 并行构建 | 可并行执行独立任务,充分利用多核 CPU 提升性能。 | 在 gradle.properties 中设置 org.gradle.parallel=true。 |
| 生命周期监听 | 提供初始化、配置、执行阶段的钩子,便于监控和干预构建过程。 | 钩子代码应轻量,避免阻塞主线程。 |
| Wrapper 支持 | 提供 gradlew 脚本,确保团队使用统一 Gradle 版本,无需手动安装。 | 推荐项目中包含 wrapper 文件,便于 CI/CD 集成。 |
1.4 Gradle 的安装与环境配置
| 方法名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| 手动安装(官网下载) | 1. 下载 Gradle 发行包 2. 解压到目录 3. 配置环境变量 | 手动控制版本,适合学习和测试 | 下载地址:https://gradle.org/releases/ | 需手动维护版本升级 |
| 环境变量配置(GRADLE_HOME) | GRADLE_HOME = /path/to/gradle | 指定 Gradle 安装根目录 | export GRADLE_HOME=/opt/gradle-8.5 | Windows 使用系统属性设置 |
| PATH 添加 | PATH = $PATH:$GRADLE_HOME/bin | 使 gradle 命令全局可用 | export PATH=$PATH:$GRADLE_HOME/bin | 配置后需重启终端或 source ~/.bashrc |
| 验证安装 | gradle -v | 查看 Gradle 版本及环境信息 | gradle -v | 应显示 Gradle 版本、Groovy、JVM 信息 |
| 使用 SDKMAN!(Linux/macOS) | sdk install gradle 8.5 | 通过 SDK 管理工具安装 | sdk install gradle 8.5 | 推荐开发者使用,方便版本切换 |
| 使用 Chocolatey(Windows) | choco install gradle | Windows 包管理器安装 | choco install gradle | 需管理员权限运行 CMD |
| Gradle Wrapper(推荐) | gradle wrapper | 生成 wrapper 文件,避免手动安装 | gradle wrapper --gradle-version 8.5 | 项目级版本控制,CI/CD 友好 |
注意事项:
- 安装前需确保已安装 JDK 8 或更高版本。
- 推荐使用 Gradle Wrapper(gradlew),避免团队版本不一致问题。
- 验证安装时若提示”command not found”,检查 PATH 是否正确配置。
1.5 Gradle 命令行基础使用
| 命令名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| gradle tasks | gradle tasks [--all] | 列出项目中所有可执行任务 | gradle tasksgradle tasks --all | --all 显示所有任务(含隐藏任务) |
| gradle build | gradle build | 执行完整构建(编译、测试、打包等) | gradle build | 是最常用的构建命令 |
| gradle clean | gradle clean | 删除 build 目录,清理输出文件 | gradle clean | 清理后重新构建可避免缓存问题 |
| gradle help | gradle help --task <任务名> | 查看任务帮助信息 | gradle help --task build | 可查看任务用途和依赖 |
| gradle -q | gradle -q build | 静默模式执行任务(减少日志输出) | gradle -q build | -q 表示 quiet 模式 |
| gradle -i | gradle -i build | 输出信息级日志(info) | gradle -i build | 用于调试构建过程 |
| gradle -d | gradle -d build | 输出调试级日志(debug) | gradle -d build | 日志最多,用于深入排查问题 |
| gradle —dry-run | gradle --dry-run build | 模拟执行,不真正运行任务 | gradle --dry-run build | 查看任务执行顺序,不实际执行 |
| gradle —continue | gradle --continue build | 即使某任务失败也继续执行其他任务 | gradle --continue build | 用于多任务场景,收集全部错误 |
| gradle wrapper | gradle wrapper --gradle-version X.X | 生成 Gradle Wrapper 文件 | gradle wrapper --gradle-version 8.5 | 生成 gradlew, gradlew.bat, wrapper JAR |
| ./gradlew | ./gradlew build | 使用 Wrapper 执行构建 | ./gradlew build | 推荐在项目中始终使用此方式 |
注意事项:
- 所有命令应在包含 build.gradle 的目录中执行。
- gradlew 自动下载指定版本的 Gradle,无需本地安装。
- 日志级别:
-q(quiet)< 默认 <-i(info)<-d(debug) - 使用
--info或-i可查看任务是否被 UP-TO-DATE 跳过。
第二章:Gradle 构建脚本基础(build.gradle)
2.1 build.gradle 文件结构概述
| 概念名称 | 说明 | 注意事项 |
|---|---|---|
| 脚本顶层块(Top-level blocks) | build.gradle 文件由多个 DSL 块组成,用于配置项目不同方面。 | 每个块有特定作用域,不能随意嵌套。 |
| plugins {} | 用于应用 Gradle 插件,通常位于文件顶部。 | 使用此方式应用的是二进制插件,需指定插件 ID。 |
| apply plugin: ‘java’ | 老式插件应用语法,通过 apply 方法引入插件。 | 适用于脚本插件或未在插件门户注册的插件。 |
| group / version / description | 项目元数据属性,定义项目组、版本和描述信息。 | 通常用于多项目构建或发布场景。 |
| repositories {} | 配置依赖仓库(如 Maven Central、JCenter、自定义仓库)。 | 必须配置才能下载外部依赖。 |
| dependencies {} | 声明项目依赖,按配置(configuration)分类管理。 | 不同配置对应不同使用场景(编译、运行时等)。 |
| sourceSets {} | 定义源码目录结构(如 Java 源码、资源文件路径)。 | 可自定义目录,覆盖默认约定。 |
| task 定义块 | 使用 task 关键字定义自定义任务。 | 可带动作(action)或配置属性。 |
| ext {} 或 ext.属性 = 值 | 定义扩展属性,用于在项目中共享变量。 | 类似全局变量,便于统一管理配置。 |
| configurations {} | 自定义依赖配置(较少使用,通常用默认配置)。 | 高级用法,用于特殊依赖管理需求。 |
注意事项:
- plugins {} 块必须位于文件最前(除注释外),不可包含逻辑代码。
- 多个 build.gradle 文件可存在于多项目构建中,每个子项目可独立配置。
- Groovy DSL 中分号可省略,每行一条语句即可。
2.2 项目与任务(Project 与 Task)
| 概念名称 | 说明 | 注意事项 |
|---|---|---|
| Project | Gradle 构建中的基本单元,每个 build.gradle 对应一个 Project 实例。 | 通过 project 对象可访问项目属性和方法。 |
| Task | 构建中的最小执行单元,如编译、打包、测试等。 | 每个任务是 org.gradle.api.Task 的实例。 |
| 内建任务 | 插件自动创建的任务(如 Java 插件的 compileJava、test)。 | 无需手动定义,应用插件后自动可用。 |
| 自定义任务 | 用户通过 task 关键字或类定义的任务。 | 可实现特定构建逻辑。 |
| 任务动作(Action) | 任务执行的具体操作,通过 doLast 或 doFirst 添加。 | doLast 添加到任务末尾,doFirst 添加到开头。 |
| 任务状态 | 任务执行后有不同状态:SUCCESS、FAILED、SKIPPED、UP-TO-DATE。 | UP-TO-DATE 表示输入未变,跳过执行。 |
| 项目层次结构 | 单项目或多层次项目(root project + subprojects)。 | settings.gradle 控制项目包含关系。 |
| project 对象 | 在 build.gradle 中隐式可用,代表当前项目。 | 可调用 project.property 访问属性。 |
| task 对象 | 每个任务是 Task 接口的实现,可配置属性和行为。 | 支持动态添加属性和方法。 |
| 生命周期方法 | project 提供 afterEvaluate、beforeEvaluate 等钩子。 | 用于在配置阶段后干预项目配置。 |
注意事项:
- 所有配置代码在配置阶段执行,任务动作在执行阶段执行。
- 避免在任务动作中进行复杂配置,应放在配置块中。
- 可通过 tasks 集合访问所有任务(如
tasks.build)。
2.3 任务定义与执行(task)
| 方法名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| task(关键字) | task <任务名> | 定义一个空任务 | task hello | 最基础的任务定义方式 |
| task with type | task <任务名>(type: <任务类型>) | 创建指定类型的任务 | task zip(type: Zip) | 用于创建内建任务类型实例 |
| task with action | task <任务名> { doLast { ... } } | 定义任务并添加执行动作 | task hello { doLast { println 'Hello' } } | doLast 添加动作到任务末尾 |
| task with doFirst | task <任务名> { doFirst { ... } } | 添加动作到任务开始前 | task hello { doFirst { println 'Start' } } | 多个 doFirst 按逆序执行 |
| task with description | task <任务名> { description = '...' } | 设置任务描述 | task hello { description = 'Prints hello' } | 可通过 tasks 查看 |
| task with group | task <任务名> { group = '...' } | 设置任务所属分组 | task hello { group = 'custom' } | 组织任务显示结构 |
| task with property | task <任务名> { <属性> = <值> } | 配置任务属性 | task copyFiles(type: Copy) { from 'src'; into 'dest' } | 不同任务类型支持不同属性 |
| task (lazy) | tasks.register('name') { ... } | 惰性注册任务(推荐方式) | tasks.register('hello') { doLast { println 'Hello' } } | 仅在需要时创建,提升性能 |
| task (eager) | tasks.create('name') { ... } | 立即创建任务 | tasks.create('hello') { doLast { println 'Hello' } } | 立即执行配置,兼容旧写法 |
| 调用任务 | gradle <任务名> | 从命令行执行任务 | gradle hello | 在项目根目录执行 |
注意事项:
- 使用 tasks.register 是现代 Gradle 推荐方式,支持惰性求值。
- doLast 和 doFirst 可多次调用,动作按顺序执行。
- 任务名不能包含空格,建议使用驼峰命名法(如 myTask)。
- 任务定义后可通过
gradle tasks查看是否注册成功。
2.4 任务依赖(dependsOn)
| 方法名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| dependsOn(属性) | taskA.dependsOn taskB | 设置任务 A 依赖任务 B | task taskB { doLast { println 'B' } }task taskA(dependsOn: taskB) { doLast { println 'A' } } | taskA 执行前先执行 taskB |
| dependsOn(方法) | taskA.dependsOn(taskB) | 动态设置依赖关系 | taskA.dependsOn(taskB) | 可在配置阶段任意位置调用 |
| dependsOn(多个任务) | taskA.dependsOn taskB, taskC | 依赖多个任务 | taskA.dependsOn(taskB, taskC) | 所有依赖任务执行完后才执行 taskA |
| dependsOn(任务集合) | taskA.dependsOn tasks.matching { ... } | 依赖符合条件的任务 | taskA.dependsOn(tasks.matching { it.name.startsWith('compile') }) | 动态依赖,适用于多源集场景 |
| dependsOn(字符串) | taskA.dependsOn 'taskB' | 通过任务名设置依赖 | taskA.dependsOn('taskB') | 适用于跨项目或后定义任务 |
| dependsOn(闭包) | taskA.dependsOn { [...] } | 惰性计算依赖列表 | taskA.dependsOn { project.hasProperty('runTests') ? [test] : [] } | 依赖列表在执行前计算 |
| mustRunAfter | taskA.mustRunAfter taskB | 指定顺序但不强制依赖 | taskA.mustRunAfter(taskB) | 不影响执行计划,仅排序 |
| finalizedBy | taskA.finalizedBy taskB | 无论 taskA 成功或失败,都执行 taskB | taskA.finalizedBy(taskB) | 常用于清理或报告任务 |
注意事项:
- dependsOn 创建强依赖,确保依赖任务完成后再执行当前任务。
- 避免循环依赖(A→B→A),会导致构建失败。
- mustRunAfter 不是依赖,仅控制执行顺序,若 taskB 不执行,taskA 仍可运行。
- finalizedBy 用于确保清理任务始终执行(如
stopTomcat.finalizedBy cleanUp)。
2.5 任务分组与描述(group, description)
| 方法名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| group(属性) | taskX.group = 'GroupName' | 设置任务所属分组 | task hello { group = 'custom' } | 分组用于组织任务显示 |
| group(构造参数) | task (group: 'GroupName') | 定义任务时指定分组 | task hello(group: 'utility') { doLast { ... } } | 与 description 一起使用更清晰 |
| description(属性) | taskX.description = '...' | 设置任务描述信息 | task hello { description = 'Prints greeting' } | 通过 gradle tasks 查看 |
| description(构造参数) | task (description: '...') | 定义任务时指定描述 | task hello(description: 'Say hello') { doLast { ... } } | 推荐为自定义任务添加描述 |
| 查看任务信息 | gradle tasks --all | 显示所有任务及其分组和描述 | gradle tasks --all | --all 显示隐藏任务 |
| 内建分组名称 | 'build', 'documentation', 'verification', 'publishing', 'custom' | Gradle 推荐的标准分组 | task generateReport { group = 'documentation' } | 遵循惯例便于理解 |
| 自定义分组 | 任意字符串 | 创建自定义任务类别 | task deploy(group: 'deployment', description: 'Deploy app') | 建议命名清晰一致 |
注意事项:
- 为自定义任务设置 group 和 description 是最佳实践,便于团队协作。
- 分组名建议使用小写字母,单词间用连字符或空格分隔。
- 未设置分组的任务默认归为”Other tasks”。
- 描述应简洁明了,说明任务用途。
第三章:Gradle 生命周期与执行流程
3.1 初始化阶段(Initialization)
| 概念名称 | 说明 | 注意事项 |
|---|---|---|
| 初始化阶段 | Gradle 构建的第一个阶段,确定构建哪些项目(Project)并创建项目实例。 | 该阶段仅运行一次,决定整个构建的项目范围。 |
| settings.gradle | 位于项目根目录,用于配置多项目构建结构。 | 必须存在才能正确初始化多项目。 |
| include | 在 settings.gradle 中使用,声明包含的子项目。 | 语法:include 'sub1', 'sub2' 或 include ':sub1' |
| project.name | 设置项目名称,可覆盖目录名。 | 在 settings.gradle 中配置:findProject(':sub1')?.name = 'module1' |
| Gradle 版本选择 | 通过 Gradle Wrapper(gradle/wrapper/gradle-wrapper.properties)确定使用哪个 Gradle 版本。 | 推荐使用 Wrapper 保证环境一致性。 |
| 多项目结构识别 | Gradle 解析 settings.gradle 并构建项目树结构。 | 根项目和子项目均在初始化阶段创建。 |
| 单项目构建 | 若无 settings.gradle,Gradle 将当前目录视为单项目。 | 适用于简单项目。 |
| 异常处理 | 若 settings.gradle 语法错误或项目路径不存在,初始化失败。 | 构建终止,输出错误信息。 |
| 初始化脚本(init scripts) | 使用 -I 参数指定,用于全局配置(如企业级仓库设置)。 | 高级用法,影响所有构建。 |
| buildSrc 项目 | 特殊子项目,用于存放构建脚本的源码,在初始化后自动编译。 | 代码位于根目录的 buildSrc/ 下。 |
注意事项:
- settings.gradle 必须在根项目目录下。
- 初始化阶段不执行任何任务,仅确定项目结构。
- 可通过
--include-build将外部构建包含进来(复合构建)。
3.2 配置阶段(Configuration)
| 概念名称 | 说明 | 注意事项 |
|---|---|---|
| 配置阶段 | 第二阶段,执行所有项目的 build.gradle 脚本,配置 Project 和 Task 对象。 | 所有任务都会被配置,无论是否执行。 |
| 脚本执行 | 每个 build.gradle 文件在此阶段被解析并执行。 | 包括变量定义、插件应用、任务创建等。 |
| 插件应用 | plugins {} 或 apply plugin: 在此阶段生效,添加任务和配置。 | 插件会扩展 Project 对象。 |
| 任务创建 | 所有 task 定义在此阶段注册到 Project 中。 | 即使任务不执行也会被创建。 |
| 依赖解析(配置时) | dependencies {} 块被读取,但依赖尚未下载。 | 实际下载发生在执行阶段需要时。 |
| 属性计算 | 所有 ext 扩展属性、项目属性在此阶段确定。 | 可用于后续任务配置。 |
| 任务配置 | 任务的 group、description、inputs/outputs 等属性被设置。 | 动作(doFirst/doLast)被添加但不执行。 |
| afterEvaluate | project.afterEvaluate { } 钩子在此阶段末尾执行。 | 用于配置已被其他插件修改的任务。 |
| 错误类型 | 若 DSL 语法错误或属性未定义,配置阶段失败。 | 构建终止,不进入执行阶段。 |
| 性能影响 | 配置阶段耗时随项目数量和脚本复杂度增加。 | 应避免在此阶段进行耗时操作(如网络请求)。 |
注意事项:
- 所有任务无论是否执行都会经历配置阶段。
- 配置代码应尽量轻量,避免 I/O 或复杂计算。
- 使用惰性配置 API(如 tasks.register)可优化性能。
3.3 执行阶段(Execution)
| 概念名称 | 说明 | 注意事项 |
|---|---|---|
| 执行阶段 | 第三阶段,按依赖顺序执行命令行指定的任务。 | 仅执行计划中的任务。 |
| 任务执行图(Task Graph) | Gradle 根据依赖关系构建任务执行顺序。 | 自动处理 dependsOn、mustRunAfter 等关系。 |
| 动作执行 | 任务的 doFirst 和 doLast 闭包在此阶段按序执行。 | 实际工作(如编译、复制)发生在此处。 |
| 增量构建判断 | Gradle 检查任务输入输出,若未变化则标记为 UP-TO-DATE。 | 跳过执行,提升速度。 |
| 依赖下载 | 若任务需要依赖(如 compile),此时从仓库下载。 | 首次构建可能较慢。 |
| 失败处理 | 若某任务失败,默认终止构建。 | 使用 --continue 可继续执行其他独立任务。 |
| 日志输出 | 显示任务执行状态(:taskName SUCCESS)。 | 可通过 -i 或 -d 查看详细日志。 |
| 并行执行 | 若启用并行(org.gradle.parallel=true),独立任务可并发运行。 | 提升多核机器构建效率。 |
| finalizedBy 执行 | 被 finalize 的任务无论原任务成功与否都会执行。 | 常用于清理或报告。 |
| 缓存使用 | 若启用构建缓存,尝试复用远程或本地缓存输出。 | 减少重复构建。 |
注意事项:
- 任务动作中可访问配置阶段确定的属性。
- 避免在动作中修改任务输入,可能导致缓存失效。
- 执行阶段是唯一可进行 I/O 和外部调用的阶段。
3.4 生命周期钩子(project.afterEvaluate, gradle.taskGraph.whenReady 等)
| 钩子方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| project.afterEvaluate | project.afterEvaluate { ... } | 在项目配置完成后执行 | afterEvaluate { println "Project ${name} configured" } | 用于修改被插件配置后的任务 |
| allprojects.afterEvaluate | allprojects { afterEvaluate { ... } } | 对所有项目(含子项目)执行 | allprojects { afterEvaluate { applyCommonConfig() } } | 确保子项目配置完成后再干预 |
| gradle.projectsEvaluated | gradle.projectsEvaluated { ... } | 所有项目配置完成后执行 | gradle.projectsEvaluated { tasks.withType(Test) { maxHeapSize = '2g' } } | 适合跨项目统一配置 |
| gradle.taskGraph.whenReady | gradle.taskGraph.whenReady { ... } | 任务执行图构建完成后执行 | gradle.taskGraph.whenReady { if (it.hasTask(build)) { println "Build is running" } } | 可查询将要执行的任务 |
| gradle.buildStarted | gradle.buildStarted { ... } | 构建开始时执行(极少使用) | gradle.buildStarted { startTime = System.currentTimeMillis() } | 通常用日志监听器替代 |
| gradle.buildFinished | gradle.buildFinished { ... } | 构建结束时执行,无论成功失败 | gradle.buildFinished { result -> println "Build ${result.result}" } | result 包含构建状态 |
| Task Configuration Avoidance | tasks.register('myTask') { ... } | 惰性创建任务,仅在需要时配置 | tasks.register('gen') { doLast { generateFile() } } | 推荐替代 tasks.create |
| Project Evaluation Listener | gradle.addProjectEvaluationListener(...) | 监听项目评估开始/结束 | class MyListener implements ProjectEvaluationListener { ... } | 高级 API,用于插件开发 |
| Task Execution Listener | gradle.taskGraph.addTaskExecutionListener(...) | 监听任务执行开始/结束 | taskGraph.addTaskExecutionListener(new TaskLogger()) | 用于性能监控或日志 |
注意事项:
- afterEvaluate 是最常用的钩子,用于处理插件修改后的配置。
- whenReady 中可安全查询任务图,适合条件逻辑。
- 监听器应在配置阶段注册,否则可能错过事件。
- 避免在钩子中执行耗时操作,影响构建性能。
- 推荐优先使用惰性 API(register)而非立即创建(create)。
第四章:Project 对象与 API 使用
4.1 Project 接口核心属性与方法
| 方法/属性名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| project | (隐式对象) | 表示当前项目,所有脚本中默认可用 | println project.name | 在 build.gradle 中可省略 project 前缀 |
| name | project.name | 获取项目名称(默认为目录名) | println name | 可在 settings.gradle 中修改 |
| projectDir | project.projectDir | 返回项目根目录的 File 对象 | println projectDir.absolutePath | 不可重新赋值 |
| buildDir | project.buildDir | 返回构建输出目录(默认为 projectDir/build) | buildDir = file('out') | 可自定义路径 |
| rootProject | project.rootProject | 获取根项目对象 | println rootProject.name | 在子项目中访问根项目 |
| parent | project.parent | 获取父项目对象(子项目可用) | if (parent != null) { ... } | 根项目返回 null |
| children | project.children | 返回子项目集合 | parent.children.each { println it.name } | 用于遍历子项目 |
| properties | project.properties | 返回项目所有属性的只读 Map | println properties['version'] | 包括系统属性、ext 属性等 |
| logger | project.logger | 获取日志记录器,用于输出信息 | logger.quiet 'Quiet message'logger.info 'Info message'logger.debug 'Debug message' | 支持 quiet, info, debug, error 等级别 |
| file() | file('path') | 将路径字符串转为 File 对象 | file('src/main/java') | 相对路径基于 projectDir |
| files() | files('a', 'b', 'c') | 创建 FileCollection 对象 | def srcFiles = files('src1', 'src2') | 可包含多个文件或目录 |
| uri() | uri('path') | 将路径转为 URI 对象 | uri('file:///path') | 用于网络或绝对路径处理 |
| hasProperty() | hasProperty('propName') | 检查属性是否存在 | if (hasProperty('customProp')) { ... } | 避免访问未定义属性出错 |
| findProperty() | findProperty('propName') | 查找属性值,不存在返回 null | def val = findProperty('version') | 比直接访问更安全 |
注意事项:
- project 对象在 build.gradle 中是隐式的,通常省略前缀。
- buildDir 默认为 projectDir/build,可全局修改(如
allprojects { buildDir = 'out' })。 - 使用 logger 而非 println 可控制日志级别输出。
- file() 和 files() 是处理文件路径的标准方式,支持相对路径和 File 对象混合。
4.2 属性访问与扩展(ext)
| 方法/属性名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| ext 属性块 | ext { ... } | 批量定义扩展属性 | ext { version = '1.0'; enabled = true } | 类似定义多个 ext.prop = value |
| ext.属性 = 值 | ext.customProp = value | 定义单个扩展属性 | ext.myVersion = '2.0' | 可在任意位置定义 |
| 访问 ext 属性 | project.ext.prop 或 ext.prop | 读取扩展属性 | println ext.myVersion | 在同一项目中可直接访问 |
| hasProperty() | hasProperty('extProp') | 检查 ext 属性是否存在 | if (hasProperty('myVersion')) { ... } | 推荐用于条件判断 |
| findProperty() | findProperty('extProp') | 安全查找属性(含 ext) | def v = findProperty('myVersion') | 不存在返回 null,避免异常 |
| 父项目属性 | rootProject.ext.prop | 访问根项目定义的 ext 属性 | ext.version = rootProject.ext.appVersion | 常用于多项目版本统一 |
| gradle.properties | 在 gradle.properties 文件中定义属性 | version=1.0org=company | 自动加载为项目属性 | 支持 project 和 rootProject 级 |
| 系统属性 | systemProp.name 或 -Dname=value | 通过 JVM 系统属性传参 | systemProp.http.proxyHost=proxy | 在 gradle.properties 或命令行设置 |
| 项目属性 | -PpropName=value | 命令行传入项目属性 | gradle build -Penv=prod | 在脚本中用 hasProperty/env 访问 |
| 属性优先级 | ext < gradle.properties < 命令行 -P < 系统属性 | 属性覆盖顺序 | ext.version = '1.0'// -Pversion=2.0 会覆盖 | 命令行动态值优先级最高 |
注意事项:
- ext 是定义自定义属性的标准方式,避免污染全局命名空间。
- 多项目中推荐在根项目 gradle.properties 或 ext 中定义共享属性。
- 使用 hasProperty() 或 findProperty() 避免因属性缺失导致构建失败。
- gradle.properties 文件可存在于项目根目录或用户主目录(~/.gradle/gradle.properties)。
4.3 文件操作(file, files, copy, sync 等)
| 方法名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| file() | file('path') | 创建 File 对象 | def f = file('build.gradle') | 路径相对于 projectDir |
| files() | files('a', 'b', 'c') | 创建 FileCollection | def src = files('src1', 'src2') | 支持 List、Set、FileTree |
| copy() | copy { ... } | 执行文件复制操作 | copy { from 'src'; into 'dest' } | 在任务动作中调用 |
| Copy 任务 | task copyTask(type: Copy) | 创建可复用的复制任务 | task copyJar(type: Copy) { from jar; into 'dist' } | 支持增量构建 |
| sync() | sync { ... } | 复制并删除目标目录多余文件 | sync { from 'html'; into 'www' } | 保证目标目录与源完全一致 |
| Sync 任务 | task syncAssets(type: Sync) | 创建同步任务 | task syncFiles(type: Sync) { ... } | 比 copy 更严格 |
| from() | from 'srcDir' 或 from(files) | 指定复制源 | from 'src/main/resources'from configurations.runtime | 支持多种输入源 |
| into() | into 'destDir' | 指定复制目标目录 | into 'build/resources' | 目录不存在会自动创建 |
| include() | include '**/*.txt' | 包含匹配的文件 | include '**/*.java' | 支持 Ant 风格模式 |
| exclude() | exclude '**/*.tmp' | 排除匹配的文件 | exclude { it.name.startsWith('test') } | 可用闭包进行复杂判断 |
| rename() | rename 'old.txt', 'new.txt' | 重命名文件 | rename '(.)_V(.).txt', '1_2.txt' | 支持正则表达式 |
| filter() | filter(ReplaceTokens, tokens: [key: 'value']) | 替换文件内容占位符 | filter(ReplaceTokens, tokens: [version: version]) | 常用于模板文件 |
| expand() | expand([key: 'value']) | 展开属性到文件内容 | expand([appVersion: version]) | 类似 filter,用于 Groovy 模板 |
| eachFile() | eachFile { it.path = it.path.replace('old', 'new') } | 遍历并修改文件路径或属性 | eachFile { println it.name } | 可动态调整文件处理 |
注意事项:
- copy 和 sync 通常在任务的 doLast 中调用,或定义为 Copy/Sync 类型任务。
- Sync 任务会清理目标目录中源目录没有的文件,使用时注意数据丢失风险。
- from 可接受任务(如
from jar),实现任务间输出传递。 - 文件操作支持延迟计算(如闭包返回路径),适合动态路径。
4.4 目录与路径管理(projectDir, buildDir 等)
| 属性/方法名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| projectDir | project.projectDir | 项目根目录 | println projectDir | 只读,不可修改 |
| buildDir | project.buildDir | 构建输出目录 | buildDir = file('output') | 可重新赋值,影响所有输出 |
| rootDir | rootProject.projectDir | 多项目构建的根目录 | println rootDir | 单项目中与 projectDir 相同 |
| gradle.gradleUserHomeDir | gradle.gradleUserHomeDir | Gradle 用户主目录(~/.gradle) | println gradle.gradleUserHomeDir | 存放缓存、Wrapper 等 |
| gradle.gradleHomeDir | gradle.gradleHomeDir | Gradle 安装目录(非 Wrapper 时) | println gradle.gradleHomeDir | Wrapper 下可能为空 |
| layout.projectDirectory | layout.projectDirectory | ProjectLayout 中的项目目录 | layout.projectDirectory.dir('src') | 用于插件开发 |
| layout.buildDirectory | layout.buildDirectory | ProjectLayout 中的构建目录 | layout.buildDirectory.dir('tmp') | 支持惰性求值 |
| file() | file('path') | 创建基于 projectDir 的文件对象 | file('src/main/java') | 推荐路径操作方式 |
| mkdir() | mkdir 'dirName' | 创建目录 | mkdir 'build/custom' | 目录已存在不报错 |
| delete() | delete 'path' 或 delete(files) | 删除文件或目录 | delete buildDirdelete('temp') | 支持通配符和 FileCollection |
| temporaryDir | project.temporaryDir | 获取临时目录(用于任务中间文件) | def tmp = temporaryDir | 每次构建可能不同,自动清理 |
注意事项:
- buildDir 修改应在配置阶段早期完成,避免任务已引用旧路径。
- delete 是常用清理操作,可替代 clean 任务部分功能。
- temporaryDir 适合存放任务中间产物,构建后自动清理。
- 多项目中 rootDir 统一指向根项目目录,便于资源定位。
- 路径操作优先使用 file() 和 layout API,保证可移植性。
第五章:Task 进阶用法
5.1 自定义任务类型(class extends DefaultTask)
| 方法/注解 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| DefaultTask | class MyTask extends DefaultTask | 创建自定义任务基类 | abstract class MyTask extends DefaultTask { } | 必须继承以获得 Gradle 任务能力 |
| @TaskAction | @TaskAction def run() { ... } | 定义任务的执行动作 | @TaskAction def greet() { println 'Hello' } | 每个任务类只能有一个 @TaskAction 方法 |
| 构造函数 | MyTask() { ... } | 初始化任务属性 | MyTask() { group = 'custom' } | 可设置默认 group、description |
| 属性定义 | @Input String message | 定义可配置属性 | String message = 'Hi' | 可在任务配置块中赋值 |
| 任务注册 | tasks.register('greet', MyTask) | 注册自定义任务实例 | tasks.register('sayHi', MyTask) { message = 'Hello' } | 推荐使用 register 实现惰性创建 |
| 任务创建 | tasks.create('greet', MyTask) | 立即创建任务实例 | tasks.create('sayHi', MyTask) { message = 'Hey' } | 立即执行配置 |
| 闭包委托 | doLast { } 中访问任务属性 | 在动作中使用任务字段 | doLast { println message } | 动作闭包委托给任务实例 |
| 抽象任务类 | abstract class BaseTask extends DefaultTask | 定义共享逻辑的基类 | abstract class BaseTask extends DefaultTask { @TaskAction def exec() } | 子类继承并实现具体行为 |
| 依赖插件任务 | dependsOn compileJava | 在自定义任务中依赖内建任务 | task myTask { dependsOn 'compileJava' } | 确保前置任务完成 |
| 访问 Project | getProject() | 在任务类中获取所属项目 | println getProject().name | 通过继承 DefaultTask 获得 |
注意事项:
- 自定义任务类通常放在 buildSrc/src/main/groovy 或单独模块中。
- @TaskAction 方法是任务执行的入口,不可有参数。
- 属性应使用 @Input、@Output 等注解以支持增量构建。
- 推荐使用 tasks.register 而非 create 以提升性能。
- 任务类文件需编译,确保 buildSrc 正确配置。
5.2 任务输入输出注解(@Input, @OutputFile 等)
| 注解名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| @Input | @Input String version | 标记简单类型输入(String, Number, Boolean) | @Input String getVersion() { return project.version } | 值变化时任务需重新执行 |
| @InputFile | @InputFile File configFile | 标记单个输入文件 | @InputFile File getInputFile() { return file('config.txt') } | 文件内容或元数据变化触发重建 |
| @InputDirectory | @InputDirectory File srcDir | 标记输入目录 | @InputDirectory File getSrc() { return file('src') } | 目录下任意文件变化均触发 |
| @OutputFile | @OutputFile File outputFile | 标记单个输出文件 | @OutputFile File getOutput() { return file('build/output.txt') } | 必须生成该文件,否则构建失败 |
| @OutputDirectory | @OutputDirectory File outDir | 标记输出目录 | @OutputDirectory File getOut() { return file('build/classes') } | 目录会被清理或检查 |
| @Classpath | @Classpath FileCollection classpath | 标记类路径输入(带顺序和可变性检查) | @Classpath FileCollection getClasspath() { return configurations.compile } | 用于编译类路径 |
| @CompileClasspath | @CompileClasspath FileCollection cp | 优化的编译类路径(忽略内容哈希) | @CompileClasspath FileCollection getCompileClasspath() { ... } | 提升 Java 编译任务性能 |
| @Optional | @Optional @OutputFile File report | 标记输出可选(可不存在) | @Optional @OutputFile File getReport() { enabled ? file('report.txt') : null } | 避免因条件输出缺失报错 |
| @Nested | @Nested Config config | 标记嵌套的输入对象 | @Nested Map<String, Object> getOptions() { [encoding: 'UTF-8'] } | 内部字段变化也触发重建 |
| Internal | @org.gradle.api.tasks.Internal | 标记属性不参与增量判断 | @Internal String getTempDir() { return 'tmp' } | 用于临时或运行时属性 |
注意事项:
- 必须为任务定义至少一个 @Input 和 @Output 才能支持增量构建。
- @OutputFile 和 @OutputDirectory 标记的文件/目录必须被任务实际生成。
- @Classpath 比 @InputFiles 更智能,能处理 jar 顺序和元数据。
- 使用 @Optional 避免因条件输出导致的构建失败。
- 注解必须用于 getter 方法或属性字段。
5.3 增量构建原理与实践
| 概念/方法 | 说明 | 代码示例 | 注意事项 |
|---|---|---|---|
| 增量构建原理 | Gradle 在执行任务前检查其输入(@Input)和输出(@Output)是否变化。若未变,则跳过任务(UP-TO-DATE)。 | task process(type: ProcessTask) { inputDir = file('src'); outputFile = file('build/out.txt') } | 依赖正确使用输入输出注解 |
| UP-TO-DATE | 任务输入输出未变,Gradle 跳过执行 | > Task :process UP-TO-DATE | 大幅提升构建速度 |
| SKIP | 任务被显式跳过(如 onlyIf 条件为 false) | > Task :skipTask SKIPPED | 与 UP-TO-DATE 不同 |
| 输入快照 | Gradle 为每个 @Input 属性创建快照(值、文件内容哈希等) | 自动生成 | 存储在 .gradle/taskArtifacts/ |
| 输出快照 | 为每个 @Output 记录文件状态 | 自动生成 | 构建后更新快照 |
| 首次构建 | 无历史快照,所有任务必须执行 | gradle build | 输出被记录 |
| 第二次构建 | 比较快照,未变任务标记为 UP-TO-DATE | gradle build | 仅执行变化任务 |
| 清理影响 | delete buildDir 后快照丢失,下次全量构建 | gradle clean build | buildDir 是默认输出目录 |
| 自定义比较逻辑 | 通过注解控制比较行为(如 @PathSensitive) | @InputDirectory @PathSensitivity(RELATIVE) File src | 忽略绝对路径差异 |
| 禁用增量 | tasks.named('myTask').get().outputs.upToDateWhen { false } | outputs.upToDateWhen { false } | 强制任务每次都执行 |
注意事项:
- 增量构建依赖正确的 @Input/@Output 注解。
- 输出文件必须被任务实际生成,否则 Gradle 认为任务未完成。
- 避免在任务动作中修改输入文件。
- 使用
--info查看任务是否被 UP-TO-DATE 跳过。 - 多项目构建中,上游项目变更会触发下游重建。
5.4 任务条件执行(onlyIf)
| 方法名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| onlyIf(闭包) | task.onlyIf { boolean } | 根据条件决定是否执行任务 | task release(type: Upload) { onlyIf { project.hasProperty('release') } } | 闭包返回 false 则跳过任务 |
| onlyIf(内联) | task.onlyIf { it.name.length() > 5 } | 在任务定义中设置条件 | processFiles.onlyIf { sourceDir.exists() } | 条件在执行阶段评估 |
| 多条件组合 | onlyIf { cond1 && cond2 } | 组合多个判断逻辑 | onlyIf { project.os == 'linux' && debugEnabled } | 使用 Groovy 逻辑操作符 |
| 访问任务属性 | onlyIf { hasProperty('deploy') } | 检查项目或任务属性 | onlyIf { findProperty('env') == 'prod' } | 常用 -P 传参控制 |
| 平台判断 | onlyIf { System.getProperty('os.name').contains('Windows') } | 按操作系统条件执行 | onlyIf { JavaVersion.current().isJava8() } | 用于跨平台构建 |
| 文件存在性 | onlyIf { file('config.yml').exists() } | 根据文件是否存在决定 | onlyIf { sourceFiles.files.any() } | 避免空输入错误 |
| 跳过状态 | SKIPPED | 任务被 onlyIf 跳过时的状态 | > Task :release SKIPPED | 区别于 FAILED 或 UP-TO-DATE |
| 与 dependsOn 结合 | task A.dependsOn B; B.onlyIf { false } | 依赖任务被跳过,当前任务仍可执行 | 若 A 不依赖 B 输出,A 仍执行 | 但 A 可能因缺少输入失败 |
| 动态条件 | onlyIf { Math.random() > 0.5 } | 使用运行时动态值 | 不推荐,破坏可重现性 | 应基于稳定输入 |
| 移除条件 | task.onlyIf = null | 清除已设置的 onlyIf 条件 | release.onlyIf = null | 用于测试或动态控制 |
注意事项:
- onlyIf 在执行阶段评估,配置阶段代码仍会执行。
- 返回 false 时任务状态为 SKIPPED,不是 UP-TO-DATE。
- 条件应基于稳定输入(属性、文件状态),避免随机值。
- 被跳过任务的输出不会生成,依赖它的任务可能失败。
- 可用于实现 release、deploy 等需手动触发的任务。
5.5 任务规则(TaskRule)
| 方法名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| addRule | project.getTasks().addRule(...) | 添加动态任务规则 | project.tasks.addRule('Pattern: hello') { String taskName -> if (taskName.startsWith('hello')) { task(taskName) { doLast { println "Hello ${taskName - 'hello'}" } } } } | 匹配模式创建任务 |
| 规则描述 | String description | 规则的描述信息(可选) | 'Pattern: dist' | 在错误消息中显示 |
| 闭包参数 | { String taskName -> ... } | 接收任务名,返回 Task 或 null | { name -> if (name == 'magic') task(name) { ... } } | 必须返回 Task 实例或 null |
| 动态任务创建 | 无显式定义的任务 | 允许用户执行未定义的任务 | gradle helloWorld | 若规则匹配则自动创建 |
| 错误提示 | 若无匹配规则且任务不存在 | 报错:Task 'xxx' not found in root project | 提示可用任务或规则模式 | 规则应有清晰描述 |
| 与静态任务优先级 | 静态任务优先 | 若 helloWorld 已定义,则规则不生效 | 规则仅用于未定义任务 | 避免冲突 |
| 多规则顺序 | 按添加顺序匹配 | 先添加的规则先尝试匹配 | 可添加多个规则 | 后续规则可能被跳过 |
| 移除规则 | 不支持直接移除 | 需设计为条件返回 null | rule = project.tasks.addRule(...) // 无法 remove | Gradle API 未提供 removeRule |
| 使用场景 | 生成版本化任务、批量任务 | 如 dist2023, dist2024或 buildModuleA, buildModuleB | 减少重复定义 | |
| 替代方案 | tasks.register with name mapping | 推荐使用惰性注册替代规则 | tasks.register("hello${name}") { ... } | 更现代、可控 |
注意事项:
- 任务规则是遗留特性,官方推荐使用 tasks.register 动态创建任务。
- 规则应在配置阶段早期添加。
- 规则描述应清晰,帮助用户理解可用任务模式。
- 返回 null 表示不处理该任务名,继续尝试其他规则或报错。
- 由于无法移除规则,应谨慎使用,避免污染任务空间。
第六章:依赖管理(Dependencies)
6.1 依赖配置(configurations)
| 概念名称 | 说明 | 代码示例 | 注意事项 |
|---|---|---|---|
| Configuration | 依赖的逻辑分组,定义依赖的用途和可见性。 | configurations { myConfig } | 每个配置是一个 Configuration 对象 |
| 创建配置 | 使用 configurations {} 块创建自定义配置 | configurations { integrationTestImplementation; integrationTestRuntimeOnly } | 通常用于测试源集或插件扩展 |
| 扩展配置 | extendsFrom 实现配置间继承 | debugImplementation.extendsFrom(implementation) | 子配置包含父配置的所有依赖 |
| 默认配置 | Gradle 和插件自动创建的标准配置 | implementation, api, testImplementation 等 | Java 插件提供主要配置 |
| 配置状态 | 可被解析(resolve)、用于编译、打包等 | configurations.compileClasspath | 解析后形成依赖图 |
| 配置属性 | visible, transitive, extendsFrom, canBeResolved, canBeConsumed | canBeResolved = true | 控制配置行为 |
| canBeResolved | 配置是否可被解析下载 | configurations.myConfig.canBeResolved = true | implementation 不可解析,compileClasspath 可解析 |
| canBeConsumed | 配置是否可被其他项目消费 | canBeConsumed = true | 发布时使用 |
| visible | 是否对其他项目可见 | visible = true | 内部配置可设为 false |
| dependenciesOf | 查看某配置包含的依赖 | println configurations.implementation.allDependencies | 调试依赖关系 |
| incoming | 访问已解析的依赖(文件) | configurations.compileClasspath.incoming.files | 获取下载后的 jar 文件 |
| outgoing | 访问对外发布的依赖信息 | outgoing.artifact jar | 用于发布配置 |
注意事项:
- configurations 定义了依赖的作用域和生命周期。
- canBeResolved=true 的配置(如 compileClasspath)才能触发依赖下载。
- 自定义配置常用于多源集测试(如集成测试、性能测试)。
- 避免直接修改内建配置的 canBeResolved 或 canBeConsumed,可能导致插件异常。
6.2 常用依赖配置(implementation, api, compileOnly, runtimeOnly 等)
| 配置名称 | 用途 | 传递性 | 是否参与编译 | 是否打包 | 是否暴露给使用者 | 代码示例 | 注意事项 |
|---|---|---|---|---|---|---|---|
| api | 声明对外公开的 API 依赖 | ✅ 传递 | ✅ | ✅ | ✅ 是 | dependencies { api 'commons-lang:commons-lang3:3.12.0' } | 适用于库项目,使用者也能访问 |
| implementation | 声明内部实现依赖 | ❌ 不传递 | ✅ | ✅ | ❌ 否 | dependencies { implementation 'com.google.guava:guava:31.1-jre' } | 推荐用于大多数依赖,减少传递污染 |
| compileOnly | 仅用于编译,不打包也不传递 | ❌ | ✅ | ❌ | ❌ | dependencies { compileOnly 'javax.annotation:javax.annotation-api:1.3.2' } | 如 JSR-305 注解、Servlet API |
| runtimeOnly | 仅在运行时需要,不参与编译 | ✅ | ❌ | ✅ | ❌ | dependencies { runtimeOnly 'org.postgresql:postgresql:42.5.0' } | 如 JDBC 驱动、日志实现 |
| testImplementation | 测试代码的实现依赖 | ❌ | ✅ (测试) | ✅ (测试) | ❌ | dependencies { testImplementation 'junit:junit:4.13.2' } | JUnit、TestNG、Mockito 等 |
| testCompileOnly | 仅测试编译时需要 | ❌ | ✅ (测试) | ❌ | ❌ | dependencies { testCompileOnly 'org.junit.jupiter:junit-jupiter-api:5.9.2' } | JUnit 5 API |
| testRuntimeOnly | 仅测试运行时需要 | ✅ | ❌ | ✅ (测试) | ❌ | dependencies { testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.2' } | JUnit 5 引擎 |
| annotationProcessor | 注解处理器依赖 | ❌ | ✅ | ❌ | ❌ | dependencies { annotationProcessor 'com.google.dagger:dagger-compiler:2.44' } | 用于 Lombok, Dagger, MapStruct 等 |
| archives | 项目产生的构件(已过时) | - | - | - | - | - | 现在使用 components.java 发布 |
| default | 默认配置,包含所有可消费的依赖 | ✅ | - | ✅ | ✅ | compile project(':sub') 等旧语法 | 用于旧版项目依赖 |
注意事项:
- implementation 是首选,它隐藏内部依赖,提升构建性能和封装性。
- api 仅在你明确需要将依赖暴露给库的使用者时使用。
- compileOnly 和 runtimeOnly 组合使用可精确控制依赖生命周期。
- Java 9+ 模块化项目中,compileOnly 类似
requires static。 - 避免使用已废弃的 compile 配置。
6.3 添加外部依赖(dependencies 块)
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| Module 依赖 | group: '', name: '', version: '' | 完整坐标声明 | dependencies { implementation group: 'org.apache.commons', name: 'commons-math3', version: '3.6.1' } | 最标准方式 |
| 简化语法 | 'group:name:version' | Groovy DSL 简化写法 | dependencies { implementation 'org.springframework:spring-core:5.3.21' } | 推荐,简洁易读 |
| Map 语法 | [group: '', name: '', version: ''] | 显式 Map 写法 | dependencies { testImplementation([group: 'org.mockito', name: 'mockito-core', version: '4.6.1']) } | 适合动态构造 |
| 动态版本 | 'group:name:1.+' 或 'group:name:latest.integration' | 版本通配 | implementation 'log4j:log4j:1.2.+'runtimeOnly 'mysql:mysql-connector-java:8.0.+' | 不推荐,破坏可重现性 |
| 严格版本 | version { strictly 'x.y.z' } | 强制指定版本 | implementation('com.fasterxml.jackson.core:jackson-databind') { version { strictly '2.13.3' } } | 覆盖传递依赖的版本 |
| 排除传递依赖 | exclude module: 'module-name' | 移除特定传递依赖 | implementation('org.hibernate:hibernate-core:5.6.10.Final') { exclude module: 'slf4j-api' } | 解决冲突或减少包体积 |
| 排除所有传递依赖 | transitive = false | 关闭传递性 | implementation('com.google.code.gson:gson:2.8.9') { transitive = false } | 仅引入直接依赖 |
| 分类器(classifier) | classifier: 'sources' 或 'javadoc' | 指定构件分类 | compileOnly 'com.example:lib:1.0:tests' | 如源码、文档、特定平台库 |
| BOM 导入 | platform() 或 enforcedPlatform() | 导入版本管理 BOM | dependencies { implementation platform('org.springframework.boot:spring-boot-dependencies:2.7.0'); implementation 'org.springframework.boot:spring-boot-starter-web' } | 统一管理版本,避免冲突 |
| Kotlin DSL | implementation("group:name:version") | 在 .gradle.kts 中使用 | dependencies { implementation("org.jetbrains.kotlin:kotlin-stdlib:1.7.10") } | 使用括号和引号 |
注意事项:
- 推荐使用简化语法
'group:name:version'。 - 避免动态版本(1.+),应使用固定版本保证构建可重现。
- 使用 exclude 或 transitive = false 解决依赖冲突或瘦身。
- platform() 允许升级,enforcedPlatform() 强制锁定,按需选择。
- BOM 是管理大型项目依赖版本的最佳实践。
6.4 依赖版本管理与冲突解决
| 方法/概念 | 说明 | 代码示例 | 注意事项 |
|---|---|---|---|
| 冲突原因 | 多个路径引入同一模块不同版本 | A → C:1.0, B → C:2.0 | Gradle 需决定使用哪个版本 |
| 冲突解决策略 | 最高版本优先(默认) | 若无其他规则,使用 2.0 | 简单但可能引入不兼容版本 |
| 强制版本(force) | 强制使用指定版本 | implementation('log4j:log4j:1.2.17') { force = true } | 覆盖所有传递依赖中的版本 |
| 严格版本(strictly) | 声明所需版本,拒绝其他 | version { strictly '1.2.17' } | 若有冲突则构建失败,需手动解决 |
| 版本目录(Version Catalogs) | 在 gradle/libs.versions.toml 中集中管理版本 | [versions] guava = "31.1-jre"[libraries] guava = { group = "com.google.guava", name = "guava", version.ref = "guava" } | 推荐,集中、类型安全、易于升级 |
| 使用 Catalogs | 在 build.gradle 中引用 | dependencies { implementation libs.guava } | 通过 libs 访问 |
| 依赖约束(dependencyConstraints) | 在平台或项目中声明约束 | dependencyConstraints { constraint('com.fasterxml.jackson.core:jackson-databind') { version { strictly '[2.12.0, 2.14.0)'; prefer '2.13.3' } } } | 更灵活的版本控制 |
| 查看依赖树 | gradle dependencies | 输出所有配置的依赖树 | gradle dependencies --configuration implementation |
| 特定配置树 | --configuration <name> | 查看指定配置的依赖 | gradle dependencies --configuration runtimeClasspath |
| 忽略版本 | changing = true | 标记依赖为动态(如 SNAPSHOT) | implementation('com.mycompany:lib:1.0-SNAPSHOT') { changing = true } |
| 锁定文件(dependencyLocking) | 生成 lock 文件锁定版本 | gradle dependencies --write-locks | 保证 CI/CD 环境一致性 |
| 禁用传递依赖 | transitive = false | 阻止传递 | implementation('org.hibernate:hibernate-core') { transitive = false } |
注意事项:
- 版本目录(Version Catalogs)是现代 Gradle 推荐的版本管理方式。
- 优先使用 strictly 或 prefer 进行约束,而非 force(过于强硬)。
- 定期运行
gradle dependencies分析依赖树,避免”依赖地狱”。 - 在 CI/CD 中使用锁定文件确保构建一致性。
- changing = true 仅用于 SNAPSHOT 或动态构建,避免在生产环境滥用。
6.5 本地文件依赖与项目依赖(project(‘:xx’))
| 依赖类型 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| 本地 JAR 依赖 | files('libs/a.jar') 或 fileTree | 添加本地 jar 文件 | dependencies { implementation files('libs/utils.jar'); implementation fileTree('libs') } | 不推荐,难于管理 |
| 本地目录依赖 | fileTree(dir: 'prebuilt', include: '*.jar') | 从目录加载多个 jar | dependencies { runtimeOnly fileTree(dir: 'drivers', include: '*.jar') } | 适合第三方驱动 |
| 项目依赖(同构) | project(':subproject') | 依赖同一构建中的子项目 | dependencies { implementation project(':common'); testImplementation project(':testing-utils') } | 子项目需在 settings.gradle 中 include |
| 项目依赖(异构) | project(path: ':web', configuration: 'shadow') | 依赖子项目的特定配置 | implementation project(path: ':service', configuration: 'shadowJar') | 用于自定义构件 |
| 项目依赖传递性 | 取决于配置 | api 传递,implementation 不传递 | 子项目 A 的 api 依赖会传递给依赖 A 的项目 | 与外部依赖行为一致 |
| 多项目构建 | settings.gradle 中 include | 组织多个相关项目 | include 'web', 'service', 'common' | 根项目协调依赖和版本 |
| 构建顺序 | Gradle 自动确定 | 基于项目依赖关系 | 无需手动指定 | 自动处理 |
| 依赖替换(dependencySubstitution) | resolutionStrategy.eachDependency | 强制使用项目依赖 | dependencySubstitution { substitute module('com.mycompany:common') with project(':common') } | 用于本地开发调试远程库 |
| 文件依赖缺点 | 无法传递、难于版本控制 | - | - | 不支持传递依赖,团队协作困难 |
| 项目依赖优点 | 支持增量构建、传递性、版本一致 | - | - | 修改子项目会触发上游重建,开发效率高 |
注意事项:
- 尽量使用项目依赖而非本地文件,便于维护和传递。
- 本地 JAR 依赖应放入版本控制或私有仓库,避免路径问题。
- project(‘:sub’) 默认使用子项目的 default 配置,通常映射到 implementation 或 api。
- 在 resolutionStrategy 中使用 dependencySubstitution 可实现”本地覆盖远程”,方便调试。
- 多项目构建是组织大型应用或微服务的理想方式。
第七章:多项目构建(Multi-project Builds)
7.1 settings.gradle 与 include
| 概念 | 说明 | 代码示例 | 注意事项 |
|---|---|---|---|
| settings.gradle | 根项目中的配置文件,用于定义多项目结构,在初始化阶段执行 | // settings.gradlerootProject.name = 'myapp'include 'core', 'web', 'service' | 必须位于根项目目录 |
| rootProject.name | 设置根项目名称 | rootProject.name = 'enterprise-app' | 默认为根目录名 |
| include | 声明子项目,创建项目实例 | include 'moduleA'include ':utils', ':api' | 路径使用 : 分隔 |
| includeFlat | 包含同一层级的子项目(平级目录) | includeFlat 'shared-lib', 'tools' | 子项目与 settings.gradle 同目录 |
| projectDir 自定义 | 修改子项目目录位置 | include ':database'project(':database').projectDir = new File(settingsDir, '../external/db') | 灵活组织项目结构 |
| 动态 include | 使用 Groovy 逻辑动态包含项目 | file('modules').listFiles().each { if (it.isDirectory()) { include ":${it.name}" } } | 适合模块化项目 |
| 复合构建(Composite Build) | 将独立构建合并为一个整体 | includeBuild '../library'includeBuild 'https://github.com/user/repo.git' | 可替换依赖为源码构建 |
| buildSrc | 特殊子项目,存放构建脚本代码 | // 目录结构:buildSrc/ src/main/groovy/... build.gradle | 自动编译并加入构建 classpath |
| 初始化脚本 | -I 参数指定 init.gradle,影响所有构建 | gradle -I init.gradle build | 企业级全局配置 |
注意事项:
- settings.gradle 是多项目构建的入口,必须存在。
- include 定义的路径是项目路径(project path),不一定是文件系统路径。
- buildSrc 是一个独立的 Gradle 构建,用于扩展 Gradle 本身。
- 复合构建可用于同时开发多个相关库。
7.2 子项目结构与配置
| 概念 | 说明 | 代码示例 | 注意事项 |
|---|---|---|---|
| 子项目目录 | 每个子项目有独立目录,包含自己的 build.gradle | myapp/├── settings.gradle├── core/│ └── build.gradle└── web/ └── build.gradle | 推荐结构 |
| 子项目 build.gradle | 配置该项目的插件、依赖、任务等 | // web/build.gradleapply plugin: 'java'dependencies { implementation project(':core') } | 独立于其他项目 |
| 插件应用 | 在子项目中应用特定插件 | apply plugin: 'java-library'apply plugin: 'war' | 按需启用功能 |
| 属性继承 | 子项目可访问根项目属性 | // 在子项目中:println rootProject.nameversion = rootProject.ext.version | 推荐共享版本号 |
| 项目特定配置 | 为子项目设置唯一配置 | // 在 core/build.gradle 中:sourceCompatibility = 11 | 允许差异化 |
| 测试源集 | 每个子项目可有自己的测试 | test { useJUnitPlatform() } | 独立运行测试 |
| 构建输出隔离 | 子项目的构建输出默认在各自 buildDir | buildDir = "out" // 影响当前项目 | 避免冲突 |
| 资源文件 | 子项目管理自己的资源 | src/main/resources/config.properties | 打包时独立处理 |
| 自定义任务 | 在子项目中定义专用任务 | task deploy { ... } // 仅 web 项目有 | 按需扩展 |
| 多语言支持 | 不同子项目可用不同技术栈 | core: Java, web: Kotlin, mobile: Android | 提升灵活性 |
注意事项:
- 每个子项目是相对独立的构建单元。
- 应尽量保持子项目职责单一(SRP)。
- 避免子项目间过度耦合。
- 使用统一的代码风格和构建规范。
7.3 项目间依赖
| 依赖类型 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| 内部项目依赖 | project(':subproject') | 依赖同一构建中的子项目 | dependencies { implementation project(':common') } | 子项目需在 settings.gradle 中声明 |
| api 传递依赖 | api project(':lib') | 对外暴露依赖 | // 在 utils 项目中:api project(':logging')// 则使用 utils 的项目也能访问 logging | 类似 Maven 的 compile |
| implementation 依赖 | implementation project(':model') | 内部实现依赖,不传递 | dependencies { implementation project(':database') } | 推荐方式,减少传递污染 |
| 测试依赖 | testImplementation project(':test-utils') | 仅测试时依赖 | testImplementation project(path: ':testing', configuration: 'default') | 隔离测试辅助代码 |
| 运行时依赖 | runtimeOnly project(':plugin-system') | 运行时需要,编译时不参与 | runtimeOnly project(':report-engine') | 如插件、驱动 |
| 强制特定配置 | project(path: ':sub', configuration: 'shadow') | 使用子项目的自定义配置 | implementation project(path: ':web', configuration: 'archives') | 用于 fat jar 等场景 |
| 循环依赖检测 | Gradle 自动检测 | A → B, B → A | 构建失败,提示循环依赖 | 应重构避免 |
| 构建顺序 | Gradle 自动确定 | 依赖项目先构建 | 无需手动指定 | 基于任务依赖图 |
| 传递性行为 | 与外部依赖一致 | implementation 不传递,api 传递 | 统一模型,易于理解 | 行为一致 |
| 查看依赖树 | gradle dependencies | 分析项目依赖关系 | gradle :web:dependencies --configuration runtimeClasspath | 调试依赖问题 |
注意事项:
- 项目依赖会触发被依赖项目的构建。
- 使用 implementation 而非 api 以降低耦合。
- 避免循环依赖,可通过引入中间模块解决。
- 项目依赖支持增量构建,修改子项目会触发上游重建。
7.4 共享配置与约定(allprojects, subprojects)
| 方法 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| allprojects | allprojects { ... } | 配置所有项目(含根项目) | allprojects { version = '1.0.0'; group = 'com.example' } | 根项目也受影响 |
| subprojects | subprojects { ... } | 仅配置子项目(不含根项目) | subprojects { apply plugin: 'java'; java.sourceCompatibility = JavaVersion.VERSION_11 } | 更常用 |
| 集中式依赖管理 | 在根项目定义 ext 或使用 versions.toml | ext { junitVersion = '5.9.2' }// 子项目中:testImplementation "org.junit.jupiter:junit-jupiter-api:${junitVersion}" | 统一版本 | |
| 共享仓库 | 在 allprojects 中定义 repositories | allprojects { repositories { mavenCentral(); google() } } | 避免重复配置 | |
| 共享插件 | 在 subprojects 中应用公共插件 | subprojects { apply plugin: 'checkstyle'; checkstyle.toolVersion = '10.3' } | 统一代码质量工具 | |
| 共享任务 | 定义通用任务 | subprojects { task printName { doLast { println name } } } | 所有子项目都有该任务 | |
| 条件配置 | 结合 if 判断项目名称 | subprojects { if (name.contains('web')) { apply plugin: 'war' } } | 实现差异化配置 | |
| 配置顺序 | 先 allprojects,后 subprojects,再项目自身 | allprojects { ... }; subprojects { ... }; // 子项目 build.gradle 覆盖 | 后定义优先 | |
| 约定优于配置 | 设立团队构建规范 | allprojects { tasks.withType(JavaCompile) { options.encoding = 'UTF-8' } } | 提升一致性 | |
| 使用 Catalogs 共享 | 在 libs.versions.toml 中定义 | [libraries] junit-api = { module = "org.junit.jupiter:junit-jupiter-api", version = "5.9.2" }// 子项目:dependencies { testImplementation libs.junit.api } | 现代推荐方式 |
注意事项:
- allprojects 和 subprojects 应写在根项目的 build.gradle 中。
- 推荐使用 Version Catalogs (gradle/libs.versions.toml) 进行集中式依赖管理。
- 共享配置应尽量简洁,避免过度约束。
- 子项目可覆盖共享配置以满足特殊需求。
第八章:插件(Plugins)使用与开发
8.1 插件的作用与分类(二进制 vs 脚本插件)
| 概念 | 说明 | 代码示例 | 注意事项 |
|---|---|---|---|
| 插件作用 | 封装可复用的构建逻辑,如任务、约定、依赖配置等 | apply plugin: 'java' // 添加 Java 编译、测试等任务 | 提升构建脚本可维护性 |
| 二进制插件 | 编译后的类(JAR),实现 Plugin 接口,功能强大 | class MyPlugin implements Plugin { void apply(Project project) { project.task('hello') { ... } } } | 可发布到仓库,团队共享 |
| 脚本插件 | .gradle 文件,包含可被其他脚本应用的配置代码 | // common.gradleapply plugin: 'java'; group = 'com.example'; version = '1.0' | 适用于项目内共享配置 |
| 内建插件 | Gradle 自带插件(如 java, war, kotlin) | apply plugin: 'java-library' | 无需额外依赖 |
| 社区插件 | Gradle Plugin Portal 上的第三方插件 | id 'org.springframework.boot' version '3.1.0' | 丰富生态系统 |
| 插件 ID | 唯一标识符,用于应用插件 | 'java', 'com.android.application' | 通常为反向域名 |
| 应用时机 | 插件在配置阶段被应用,修改项目结构 | apply from: 'script.gradle'apply plugin: 'my.plugin' | 应用后立即执行逻辑 |
| 插件幂等性 | 同一插件多次应用通常只生效一次 | apply plugin: 'java'; apply plugin: 'java' // 无副作用 | 避免重复配置 |
| 插件依赖 | 插件可依赖其他插件自动应用 | plugins { id 'java' } // 自动应用 'jvm-component' 等 | 减少手动配置 |
| buildSrc 插件 | 在 buildSrc 中开发的二进制插件 | // buildSrc/src/main/groovy/MyPlugin.groovy; class MyPlugin implements Plugin | 仅对当前构建可用 |
注意事项:
- 二进制插件适合跨项目复用,脚本插件适合项目内共享。
- 插件应在 build.gradle 的早期应用,确保后续配置生效。
- 避免在插件中执行耗时操作,影响配置阶段性能。
- 脚本插件使用
apply from:,二进制插件使用apply plugin:或plugins {}。
8.2 应用官方插件(apply plugin: ‘java’)
| 插件名称 | 作用 | 应用方式 | 添加的任务 | 注意事项 |
|---|---|---|---|---|
| java | 标准 Java 项目构建 | apply plugin: 'java' | compileJava, processResources, classes, test, jar, javadoc | 基础插件,提供编译、测试、打包 |
| java-library | Java 库项目(支持 api/implementation) | apply plugin: 'java-library' | 同 java,但配置更细粒度 | 推荐用于发布库 |
| war | 构建 WAR 包(Web 应用) | apply plugin: 'war' | war, compileJsp | 依赖 java 插件 |
| application | 可执行 JVM 应用 | apply plugin: 'application' | startScripts, installDist, run | 需设置 mainClass |
| groovy | Groovy 项目支持 | apply plugin: 'groovy' | compileGroovy, compileTestGroovy | 替代 java 插件 |
| scala | Scala 项目支持 | apply plugin: 'scala' | compileScala, compileTestScala | 需配置 Scala 版本 |
| kotlin-jvm | Kotlin JVM 项目 | apply plugin: 'kotlin' | compileKotlin, compileTestKotlin | 需添加 kotlin-gradle-plugin 依赖 |
| eclipse | 生成 Eclipse 项目文件 | apply plugin: 'eclipse' | eclipse, eclipseClasspath, eclipseProject | 用于 IDE 集成 |
| idea | 生成 IntelliJ IDEA 文件 | apply plugin: 'idea' | idea, ideaModule, ideaProject | 推荐使用 IDEA 直接导入 |
| signing | 签名构件(如 JAR) | apply plugin: 'signing' | signArchives, signMavenJavaPublication | 发布到 Maven Central 所需 |
| maven-publish | 发布构件到 Maven 仓库 | apply plugin: 'maven-publish' | publish, publishToMavenLocal | 现代发布方式 |
注意事项:
- 大多数插件会自动应用其前置插件(如 war 自动应用 java)。
- 应用插件后,会自动创建源集(sourceSets)、任务、配置等。
- 插件文档是了解其功能的最佳来源。
- 避免同时应用冲突的插件(如 java 和 android-library)。
8.3 使用插件 DSL(plugins block)
| 特性 | 说明 | 代码示例 | 注意事项 |
|---|---|---|---|
| plugins {} 块 | 新的插件应用方式,位于 build.gradle 顶部 | plugins { id 'java-library'; id 'org.springframework.boot' version '3.1.0' } | 必须在文件顶部,无前置语句 |
| 插件版本声明 | 在 plugins 块中直接指定版本 | id 'com.github.johnrengelman.shadow' version '8.1.1' | 简化依赖管理 |
| 插件解析 | 从 Gradle Plugin Portal 或自定义仓库下载 | 仓库配置在 settings.gradle 中 | 可配置 pluginManagement |
| 优势:性能 | 插件在配置前解析,提升性能 | - | 比 apply plugin 更快 |
| 优势:简洁 | 一行声明插件和版本 | id 'java' | 无需 buildscript 块 |
| 优势:约束 | 强制插件版本集中管理 | - | 减少版本冲突 |
| 插件管理(pluginManagement) | 在 settings.gradle 中集中管理插件版本 | // settings.gradle; pluginManagement { plugins { id 'org.springframework.boot' version '3.1.0' } repositories { gradlePluginPortal(); mavenCentral() } } | 全局统一版本 |
| 与 buildscript 对比 | 旧方式需在 buildscript.dependencies 中声明 | buildscript { dependencies { classpath 'org.springframework.boot:spring-boot-gradle-plugin:3.1.0' } }; apply plugin: 'org.springframework.boot' | plugins {} 更现代 |
| 限制:不可条件应用 | plugins {} 不支持 if 等条件逻辑 | 条件应用仍需 apply(plugin: 'id') | 对于动态插件需使用 apply |
| 插件别名 | 在 versions.toml 中定义别名 | [plugins] spring-boot = { id = "org.springframework.boot", version = "3.1.0" }// build.gradle: plugins { alias(libs.plugins.spring.boot) } | 提升可维护性 |
注意事项:
- plugins {} 是推荐的现代方式,应优先使用。
- 必须放在 build.gradle 文件的最顶部,前面不能有任何语句(注释除外)。
- 无法在 plugins {} 中使用项目属性或条件逻辑。
- 对于需要动态决定是否应用的插件,仍需使用
apply(plugin: 'id')。
8.4 自定义插件开发(实现 Plugin 接口)
| 方法/概念 | 说明 | 代码示例 | 注意事项 |
|---|---|---|---|
| 实现 Plugin 接口 | 创建类实现 Plugin<T>,T 通常是 Project | class GreetingPlugin implements Plugin { void apply(Project project) { project.task('hello') { doLast { println "Hello from ${project.name}" } } } } | 核心入口 |
| apply 方法 | 插件被应用时执行的逻辑 | void apply(Project project) { ... } | 可访问项目对象,添加任务、配置等 |
| 插件 ID | 通过 META-INF 定义 | // resources/META-INF/gradle-plugins/com.example.greeting.properties; implementation-class=com.example.GreetingPlugin | 文件名即 ID |
| 插件属性文件 | 声明实现类的全限定名 | implementation-class=com.example.MyPlugin | 位于 buildSrc 或独立模块 |
| 添加任务 | 在 apply 中创建任务 | project.tasks.register('customTask') { doLast { ... } } | 推荐使用 register |
| 配置扩展(Extension) | 为插件提供配置 DSL | project.extensions.create('greeting', GreetingExtension)// 使用:greeting { message = 'Hi' } | 提升可用性 |
| 扩展类定义 | 定义配置属性 | class GreetingExtension { String message = 'Hello'; String subject = 'World' } | 可在 build.gradle 中配置 |
| 访问扩展 | 在任务中读取用户配置 | project.tasks.register('sayHello') { doLast { def ext = project.extensions.getByType(GreetingExtension); println "${ext.message} ${ext.subject}" } } | 解耦配置与逻辑 |
| 应用插件 | 在项目中使用自定义插件 | plugins { id 'com.example.greeting' } | 需确保插件在 classpath |
| buildSrc 开发 | 在 buildSrc 中直接编写插件 | buildSrc/src/main/groovy/com/example/GreetingPlugin.groovy | 仅对当前构建有效,无需发布 |
| 独立模块开发 | 创建独立的 Java/Groovy 模块开发插件 | 新建项目,添加 gradleApi() 依赖 | 可发布到私有或公共仓库 |
注意事项:
- 插件类必须有公共无参构造函数。
- 使用 project.extensions.create() 添加配置 DSL,提升用户体验。
- 推荐在 buildSrc 中快速开发和测试插件。
- 发布前进行充分测试,确保兼容性。
- 文档化插件的使用方法和配置选项。
8.5 插件的发布与复用
| 方法 | 说明 | 代码示例 | 注意事项 |
|---|---|---|---|
| 发布到 Gradle Plugin Portal | 官方插件仓库,便于分享 | 使用 gradlePlugin DSL 和 publishPlugins 任务 | 需注册账号,审核机制 |
| 发布到 Maven 仓库 | 私有或公共 Maven 仓库(如 Nexus) | publishing { publications { maven(MavenPublication) { from components.java } } repositories { maven { url "https://repo.mycompany.com"; credentials { ... } } } } | 企业内部常用 |
| 插件元数据 | 包含 ID、实现类、标签、网站等 | // plugin.xml 或 build.gradle 配置 | 影响在 Portal 中的展示 |
| 使用插件标记(markers) | 发布独立的插件标记模块 | publishPluginMarkerMavenPublication // 自动生成 | 简化版本管理 |
| 版本管理 | 语义化版本(SemVer) | 1.0.0, 1.0.1, 2.0.0 | 重大变更应升级主版本 |
| 在其他项目中使用 | 通过 plugins {} 应用已发布的插件 | plugins { id 'com.example.myplugin' version '1.2.0' } | 需能访问仓库 |
| 本地测试发布 | 使用 mavenLocal() 测试 | publishing { repositories { mavenLocal() } }// 测试项目:plugins { id 'com.example.plugin' version '1.0.0' } | 验证发布流程 |
| 插件文档 | 编写 README 和使用说明 | // src/docs/asciidoc/guide.adoc | 提高可用性 |
| 依赖管理 | 插件自身的依赖 | dependencies { implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.3'; gradleApi(); localGroovy() } | 避免传递不必要的依赖 |
| 兼容性测试 | 测试不同 Gradle 版本 | 在多个 Gradle 版本下运行测试 | 确保向后兼容 |
注意事项:
- 发布前确保插件稳定、有文档、有测试。
- 推荐发布到私有 Maven 仓库用于企业内部共享。
- 发布到 Plugin Portal 适合开源项目。
- 使用 gradleApi() 依赖 Gradle 自身 API,版本与构建环境一致。
- 避免在插件中引入大型或不稳定的第三方库。
第九章:Gradle 与 Java 项目集成
9.1 Java 插件引入与默认任务
| 概念 | 说明 | 代码示例 | 注意事项 |
|---|---|---|---|
| 应用 Java 插件 | 启用 Java 项目构建功能 | plugins { id 'java' }// 或旧方式:apply plugin: 'java' | 必须应用插件才能使用 Java 任务 |
| 插件自动配置 | 自动添加仓库、源集、任务等 | - | 无需手动创建 compileJava 等任务 |
| 默认源集 | 创建 main 和 test 源集 | src/main/java, src/test/java | 遵循标准布局 |
| 核心任务:compileJava | 编译主源集 Java 代码 | gradle compileJava | 生成 class 文件到 build/classes/java/main |
| 核心任务:compileTestJava | 编译测试源集代码 | gradle compileTestJava | 依赖 compileJava |
| 核心任务:test | 编译并运行测试 | gradle test | 使用 JUnit 3/4/5 或 TestNG |
| 核心任务:jar | 打包主源集为 JAR | gradle jar | 输出到 build/libs/ |
| 核心任务:javadoc | 生成 Java 文档 | gradle javadoc | 输出到 build/docs/javadoc |
| 核心任务:clean | 删除 build 目录 | gradle clean | 清理构建输出 |
| 任务依赖关系 | 任务间自动建立依赖 | test.dependsOn classesjar.dependsOn classes | 构建时自动触发前置任务 |
| 查看任务 | 列出项目所有任务 | gradle tasksgradle tasks --all | 了解可用任务 |
| 生命周期任务 | build 聚合常用任务 | gradle build // 等价于:classes, test, jar | 推荐用于完整构建 |
| 工件(Artifacts) | jar 任务生成的 JAR 文件自动注册为工件 | - | 可被其他项目依赖 |
| Java 版本配置 | 设置源和目标兼容性 | java { sourceCompatibility = JavaVersion.VERSION_11; targetCompatibility = JavaVersion.VERSION_11 } | 推荐使用 java 工件属性 |
| 默认仓库 | 无默认仓库,需手动配置 | repositories { mavenCentral() } | 否则无法下载依赖 |
注意事项:
- 应用 java 插件后,Gradle 会自动创建标准 Java 项目的构建模型。
- build 任务是推荐的完整构建命令,包含编译、测试、打包。
- 任务执行遵循依赖关系,调用 test 会自动执行 compileJava 和 compileTestJava。
- 若需运行应用,需额外配置 JavaExec 任务或应用 application 插件。
9.2 源码目录结构配置(sourceSets)
| 概念 | 说明 | 代码示例 | 注意事项 |
|---|---|---|---|
| sourceSets | 定义源代码和资源的逻辑分组 | sourceSets { main { ... }; test { ... } } | Java 插件默认创建 main 和 test |
| 修改主源集 | 自定义 main 的源目录 | sourceSets { main { java { srcDirs = ['src/main/java', 'generated/src'] }; resources { srcDirs = ['src/main/resources'] } } } | 可添加多个目录 |
| 添加测试源集 | 自定义 test 目录 | sourceSets { test { java { srcDir 'src/test/java' } } } | 支持 JUnit/TestNG |
| 创建自定义源集 | 定义新源集(如 integrationTest) | sourceSets { integrationTest { compileClasspath += main.output + test.runtimeClasspath; runtimeClasspath += output + compileClasspath } }; dependencies { integrationTestImplementation project(':common'); integrationTestRuntimeOnly 'org.h2:h2:2.1.214' } | 用于特殊测试类型 |
| 源集属性 | java, resources, output, compileClasspath, runtimeClasspath | sourceSets.main.java.srcDirssourceSets.test.output.classesDirs | 访问源集配置 |
| 添加依赖到源集 | 为特定源集配置依赖 | dependencies { mainImplementation 'com.google.guava:guava:31.1-jre'; testImplementation 'org.mockito:mockito-core:4.6.1' } | 配置名格式:{sourceSet}{Configuration} |
| 输出目录配置 | 自定义编译输出路径 | sourceSets.main.output.classesDirs = new File(buildDir, 'custom-classes') | 默认在 build/classes |
| 排除/包含文件 | 过滤源文件 | sourceSets.main.java { exclude 'com/example/internal/'; include '**/Public*.java' } | 支持 Ant 风格模式 |
| 多语言支持 | 在同一源集中混合语言 | sourceSets.main { java { srcDir 'src/main/java' }; resources { srcDir 'src/main/resources' } } | Java 插件默认支持 |
| 源集任务 | 每个源集生成独立的编译任务 | compileIntegrationTestJava, processIntegrationTestResources | 可单独执行 |
注意事项:
- 自定义源集不会自动创建依赖配置,需手动定义(如 integrationTestImplementation)。
- 需正确设置 compileClasspath 和 runtimeClasspath 以确保类路径完整。
- 源集是组织代码的逻辑单元,可对应不同构建场景。
- 修改 srcDirs 会覆盖默认目录,使用 srcDir 添加目录更安全。
9.3 编译、测试、打包配置
| 配置项 | 说明 | 代码示例 | 注意事项 |
|---|---|---|---|
| 编译器选项 | 配置 javac 参数 | tasks.withType(JavaCompile) { options.encoding = 'UTF-8'; options.compilerArgs << '-Xlint:unchecked'; options.release = 11 } | 推荐使用 options.release |
| 增量编译 | Gradle 默认启用 | - | 修改的类及其依赖类会被重新编译 |
| 编译失败处理 | 控制失败行为 | compileJava { options.failOnError = true; options.warnings = true } | 可关闭警告 |
| 测试配置 | 配置 Test 任务 | test { useJUnitPlatform(); testLogging { events 'passed', 'skipped', 'failed' }; systemProperty 'property.name', 'value' } | 设置日志、系统属性 |
| 测试分组 | 运行特定测试 | test { include '**/UserServiceTest.class'; exclude '**/*IntegrationTest.class' } | 使用 include/exclude |
| 并行测试 | 启用并行执行 | test { maxParallelForks = 4 } | 提升测试速度 |
| 测试报告 | 生成 HTML 报告 | test { reports { html.required = true; junitXml.required = true } } | 默认生成 XML 和 HTML |
| 打包配置 | 配置 jar 任务 | jar { duplicatesStrategy = DuplicatesStrategy.EXCLUDE } | 处理重复文件 |
| 编译依赖 | implementation, api, compileOnly | dependencies { implementation 'org.slf4j:slf4j-api:2.0.7'; compileOnly 'org.projectlombok:lombok:1.18.28' } | 正确使用配置 |
| 测试依赖 | testImplementation, testRuntimeOnly | testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2'; testRuntimeOnly 'org.junit.platform:junit-platform-launcher:1.9.2' | JUnit 5 需要 engine |
| 运行时依赖 | runtimeOnly | runtimeOnly 'com.h2database:h2:2.1.214'runtimeOnly 'mysql:mysql-connector-java:8.0.33' | 数据库驱动等 |
| 构建跳过 | 跳过特定任务 | gradle build -x test// 或在脚本中:test.enabled = false | 快速构建,但不运行测试 |
注意事项:
- 推荐使用 options.release 而非 sourceCompatibility 和 targetCompatibility(Java 9+)。
- useJUnitPlatform() 是使用 JUnit 5 的必需配置。
- maxParallelForks 应根据 CPU 核心数调整。
- 避免在生产构建中跳过测试(-x test)。
9.4 生成 JAR 文件与 MANIFEST 配置
| 配置项 | 说明 | 代码示例 | 注意事项 |
|---|---|---|---|
| jar 任务 | 生成 JAR 文件 | jar { // 配置 } | 默认任务,输出到 build/libs |
| MANIFEST.MF | JAR 包内的元数据文件 | jar { manifest { attributes('Implementation-Title': project.name, 'Implementation-Version': project.version, 'Main-Class': 'com.example.Main') } } | 可用于指定主类 |
| 主类配置(可执行 JAR) | 指定程序入口点 | jar { manifest { attributes 'Main-Class': 'com.example.App' } } | 需配合类路径或 fat jar |
| 添加文件到 JAR | 将额外文件打包 | jar { from 'README.txt'; from(sourceSets.main.output) { exclude '**/*.class' } } | 支持 from + exclude/include |
| 文件重命名 | 在 JAR 中重命名条目 | jar { rename '(.+).properties', 'config/$1.properties' } | 使用正则表达式 |
| 排除文件 | 防止文件被打包 | jar { exclude 'META-INF/*.SF', 'META-INF/*.DSA' } | 如排除签名文件 |
| 多个 JAR | 生成不同用途的 JAR | task sourcesJar(type: Jar) { archiveClassifier = 'sources'; from sourceSets.main.allSource }; artifacts { archives sourcesJar } | 发布源码 JAR |
| Javadoc JAR | 生成文档 JAR | task javadocJar(type: Jar) { archiveClassifier = 'javadoc'; from javadoc } | 发布 Javadoc |
| 定制 JAR 名称 | 修改输出文件名 | jar { archiveBaseName = 'myapp'; archiveVersion = '1.0'; archiveExtension = 'jar' } | 或使用 archiveFileName |
| 清单继承 | 从其他清单合并属性 | jar { manifest { from 'src/config/MANIFEST.MF'; attributes 'Built-By': System.getProperty('user.name') } } | 支持多个 from |
注意事项:
- 仅配置 Main-Class 的 JAR 无法直接运行(缺少依赖)。
- 要创建可运行的”fat jar”,需使用 shadow 插件或手动配置 jar 任务包含依赖。
- archiveClassifier 用于区分不同类型的构件(如 sources, javadoc)。
- MANIFEST 配置是发布库或应用的重要步骤。
9.5 运行 Java 应用(JavaExec)
| 配置项 | 说明 | 代码示例 | 注意事项 |
|---|---|---|---|
| JavaExec 任务 | 执行 Java 应用程序 | task runApp(type: JavaExec) { mainClass = 'com.example.Main'; classpath = sourceSets.main.runtimeClasspath } | 核心方式 |
| mainClass | 指定主类 | mainClass = 'com.example.Launcher'// 或 main = 'com.example.Launcher' | 必须设置 |
| classpath | 设置类路径 | classpath = sourceSets.main.runtimeClasspath// 或 classpath configurations.runtimeClasspath, sourceSets.main.output | 包含编译输出和依赖 |
| JVM 参数 | 传递给 JVM 的参数 | jvmArgs = ['-Xmx512m', '-XX:+HeapDumpOnOutOfMemoryError'] | 如内存、GC 选项 |
| 程序参数 | 传递给主方法的参数 | args = ['arg1', 'arg2'] | String 列表 |
| 系统属性 | 设置系统属性 | systemProperties = [ 'prop.name': 'value', 'debug': 'true' ] | 可在代码中通过 System.getProperty 读取 |
| 环境变量 | 设置环境变量 | environment = [ 'ENV_VAR': 'prod' ] | 影响进程环境 |
| 标准输入/输出 | 重定向流 | standardInput = new ByteArrayInputStream("input\n".bytes); standardOutput = new FileOutputStream('output.log') | 高级用法 |
| 应用 application 插件 | 简化应用运行 | plugins { id 'application' }; mainClass = 'com.example.App' // 生成 run 任务 | 推荐用于可执行应用 |
| run 任务 | application 插件提供的快捷任务 | gradle run // 直接运行应用 | 自动配置 classpath 和 mainClass |
| 调试运行 | 启用远程调试 | jvmArgs '-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005' | 便于 IDE 调试 |
注意事项:
- JavaExec 是运行 Java 应用的底层任务,application 插件在此基础上提供更高层的 run 任务。
- 必须正确设置 classpath,否则会报 ClassNotFoundException。
- args 和 jvmArgs 区分清楚:args 传给 main(String[] args),jvmArgs 传给 JVM。
- 使用 application 插件可以自动生成启动脚本(startScripts 任务)。
第十章:构建脚本优化与最佳实践
10.1 脚本模块化(apply from:)
| 方法 | 说明 | 代码示例 | 注意事项 |
|---|---|---|---|
| apply from: | 将公共配置提取到外部 .gradle 文件 | // common.gradle; group = 'com.example'; version = '1.0.0'// build.gradle; apply from: 'common.gradle' | 基础模块化方式 |
| 按功能拆分 | 将不同配置分离到不同文件 | config.gradle:项目元数据 dependencies.gradle:依赖声明 publishing.gradle:发布配置 quality.gradle:代码质量任务 | 提升可维护性 |
| 子项目模块化 | 在子项目中应用共享脚本 | // 子项目 build.gradle; apply from: '../gradle/java-conventions.gradle' | 实现跨项目一致性 |
| 参数化脚本 | 通过扩展属性传递参数 | // script.gradle; ext.projectType = project.findProperty('projectType') ?: 'library'// 应用时设置:project.ext.projectType = 'service'; apply from: 'config.gradle' | 增强脚本灵活性 |
| 动态应用 | 根据条件决定是否应用脚本 | if (project.hasProperty('enableMonitoring')) { apply from: 'monitoring.gradle' } | 实现可选功能 |
| 集中式脚本管理 | 在 gradle/ 目录下组织脚本 | project/├── gradle/│ ├── conventions/│ │ ├── java.gradle│ │ └── test.gradle│ └── scripts/│ └── deploy.gradle└── build.gradle | 推荐项目结构 |
| 脚本作用域 | 外部脚本与主脚本共享 Project 对象 | // in external.gradle; println "Applying to $project.name" | 上下文一致 |
| 避免循环引用 | 不要出现脚本互相 apply | - | 会导致构建失败 |
| 使用 buildSrc 模块化 | 将复杂逻辑移到 buildSrc 的二进制插件 | // buildSrc 模块中开发插件; apply plugin: 'com.example.java-conventions' | 更强大,支持编译时检查 |
| 版本控制 | 将模块化脚本纳入版本控制 | git add gradle/*.gradle | 团队共享配置 |
注意事项:
- 模块化脚本是提升大型项目可维护性的关键。
- 避免过度拆分,保持逻辑内聚。
- 为外部脚本编写清晰的注释和文档。
- apply from: 适用于项目内共享,跨项目复用推荐使用二进制插件。
10.2 使用 gradle.properties 管理属性
| 属性类型 | 说明 | 代码示例 | 注意事项 |
|---|---|---|---|
| 项目级属性 | 位于项目根目录,影响当前构建 | // gradle.properties; version=1.2.0; org.gradle.jvmargs=-Xmx2g | 最常用 |
| 用户级属性 | 位于 ~/.gradle/gradle.properties,影响所有构建 | // ~/.gradle/gradle.properties; org.gradle.daemon=true; systemProp.http.proxyHost=proxy.company.com | 全局配置,如代理、守护进程 |
| 命令行传参 | 使用 -P 传递属性 | gradle build -Pversion=2.0.0-SNAPSHOT | 优先级最高 |
| 访问属性 | 在 build.gradle 中通过 project.property 或直接引用 | println project.versionprintln version // Groovy DSL 支持省略 project. | 推荐使用 project.version |
| 类型转换 | 属性默认为 String,需手动转换 | ext.maxHeap = project.findProperty('maxHeap')?.toInteger() ?: 1024 | 注意空值处理 |
| 敏感信息 | 避免在 gradle.properties 中存储密码 | 推荐使用环境变量或 ~/.gradle/gradle.properties | 安全考虑 |
| 构建性能属性 | 配置 Gradle 运行时行为 | org.gradle.parallel=true; org.gradle.configureondemand=true; org.gradle.caching=true | 显著提升性能 |
| JVM 参数 | 设置 Gradle 守护进程的 JVM 选项 | org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 | 根据项目大小调整内存 |
| 仓库认证 | 配置私有仓库凭据 | mavenRepoUsername=user; mavenRepoPassword=pass// build.gradle 中:credentials { username mavenRepoUsername; password mavenRepoPassword } | 分离配置与代码 |
| 环境特定属性 | 为不同环境定义属性 | // gradle.properties; api.url.dev=http://localhost:8080; api.url.prod=https://api.example.com | 结合 Profile 使用 |
注意事项:
- 项目级 gradle.properties 应提交到版本控制。
- 用户级 ~/.gradle/gradle.properties 通常不提交,用于本地配置。
- 属性优先级:命令行 -P > 项目级 > 用户级。
- 使用 findProperty() 可安全处理可能不存在的属性。
10.3 构建缓存与性能优化
| 优化策略 | 说明 | 配置方式 | 效果 |
|---|---|---|---|
| Gradle 守护进程 | 长生命周期的后台进程,避免 JVM 启动开销 | 默认启用 | 显著提升连续构建速度 |
| 并行构建 | 并行执行独立的项目任务 | org.gradle.parallel=true(gradle.properties) | 多项目构建提速 |
| 配置缓存 | 缓存构建脚本配置阶段的结果 | gradle build --configuration-cacheorg.gradle.configuration-cache=true | 大幅减少配置时间 |
| 构建缓存(Build Cache) | 缓存任务输出,避免重复执行 | org.gradle.caching=true// 启用远程缓存:buildCache { remote(HttpBuildCache) { url = "https://cache.company.com"; push = true } } | CI/CD 和团队共享缓存 |
| 增量编译 | 仅重新编译变更的文件 | Java 插件默认支持 | 编译速度提升 |
| 增量注解处理 | 支持增量的注解处理器 | 使用支持增量的处理器(如 Dagger, Lombok) | 加快编译 |
| 按需配置 | 仅配置执行的项目 | org.gradle.configureondemand=true | 多项目构建初始化更快 |
| 文件系统监视 | Gradle 7+ 监视文件变更 | 默认启用(Windows/macOS/Linux) | 更快的 up-to-date 检查 |
| 减少插件应用 | 仅在需要的项目应用插件 | subprojects { if (file('src/main/java').exists()) { apply plugin: 'java' } } | 缩短配置阶段 |
| 优化依赖声明 | 使用 implementation 替代 api | dependencies { implementation '...' // 而非 api } | 减少传递依赖,加快解析 |
注意事项:
- 守护进程、并行、缓存是提升性能的三大支柱。
- 配置缓存是较新功能,需确保插件兼容。
- 远程构建缓存在 CI/CD 环境中效果最佳,可共享缓存。
- 定期监控构建时间,识别瓶颈任务。
- 避免在构建脚本中执行耗时的 I/O 操作。
10.4 日志输出与调试技巧
| 技巧 | 说明 | 代码/命令示例 | 注意事项 |
|---|---|---|---|
| 日志级别 | 控制输出详细程度 | gradle build --infogradle build --debug | —debug 最详细 |
| 任务日志 | 查看特定任务的执行 | gradle compileJava --info | 定位任务问题 |
| 静默模式 | 减少输出 | gradle build -q | 仅输出错误 |
| 打印信息 | 在脚本中输出调试信息 | println "Project: $name"logger.quiet("Quiet message")logger.info("Info: $value")logger.debug("Debug detail") | 使用 logger 推荐 |
| 断点调试 | 在 IDE 中调试构建脚本 | 1. 在 build.gradle 中设断点 2. 使用 —no-daemon 3. 在 IDE 中远程调试 5005 端口 | 需关闭守护进程 |
| 查看依赖树 | 分析依赖关系 | gradle dependenciesgradle :web:dependencies --configuration runtimeClasspath | 诊断版本冲突 |
| 查看任务依赖 | 分析任务执行顺序 | gradle build --dry-rungradle taskName --task-graph | 预览执行计划 |
| 检查任务状态 | 查看任务是否 up-to-date | gradle build --info | 输出 UP-TO-DATE 或 FROM-CACHE |
| 脚本执行顺序 | 理解初始化、配置、执行阶段 | // 阶段钩子:settings.gradle: println "In settings"; build.gradle: println "In configuration"; task myTask { doFirst { println "In execution" } } | 避免在配置阶段执行 |
| 使用 buildScan() | 生成构建扫描报告 | plugins { id 'com.gradle.build-scan' version '3.16' }; buildScan { termsOfServiceUrl = 'https://gradle.com/terms-of-service'; termsOfServiceAgree = 'yes' }; gradle build --scan | 获取详细分析 |
注意事项:
- 使用 logger.* 方法比 println 更规范,可控制级别。
- —debug 输出非常详细,通常先用 —info。
- 调试脚本时务必使用 —no-daemon 以避免守护进程影响。
- build —dry-run 是分析构建流程的好工具。
10.5 构建扫描(Build Scan)
| 功能 | 说明 | 使用方式 | 价值 |
|---|---|---|---|
| 构建扫描概念 | 将构建的详细数据上传到服务进行分析 | gradle build --scan | 深入洞察构建性能 |
| 免费服务 | Gradle 官方提供的 buildScan.com | —scan 参数自动上传 | 快速获取报告 |
| 企业版 | Gradle Enterprise,私有部署 | 配置企业服务器 URL | 安全、可审计 |
| 性能分析 | 识别耗时最长的任务 | 报告中 “Performance” 标签页 | 优化构建瓶颈 |
| 依赖分析 | 可视化依赖树和冲突 | ”Dependencies” 标签页 | 解决版本问题 |
| 缓存效率 | 查看本地/远程缓存命中率 | ”Build Cache” 标签页 | 评估缓存策略 |
| 任务详情 | 查看每个任务的输入、输出、执行时间 | 点击具体任务 | 深入诊断 |
| 环境信息 | 查看 Gradle 版本、JVM、OS 等 | ”Environment” 标签页 | 排查环境问题 |
| 比较构建 | 对比两次构建的差异 | 在网站上选择两个扫描进行比较 | 评估优化效果 |
| 故障诊断 | 快速定位构建失败原因 | 失败任务高亮显示,堆栈跟踪 | 加速调试 |
| 分享报告 | 生成可分享的 URL | 上传后获得链接,可发给团队成员 | 协作分析 |
注意事项:
- —scan 会上传构建数据,敏感项目需确认合规性。
- 企业环境中推荐使用 Gradle Enterprise。
- 构建扫描是性能优化和故障排查的终极工具。
- 可在 gradle.properties 中配置 gradle.enterprise.host 使用私有实例。
- 免费版扫描数据保留有限时间,重要分析建议下载或使用企业版。
第十一章:Gradle 工具链与集成
11.1 与 IntelliJ IDEA 集成
| 集成方式 | 说明 | 操作步骤/配置 | 注意事项 |
|---|---|---|---|
| 直接导入 | IDEA 原生支持 Gradle 项目 | 1. 打开 IDEA 2. 选择 “Open” 或 “Import Project” 3. 选择 build.gradle 文件 4. 选择 “Import project from external model” → Gradle 5. 配置 JDK 和 Gradle 版本 | 推荐方式,自动同步 |
| Gradle 工具窗口 | 管理 Gradle 任务和项目 | View → Tool Windows → Gradle | 可浏览、运行任务,刷新项目 |
| 自动导入 | 修改 build.gradle 后自动同步 | Settings → Build → Build Tools → Gradle → 勾选 “Auto-import” | 提高开发效率,但可能影响性能 |
| 使用 Gradle 构建 | 让 IDEA 使用 Gradle 而非内置构建器 | Settings → Build → Build Tools → Gradle → “Build and run using: Gradle” | 保证本地构建与 CI 一致性 |
| JVM 选项配置 | 设置 IDEA 内 Gradle 进程的 JVM 参数 | Help → Edit Custom VM Options… 添加 -Dorg.gradle.jvmargs=-Xmx2g 等 | 解决内存不足问题 |
| 多版本 Gradle 支持 | 在同一机器使用不同 Gradle 版本 | 在导入时选择 “Use Gradle from” → “Specified location” 或使用 Wrapper | 避免版本冲突 |
| 调试构建脚本 | 在 IDEA 中调试 build.gradle | 1. 在 build.gradle 中设断点 2. 右键任务 → “Debug ‘taskName‘“ 3. 确保 —no-daemon | 需关闭守护进程才能调试 |
| Kotlin DSL 支持 | 编辑 build.gradle.kts | IDEA 提供语法高亮、代码补全、错误检查 | 功能完善,推荐使用 |
| 忽略 generated 目录 | 防止 IDE 索引生成的代码 | IDEA 自动识别 build/ 目录为 excluded | 若未识别,手动标记 |
| 项目 SDK 配置 | 设置项目使用的 JDK | File → Project Structure → Project → Project SDK | 确保与 build.gradle 中 sourceCompatibility 一致 |
注意事项:
- 始终使用 Gradle Wrapper 导入项目,确保团队环境一致。
- 若遇到同步问题,尝试点击 Gradle 工具栏的 “Reload All Gradle Projects”。
- 对于大型项目,关闭 “Auto-import” 并手动刷新以提高响应速度。
- 推荐将 “Build and run using” 设置为 Gradle,避免构建差异。
11.2 与 Android 构建系统(AGP)关系
| 概念 | 说明 | 配置示例 | 注意事项 |
|---|---|---|---|
| AGP 本质 | Android Gradle Plugin,一个基于 Gradle 的二进制插件 | plugins { id 'com.android.application' } | 核心是 Gradle 插件 |
| 构建生命周期 | 继承并扩展 Gradle 生命周期 | android {...} DSL 扩展了 Project | 在 Gradle 的基础上添加 Android 任务 |
| 新增任务 | AGP 添加大量 Android 特定任务 | assembleDebug, assembleRelease, installDebug, lint, connectedAndroidTest | 可通过 gradle tasks 查看 |
| 新增 DSL | android {} 块提供 Android 配置 | android { compileSdk 34; defaultConfig { applicationId "com.example.app"; minSdk 21; targetSdk 34; versionCode 1; versionName "1.0" }; buildTypes { release { minifyEnabled true } } } | 替代旧版 build.gradle 结构 |
| 源集扩展 | 支持 productFlavors 和 buildTypes 源集 | android { flavorDimensions "version"; productFlavors { free { dimension "version" }; paid { dimension "version" } } } | 实现多渠道构建 |
| 依赖管理 | 复用 Gradle 的 dependencies DSL | dependencies { implementation 'androidx.appcompat:appcompat:1.6.1'; testImplementation 'junit:junit:4.13.2' } | 支持 implementation, api 等 |
| 构建变体(Build Variants) | 组合 buildType 和 productFlavor 生成 APK/AAB | debug, release, freeDebug, paidRelease | 每个变体有独立的任务 |
| R8/ProGuard | 代码混淆和缩减 | buildTypes { release { minifyEnabled true; shrinkResources true } } | 默认启用 R8 |
| DEX 处理 | 将字节码转换为 Android 可执行格式 | AGP 自动处理,无需手动配置 | 支持 multidex |
| 与 Gradle 版本对应 | AGP 版本需匹配特定 Gradle 版本 | 查阅官方兼容性表 | 不匹配会导致构建失败 |
注意事项:
- AGP 是 Gradle 的超集,掌握 Gradle 是理解 Android 构建的基础。
- 升级 AGP 时必须同时升级 Gradle Wrapper 到兼容版本。
- android {} DSL 是 AGP 的核心配置入口。
- Android Studio 内置的 Gradle 版本应与项目 wrapper 保持一致。
11.3 与 CI/CD 集成(Jenkins, GitHub Actions)
| CI/CD 平台 | 集成要点 | 示例配置片段 | 注意事项 |
|---|---|---|---|
| 通用原则 | 使用 Wrapper 确保环境一致性 | ./gradlew build | 避免使用全局 gradle 命令 |
| Jenkins | 使用 Pipeline 或 Freestyle 项目 | // Jenkinsfile; pipeline { agent any; stages { stage('Build') { steps { sh './gradlew build --no-daemon' } }; stage('Test') { steps { sh './gradlew test --continue' } } } } | —no-daemon 避免守护进程残留 |
| GitHub Actions | 使用 actions/setup-java 和 gradle/wrapper-validation | name: CI; on: [push, pull_request]; jobs: build: runs-on: ubuntu-latest; steps: - uses: actions/checkout@v4; - uses: actions/setup-java@v4 with: java-version: '17'; distribution: 'temurin'; - uses: gradle/wrapper-validation-action@v1; - uses: gradle/gradle-build-action@v2 with: arguments: build --no-daemon | 官方 action 简化配置 |
| 缓存优化 | 缓存 Gradle 依赖和构建缓存 | - uses: actions/cache@v3 with: path: ~/.gradle/caches; key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }} | 显著减少 CI 时间 |
| 并行执行 | 在多核机器上并行构建 | ./gradlew build --parallel | 多项目构建提速 |
| 持续反馈 | 上传测试报告和代码覆盖率 | 使用 jacocoReport 任务,发布到 SonarQube 或 Codecov | 实现质量门禁 |
| 发布工件 | 构建后发布 JAR/APK | ./gradlew publishToMavenLocal 或部署到 Nexus/Artifactory | 需配置凭据 |
| 环境变量 | 从 CI 环境注入敏感信息 | ./gradlew build -PsonatypeUsername=$SONATYPE_USER -PsonatypePassword=$SONATYPE_PASS | 避免硬编码密码 |
| 构建扫描 | 生成构建分析报告 | ./gradlew build --scan | 需同意服务条款 |
| 状态检查 | 将构建结果反馈给 Git | 所有平台均支持 | PR 的必过检查项 |
注意事项:
- 始终在 CI 中使用 ./gradlew,而非假设系统已安装 Gradle。
- 启用构建缓存(org.gradle.caching=true)并在 CI 中持久化 ~/.gradle/caches 目录。
- 使用 —no-daemon 防止守护进程占用资源。
- —continue 允许在部分测试失败时继续执行其他任务,获取完整报告。
11.4 Gradle Wrapper 使用与配置
| 项目 | 说明 | 配置方式/命令 | 注意事项 |
|---|---|---|---|
| Wrapper 概念 | 包装脚本(gradlew)和 JAR,用于引导特定版本的 Gradle | 自动生成的文件:gradlew, gradlew.bat, gradle/wrapper/gradle-wrapper.jar, gradle/wrapper/gradle-wrapper.properties | 核心是 gradle-wrapper.jar |
| 初始化 Wrapper | 为项目创建 Wrapper | gradle wrapper --gradle-version 8.5 | 首次使用时运行 |
| 配置属性文件 | gradle/wrapper/gradle-wrapper.properties | distributionBase=GRADLE_USER_HOME; distributionPath=wrapper/dists; zipStoreBase=GRADLE_USER_HOME; zipStorePath=wrapper/dists; distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip | distributionUrl 决定 Gradle 版本 |
| 分发类型 | bin(仅 Gradle)vs all(含源码和文档) | 修改 distributionUrl 中的 -bin 或 -all | 通常使用 -bin |
| 升级 Gradle 版本 | 修改 distributionUrl | 手动编辑 properties 文件或重新运行 gradle wrapper --gradle-version X.Y | 团队需同步更新 |
| 使用 Wrapper | 开发者和 CI 使用 ./gradlew | ./gradlew build./gradlew tasks | 确保可执行权限(chmod +x gradlew) |
| Wrapper 脚本 | 跨平台启动脚本 | gradlew(Unix), gradlew.bat(Windows) | 无需安装 Gradle |
| 安全性 | 验证 Wrapper JAR 完整性 | gradle wrapper --gradle-distribution-sha256-sum=<hash> | 防止中间人攻击 |
| 自定义分发源 | 使用私有仓库下载 Gradle | 修改 distributionUrl 为内部 URL | 如 http://repo.internal/gradle-8.5-bin.zip |
| Wrapper 任务 | 更新 Wrapper 自身 | gradle wrapper --gradle-version 8.6 | 会更新所有相关文件 |
| 最佳实践 | 将 Wrapper 文件提交到版本控制 | git add gradlew gradlew.bat gradle/ | 确保团队一致性 |
注意事项:
- Wrapper 是现代 Gradle 项目的标准,不应要求用户手动安装 Gradle。
- 提交 gradle-wrapper.jar 和 gradle-wrapper.properties 到 Git。
- 升级 Gradle 版本时,务必更新 gradle-wrapper.properties 并通知团队。
- 在 CI/CD 中,优先使用 ./gradlew 命令。