我正在尝试使用 Java 代码中的 Google Closure Compiler,但希望它成为可选依赖项(在构建时存在,但在部署时可能不存在)。 我遇到的问题是,当库在运行时不在类路径上时,我会得到
NoClassDefFoundError
。
如果我要执行引用库符号的代码,这当然是预期的。但所有这些代码永远不会被执行(在标志后面)。事实上,当第一次访问包含该代码的类时,就会发生异常。
我现在对此一直很头疼,我想我可能错过了一些关于 JVM 中类加载语义的见解。
我已将问题简化为以下内容:
import com.google.javascript.jscomp.CompilerOptions;
import com.google.javascript.jscomp.StrictWarningsGuard;
public class Main {
public static void main(String[] args) {
System.out.println("HelloWorld");
}
private static void thisMethodIsNotCalled() {
new CompilerOptions().addWarningsGuard(new StrictWarningsGuard());
}
}
访问库的唯一代码位于从未被调用的方法中。
编译(从maven下载的jar):
javac -cp closure-compiler-v20240317.jar Main.java
跑步时,我得到一个
NoClassDefFoundError
:
$ java Main
Error: Unable to initialize main class Main
Caused by: java.lang.NoClassDefFoundError: com/google/javascript/jscomp/WarningsGuard
我本来希望程序能够成功运行。 JVM 没有理由尝试加载任何库类。
为什么会出现这种情况?我一直在查看 JVM Spec,但没有看到
Main
的加载将如何导致 JVM 尝试加载 thisMethodIsNotCalled
中引用的符号。
更奇怪的是,将
thisMethodIsNotCalled
的内容替换为以下内容,不会导致此错误,因此这似乎是由非常特定的交互引起的。
new StrictWarningsGuard();
new CompilerOptions().addWarningsGuard(null);
Ubuntu 24.04 上的 OpenJDK 21 会发生这种情况:
$ java -version
openjdk version "21.0.3" 2024-04-16
OpenJDK Runtime Environment (build 21.0.3+9-Ubuntu-1ubuntu1)
OpenJDK 64-Bit Server VM (build 21.0.3+9-Ubuntu-1ubuntu1, mixed mode, sharing)
但我相信这并不特定于该版本,我也用各种其他版本复制了此内容。
我在 macOS 上使用 JDK 21.0.3 获取“HelloWorld”。您的 java 命令缺少类路径,但这会引发导入和符号错误。您的环境中的某些内容可能正在定义相同类的不同版本。这可能会导致
NoClassDefFoundError
异常。查看 JDK 中是否已有库,或者某些环境变量正在添加到类路径中。这可以解释为什么你的 java
命令也没有缺少类路径。