bigywater.tech
Java · 433

Java 依赖锁定

没有锁定的依赖,同一份代码今天和明天可能解析出不同的版本。这篇在可控的本地仓库里验证:动态版本如何漂移、Gradle 的依赖锁怎样固定直接与传递依赖、三种 lockMode 的差别,以及校验文件对被篡改产物的检查。

lib 1.0 → 1.1
没有锁时,同一份声明在发布新版本后(Maven 加 -U、Gradle 加 --refresh-dependencies)解析的结果变化,传递依赖 core 从 1.0 变成 1.2
仍是 1.1
写锁后发布了 lib 1.2、core 1.3,解析仍然是锁里的 lib 1.1 / core 1.2
rc=1
篡改仓库里 jar 的一个字节后,依赖校验让 compileJava 失败

没有锁时的版本漂移

实测 · 本机执行输出
  仓库里只有 lib 1.0 / core 1.0:Maven 版本范围 [1.0,2.0) 解析为 lib:1.0 core:1.0(rc=0);Gradle lock:lib:1.+ 解析为 lib:1.+→1.0 core:[1.0,2.0)→1.0
  之后发布了 lib 1.1、core 1.1、core 1.2,什么都没改:Maven [1.0,2.0) 解析为 lib:1.0 core:1.0(本地仓库缓存了仓库元数据,默认一天内不再检查);加 -U 强制更新后解析为 lib:1.1 core:1.2;Gradle 1.+ 加 --refresh-dependencies 解析为 lib:1.+→1.1 core:[1.0,2.0)→1.2
  把 Maven 依赖写成固定版本 1.0(加 -U):解析为 lib:1.0 core:1.2(lib 自己的依赖范围 [1.0,2.0) 仍然让传递依赖 core 取最新版)

写锁、更新锁

实测 · 本机执行输出
  gradle dependencies --write-locks:rc=0;生成 gradle.lockfile,3 行:lock:core:1.2=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath | lock:lib:1.1=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath | empty=annotationProcessor,testAnnotationProcessor
  锁文件写好之后解析:lib:1.+→1.1 core:[1.0,2.0)→1.2
  再发布 lib 1.2 与 core 1.3(仓库里 lib 有 1.0,1.1,1.2、core 有 1.0,1.1,1.2,1.3),加 --refresh-dependencies 解析:lib:1.+→1.1 core:[1.0,2.0)→1.2(仍然是锁里的版本)
  gradle dependencies --update-locks lock:lib:rc=0;锁文件变成:lock:core:1.2=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath | lock:lib:1.2=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath | empty=annotationProcessor,testAnnotationProcessor;解析:lib:1.+→1.2 core:[1.0,2.0)→1.2(只更新了 lib,没有传递依赖 core)
  gradle dependencies --update-locks 'lock:*':锁文件变成:lock:core:1.3=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath | lock:lib:1.2=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath | empty=annotationProcessor,testAnnotationProcessor

lockMode 的差别

实测 · 本机执行输出
  锁文件当前内容:lock:core:1.3 | lock:lib:1.2 (lib 1.2 / core 1.3)
  DEFAULT:声明与锁一致 compileJava rc=0;新增不在锁文件里的依赖 lock:other:1.0 rc=1(> Could not resolve all files for configuration ':compileClasspath'. / > Resolved 'lock:other:1.0' which is not part of the depend);声明改成 lock:lib:[1.0,1.1](锁里是 lib 1.2)rc=0,解析为 lib:[1.0,1.1]→1.2 core:[1.0,2.0)→1.3;删掉锁文件后 rc=0
  STRICT:声明与锁一致 compileJava rc=0;新增不在锁文件里的依赖 lock:other:1.0 rc=1(> Could not resolve all files for configuration ':compileClasspath'. / > Resolved 'lock:other:1.0' which is not part of the depend);声明改成 lock:lib:[1.0,1.1](锁里是 lib 1.2)rc=0,解析为 lib:[1.0,1.1]→1.2 core:[1.0,2.0)→1.3;删掉锁文件后 rc=1(> Could not resolve all dependencies for configuration ':compileClasspath'. / > Locking strict mode: Configuration ':compileClassp)
  LENIENT:声明与锁一致 compileJava rc=0;新增不在锁文件里的依赖 lock:other:1.0 rc=0;声明改成 lock:lib:[1.0,1.1](锁里是 lib 1.2)rc=0,解析为 lib:[1.0,1.1]→1.2 core:[1.0,2.0)→1.3;删掉锁文件后 rc=0

依赖校验

实测 · 本机执行输出
  gradle --write-verification-metadata sha256:rc=0;verification-metadata.xml 里 2 个 component:core:1.3,lib:1.2;sha256 条目 4 个
  没有改动时 compileJava:rc=0
  篡改了仓库里的 lib-1.2.jar(末尾多一个字节):rc=1;错误:> Dependency verification failed for configuration ':compileClasspath' / One artifact failed verification: lib-1.2.jar (lock:lib:1.2) from repository maven / Open this report for more details: file://<jtmp>/j293a/build/reports/dependency-verification/at-1791202214798/dependency-verification-report.html
  还原后 compileJava:rc=0

分步演示

依赖锁定
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK(Temurin 17.0.20、Homebrew 21.0.12 与 27,每篇注明实际用的版本);用 javac 编译、java 直接运行,需要第三方库时用 Maven 解析依赖;计时用 System.nanoTime,数字每次运行会有小幅抖动。Gradle 9.8.0(官方发行版,--offline,隔离的 GRADLE_USER_HOME),Maven 3.10.0,JDK 21.0.12;依赖来自自己建的 file:// 仓库(lock:lib、lock:core、lock:other),没有访问远程仓库;实验后 ~/.m2 里的 lock 目录已删除
    • 没有验证:Maven 的锁定方案(maven-enforcer、BOM、第三方 lock 插件等)、Gradle 的 constraints 与 platform、锁文件在多项目构建与 Dependabot / Renovate 更新流程中的维护成本、SHA-256 之外的 PGP 签名校验(见发布产物签名一篇)、真实远程仓库上的 SNAPSHOT 与元数据缓存
    京ICP备2026067502号-1京公网安备11010502063487号