1. 依赖声明基础:build.gradle 中的 Dependencies 块
Gradle 依赖管理是一切构建的起点。掌握如何在 build.gradle 中正确声明依赖,理解坐标结构与配置项语义,是避免版本冲突、构建失败的核心前提。本章从最基础的语法出发,逐步深入配置差异与最佳实践。
1.1 依赖声明规范与坐标模型
每个依赖由三部分组成:group、name、version。完整格式为:
implementation 'group:name:version'
implementation 'com.google.guava:guava:33.0.0-jre'
在 build.gradle 的 dependencies 块中,所有声明必须指定一个配置(Configuration)。常见配置如下表:
| 配置名称 | 作用域 | 典型用途 |
|---|---|---|
| implementation | 编译时 + 运行时(不暴露给消费者) | 模块内部依赖 |
| api | 编译时 + 运行时(暴露给消费者) | 公共 API 依赖 |
| compileOnly | 仅编译时 | 注解处理器、编译期库 |
| runtimeOnly | 仅运行时 | JDBC 驱动、日志实现 |
| testImplementation | 仅测试编译 + 运行时 | JUnit、Mockito |
1.2 implementation vs api —— 核心差异
两者最关键的差异在于依赖透传性:
- implementation:当前模块编译时需要,但是不会泄露给直接依赖该模块的消费者。可缩短编译路径,提升增量构建速度。
- api:当前模块编译时需要,并且强制透传给所有消费者。常用于公共库(如 Spring Boot Starter)。
选择建议:
- 如果该依赖仅用于模块内部实现,绝不暴露在 ABI(Application Binary Interface)中 → 使用 implementation。
- 如果该依赖的类型、接口被模块的公共 API 直接引用 → 使用 api。
错误示例(直接透传导致版本冲突):
// 模块A:声明 api 'com.example:lib:1.0'
// 模块B依赖A后,B也会看到lib:1.0,若B本意使用lib:2.0,则冲突
1.3 依赖配置与版本冲突
Gradle 采用最新版本优先策略,配合强制声明可精准控制:
implementation('com.example:lib:1.0') {
force = true // 强制使用此版本
exclude group: 'com.unwanted' // 排除传递依赖
}
常见错误:多模块项目中,
api 的过度使用会导致传递依赖数量膨胀,增加构建时间和版本冲突概率。优先使用 implementation,仅当确需暴露时升级为 api。
1.4 依赖声明流程与调试
典型的声明到生效流程:
- 在
build.gradle的dependencies块中写入坐标。 - 执行
gradle dependencies查看完整树形结构。 - 分析冲突分支(红色标记),通过
force或strictly锁定版本。 - 在
build.gradle中配置 resolutionStrategy。
调试命令:
gradle dependencies --configuration compileClasspath
gradle dependencyInsight --dependency com.google.guava --configuration implementation
1.5 核心结构图:依赖配置层次与作用域
调试技巧:通过
gradle dependencyInsight 可查看某个坐标的具体来源及透传路径。使用 --configuration runtimeClasspath 可分析最终运行时的实际依赖集。
1.6 常见错误与优化
| 问题场景 | 错误表现 | 解决方案 |
|---|---|---|
| 误将公共 API 声明为 implementation | 消费者编译报错:找不到符号 | 改为 api;或确认消费者是否需要该依赖 |
| 版本冲突导致运行时 NoSuchMethodError | Gradle 默认选最高版本,但冲突版本并存 | 使用 force = true 或 strictly 'version' |
| 传递依赖膨胀 | 构建时间增加,依赖树复杂 | 优先 implementation;必要时 exclude |
| 重复声明同坐标不同版本 | 构建警告:Dependency conflict | 统一版本号至 property;使用 BOM |