当我在子目录中创建jar文件时,来自bcprov-jdk15on-159.jar的BouncyCastleProvider
类无法加载ClassNotFoundException
。我认为创建jar文件的位置应该对其内容和行为没有影响。
以下是创建工作jar的示例。
$ jar cfm MyProject.jar Manifest.txt Main.class bcprov-jdk15on-159.jar
$ java -jar MyProject.jar
hello provider: BC version 1.59
以下是运行具有完全相同输入文件但具有不同jar文件目标的jar的示例,导致jar失败。
$ jar cfm dist/MyProject.jar Manifest.txt Main.class bcprov-jdk15on-159.jar
$ java -jar dist/MyProject.jar
Error: Unable to initialize main class Main
Caused by: java.lang.NoClassDefFoundError: org/bouncycastle/jce/provider/BouncyCastleProvider
这是文件Manifest.txt
Manifest-Version: 1.0
Main-Class: Main
Class-Path: bcprov-jdk15on-159.jar
这是使用Main.java
类的BouncyCastleProvider
文件。
public class Main {
public static void main(String... arg) {
java.security.Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
java.security.Provider p = java.security.Security.getProvider("BC");
System.out.println("hello provider: " + p);
}
}
我在JDK 8和JDK 9中都看到了这种行为,并且还看到了JDK jar命令(如上所示)和Ant的jar任务。
我在尝试将PCSecrets密码管理器升级到Java 9下工作时偶然发现了这个问题。
答案 0 :(得分:2)
上述失败的原因是Java不会从jar文件中加载bcprov-jdk15on-159.jar
,而是从类路径加载,这似乎根据调用jar的位置而不同。当生成的jar文件与bcprov-jdk15on-159.jar
位于同一目录中时,它会从目录中加载它,并且命令调用正常。当生成的jar文件位于另一个目录中时,它无法加载bcprov-jdk15on-159.jar
并抛出ClassNotFoundException
。在this问题中提供了在jar中包含jar的解决方案。有趣的是,this answer,投了26次,是基于同样的错误假设。
我通过比较生成的jar文件的二进制图像找到了答案。它们似乎只在META-INF/MANIFEST.MF
文件的时间戳上有所不同。通过与命令jar cfm dist/MyProject.jar Manifest.txt Main.class bcprov-jdk15on-159.jar & jar cfm MyProject.jar Manifest.txt Main.class bcprov-jdk15on-159.jar
并行生成两个文件,我有两个相同的jar文件仍然行为不端。这促使我查看两个文件执行的环境,而不是文件本身。