jdeps 从字节码里分析一个 jar 依赖了哪些 JDK 模块、哪些 JDK 内部 API、哪些其它 jar。这篇用三个真实的开源库逐个运行,并用一个字符串反射的例子展示 jdeps 的盲区。
commons-lang3 是多版本 jar,不加 --multi-release 时:返回码 2 ; Error: commons-lang3-3.17.0.jar is a multi-release jar file but --multi-release option is not set
netty-common 4.1.115 --print-module-deps → java.base,java.logging,jdk.unsupported
--list-deps → java.base java.logging jdk.unsupported;--list-reduced-deps → java.base java.logging jdk.unsupported
guava 33.4.0 --print-module-deps → java.base,java.logging,jdk.unsupported
--list-deps → java.base java.logging jdk.unsupported;--list-reduced-deps → java.base java.logging jdk.unsupported
commons-lang3 3.17.0 --print-module-deps → java.base,java.desktop
--list-deps → java.base java.desktop;--list-reduced-deps → java.base java.desktop [netty-common] 返回码 0;36 行输出:
netty-common-4.1.115.Final.jar -> jdk.unsupported
io.netty.util.internal.CleanerJava9 -> sun.misc.Unsafe JDK internal API (jdk.unsupported)
io.netty.util.internal.CleanerJava9$1 -> sun.misc.Unsafe JDK internal API (jdk.unsupported)
io.netty.util.internal.CleanerJava9$2 -> sun.misc.Unsafe JDK internal API (jdk.unsupported)
io.netty.util.internal.PlatformDependent$Mpsc$1 -> sun.misc.Unsafe JDK internal API (jdk.unsupported)
io.netty.util.internal.PlatformDependent0 -> sun.misc.Unsafe JDK internal API (jdk.unsupported)
[guava] 返回码 0;22 行输出:
guava-33.4.0-jre.jar -> jdk.unsupported
com.google.common.cache.Striped64 -> sun.misc.Unsafe JDK internal API (jdk.unsupported)
com.google.common.cache.Striped64$1 -> sun.misc.Unsafe JDK internal API (jdk.unsupported)
com.google.common.cache.Striped64$Cell -> sun.misc.Unsafe JDK internal API (jdk.unsupported)
com.google.common.hash.LittleEndianByteArray$UnsafeByteArray -> sun.misc.Unsafe JDK internal API (jdk.unsupported)
com.google.common.hash.LittleEndianByteArray$UnsafeByteArray$1 -> sun.misc.Unsafe JDK internal API (jdk.unsupported)
[commons-lang3] 返回码 0;0 行输出:
guava 加 -q(不打印替代 API 提示): 14 行 jdeps -s -cp <guava 的依赖> guava.jar(摘要,jar 之间的依赖):
guava-33.4.0-jre.jar -> <m2>/com/google/errorprone/error_prone_annotations/2.36.0/error_prone_annotations-2.36.0.jar
guava-33.4.0-jre.jar -> <m2>/com/google/guava/failureaccess/1.0.2/failureaccess-1.0.2.jar
guava-33.4.0-jre.jar -> <m2>/com/google/j2objc/j2objc-annotations/3.0.0/j2objc-annotations-3.0.0.jar
guava-33.4.0-jre.jar -> java.base
guava-33.4.0-jre.jar -> java.logging
guava-33.4.0-jre.jar -> jdk.unsupported
guava-33.4.0-jre.jar -> <m2>/com/google/code/findbugs/jsr305/3.0.2/jsr305-3.0.2.jar
不给 -cp、只分析 guava.jar(缺少 failureaccess 等 jar):
guava-33.4.0-jre.jar -> java.base
guava-33.4.0-jre.jar -> java.logging
guava-33.4.0-jre.jar -> jdk.unsupported
guava-33.4.0-jre.jar -> not found
--missing-deps: guava-33.4.0-jre.jar -> not found | com.google.common.base.Absent -> javax.annotation.CheckForNull not found | com.google.common.base.AbstractIterator -> javax.annotation.CheckForNull not found | com.google.common.base.CaseFormat$StringConverter -> javax.annotation.CheckForNull not found | com.google.common.base.Converter -> com.google.errorprone.annotations.CheckReturnValue not found
-R(递归分析 classpath 上所有依赖 jar)摘要行数: 11
-dotoutput 生成的文件: ['summary.dot']
-verbose:class -e Preconditions:503 行;前 3 行: [' com.google.common.cache.CacheBuilder -> com.google.common.base.Preconditions guava-33.4.0-jre.jar', ' com.google.common.cache.CacheBuilderSpec -> com.google.common.base.Preconditions guava-33.4.0-jre.jar', ' com.google.common.cache.CacheBuilderSpec$AccessDurationParser -> com.google.common.base.Preconditions guava-3'] --generate-module-info 对 commons-lang3:返回码 1;生成 [];输出:Error: <m2>/org/apache/commons/commons-lang3/3.17.0/commons-lang3-3.17.0.jar is a modular JAR file that cannot be specified with the --generate-module-info option
--generate-module-info 对 netty-common:返回码 0;生成 ['io.netty.common/versions/21/module-info.java']
生成的 module-info.java(前 10 行):
module io.netty.common {
requires java.logging;
requires transitive jdk.unsupported;
exports io.netty.util;
exports io.netty.util.collection;
exports io.netty.util.concurrent;
exports io.netty.util.internal;
exports io.netty.util.internal.logging;
共 exports 行 13 个;requires 行 2 个
--generate-module-info 对 guava:返回码 0;生成 ['com.google.common/versions/21/module-info.java']
jdeps --print-module-deps(应用 + commons-lang3)→ java.base,java.desktop (应用里通过字符串反射用到的 java.logging 与 java.sql 都没有出现;java.desktop 来自 commons-lang3)
运行时 -verbose:class 里来自 jrt: 的模块(不含 CDS 共享类): {'java.base': 8, 'java.sql': 1} ;来自 CDS 共享归档的类 739 个
应用输出: ['Hello', '通过反射加载 java.util.logging.Logger 所在模块 java.logging', '通过反射加载 java.sql.Driver 所在模块 java.sql', '引导层里的模块数 62']