第 4 章 依赖缓存污染定位:缓存中的无效jar、pom解析错误、依赖解析缓存锁定文件、gradle cache clear后仍报错
依赖缓存污染是 Gradle 构建中隐蔽性较高的故障之一。当缓存目录下的 JAR 文件被截断、POM 描述文件出现语法错误、或依赖解析过程中产生的锁定文件残留时,Gradle 会重复使用这些损坏的缓存,即使执行 clean 任务也无法恢复。本章将带领你按照标准排查流程,准确定位污染源并彻底修复。
1. 现象识别
以下三类典型现象常指向缓存污染:
| 现象分类 | 具体表现 | 初步判断 |
|---|---|---|
| 无效 JAR | 编译时报 ZipException / ClassNotFoundException,但源码与依赖坐标均正确 |
缓存中的 JAR 文件内容不完整或被篡改 |
| POM 解析错误 | 同步时提示 PomParsingException / InvalidPomException,对应 POM 文件无法被 XML 解析器加载 |
POM 文件损坏或编码异常 |
| 锁定文件残留 | 构建卡死在 resolve dependencies,日志末尾出现 .lock 文件超时 |
上次构建异常中断,锁定文件未释放 |
| cache clear 无效 | 执行 --refresh-dependencies 甚至删除整个 ~/.gradle/caches 后错误依然存在 |
缓存文件可能位于其他位置,或项目级 .gradle 目录下存在残留 |
2. 数据采集
进入缓存根目录,系统化收集证据:
# 定位 Gradle 缓存主目录
echo $GRADLE_USER_HOME
# 通常为 ~/.gradle/caches ,若未设置则为 ~/.gradle
# 查看依赖缓存子目录
ls -la ~/.gradle/caches/modules-2/files-2.1/
# 或针对特定 group 缩小范围
ls -la ~/.gradle/caches/modules-2/files-2.1/com.example/
提取日志片段:
# 无效 JAR 典型日志
> Could not expand ZIP '/home/user/.gradle/caches/modules-2/files-2.1/.../abc-1.0.jar'.
ZipException: invalid stored block lengths
# POM 解析错误典型日志
> Could not parse POM /home/user/.gradle/caches/modules-2/files-2.1/.../abc-1.0.pom
PomParsingException: Unrecognised tag: 'projec' (position: START_TAG ...)
# 锁定文件典型日志
> Timeout waiting to lock dependency cache for module 'com.example:abc:1.0'.
It is currently locked by another Gradle instance.
采集文件校验特征:
# 检查 JAR 是否合法
file ~/.gradle/caches/.../abc-1.0.jar
# 输出应为 "Zip archive data",若为 "data" 或 "empty" 则表明 JAR 无效
# 检查 POM 是否可解析
xmllint --noout ~/.gradle/caches/.../abc-1.0.pom
# 若返回语法错误,则 POM 损坏
# 检查锁定文件是否存在
find ~/.gradle/caches/ -name "*.lock"
3. 原因定位(故障树 + 排查表)
使用以下结构化的故障树逐步缩小根因范围:
故障树分析:依赖缓存污染定位
步骤1:检查报错是否可被 clean 复现?
→ 是:进入步骤2
→ 否:可能为非缓存类问题,跳至章节1
步骤2:缓存目录下是否存在对应文件?
→ 是:进入步骤3
→ 否:检查仓库配置或网络连通性
步骤3:校验 JAR / POM 完整性:
→ 文件校验失败 → 无效 JAR / POM 损坏
→ 文件校验通过 → 进入步骤4
步骤4:是否存在 .lock 文件?
→ 是 → 依赖解析缓存锁定
→ 否 → 进入步骤5
步骤5:检查 ~/.gradle/caches 和项目 .gradle 是否存在差异?
→ 项目 .gradle 下也有缓存 → cache clear 时遗漏项目级缓存
根因判定规则表:
| 采集到的证据组合 | 定位根因 | 严重程度 |
|---|---|---|
| JAR file 命令输出非 Zip + 日志报 ZipException | 无效 JAR 文件 | 高 |
| POM xmllint 报语法错误 + 日志报 PomParsingException | POM 解析错误 | 高 |
| find 发现 .lock 文件 + 构建卡死 | 依赖解析缓存锁定文件 | 中 |
| ~/.gradle/caches 已清空,但报错依旧 + 项目 .gradle/caches 存在对应文件 | cache clear 遗漏项目级缓存 | 中 |
| 单文件损坏,其他模块正常 | 某次下载中断导致部分缓存污染 | 低 |
4. 修复动作
根据定位到的根因选择对应修复动作:
- 无效 JAR / POM 损坏: 手动删除该文件及其父目录,然后重新构建
- 依赖解析缓存锁定文件: 手动删除对应
.lock文件 - cache clear 遗漏项目级缓存: 同时清理
~/.gradle/caches与项目根目录下的.gradle/caches
命令实例:
# 定位并删除无效 JAR
rm -rf ~/.gradle/caches/modules-2/files-2.1/com.example/abc/1.0/
# 删除锁定文件
rm -f ~/.gradle/caches/modules-2/modules-2.lock
# 同时清理用户级与项目级缓存
rm -rf ~/.gradle/caches/
rm -rf .gradle/caches/
# 重新下载依赖
./gradlew build --refresh-dependencies
注意: 删除整个 ~/.gradle/caches 会丢失所有项目的缓存,首次构建会非常慢。建议在定位到具体污染文件后,优先执行精确删除。
5. 复测
修复后依次执行:
- 使用
gradle --stop确保所有守护进程已退出。 - 删除指定污染文件或整个缓存。
- 执行
./gradlew clean build --refresh-dependencies重新下载并构建。 - 观察日志中不再出现前述异常,且 JAR 与 POM 校验通过。
- 如有条件,再次执行
file和xmllint验证新下载的文件完整性。
复测通过标准: 全量构建成功,无 ZipException / PomParsingException / 锁定超时,且首次构建时间恢复至正常范围。
6. 预防
- 配置缓存清理策略: 在
gradle.properties中设置org.gradle.caching,并定期执行gradle clean清理项目级缓存。 - 使用依赖锁定(dependency locking): 确保所有传递依赖版本明确,避免因 POM 解析失败导致缓存污染。
- 监控缓存目录: 编写脚本定期检查
~/.gradle/caches下的 JAR 和 POM 完整性。 - 异常中断处理: 构建过程中不要强制 Kill 进程,尽量使用
gradle --stop优雅退出。