第 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. 修复动作

根据定位到的根因选择对应修复动作:

  1. 无效 JAR / POM 损坏: 手动删除该文件及其父目录,然后重新构建
  2. 依赖解析缓存锁定文件: 手动删除对应 .lock 文件
  3. 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. 复测

修复后依次执行:

  1. 使用 gradle --stop 确保所有守护进程已退出。
  2. 删除指定污染文件或整个缓存。
  3. 执行 ./gradlew clean build --refresh-dependencies 重新下载并构建。
  4. 观察日志中不再出现前述异常,且 JAR 与 POM 校验通过。
  5. 如有条件,再次执行 file 和 xmllint 验证新下载的文件完整性。

复测通过标准: 全量构建成功,无 ZipException / PomParsingException / 锁定超时,且首次构建时间恢复至正常范围。

6. 预防

  • 配置缓存清理策略: 在 gradle.properties 中设置 org.gradle.caching,并定期执行 gradle clean 清理项目级缓存。
  • 使用依赖锁定(dependency locking): 确保所有传递依赖版本明确,避免因 POM 解析失败导致缓存污染。
  • 监控缓存目录: 编写脚本定期检查 ~/.gradle/caches 下的 JAR 和 POM 完整性。
  • 异常中断处理: 构建过程中不要强制 Kill 进程,尽量使用 gradle --stop 优雅退出。
Gradle 依赖缓存污染故障排查流程 构建失败报错 现象识别 数据采集 原因定位(故障树) 修复动作 复测通过