Java 在 jar 中加载本机库时出现问题
Java problem loading native lib inside jar
(我稍微重新编辑了 post,因为我发现我只需要一个本机库,而不是之前认为的全部 4 个)
我在 Windows 7 x64 上,我尝试使用 this 代码将我的本机库打包到 JAR 中(顺便说一句,我也基本上尝试了该页面中的所有其他代码 - 结果就我而言,所有这些都一样!!!)。
我修改后的代码如下所示:
static {
try {
//EXTRACT NATIVE LIB(S) FROM JAR
String libName = "lwjgl64.dll";
URL url = Class.class.getResource("/natives/" + libName);
File tmpDir = new File(App.FILE_APPPATH.getAbsolutePath() + "/libs");
if (tmpDir.exists()) {
FileUtils.deleteDirectory(tmpDir);
}
tmpDir.mkdir();
File nativeLibTmpFile = new File(tmpDir, libName);
try (InputStream in = url.openStream()) {
Files.copy(in, nativeLibTmpFile.toPath());
}
//ADD PATH TO EXTRACTED LIBS FOLDER TO JAVA
System.setProperty("java.library.path", tmpDir.getAbsolutePath());
Field fieldSysPath = ClassLoader.class.getDeclaredField("sys_paths");
fieldSysPath.setAccessible(true);
fieldSysPath.set(null, null);
//System.err.println("'" + System.getProperty("java.library.path") + "'");
//LOAD LIB DLL FILE(S)
//System.err.println("'" + nativeLibTmpFile.getAbsolutePath() + "'");
System.load(nativeLibTmpFile.getAbsolutePath());
} catch (IOException | SecurityException | IllegalArgumentException | NoSuchFieldException | IllegalAccessException ex) {
Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
}
}
我将此代码添加到我的主 class 和 运行 我的 Java 应用程序中,来自 NetBeansIDE - 运行 像这样安装我的 java 应用程序一切似乎都正常并且工作正常,没有任何错误
但是当我将 project/App 编译为 JAR 时,运行 它(现在已经在由 BAT 文件启动的 NetBeansIDE 之外)然后只要有一个方法或函数需要它本机库我收到此错误:
Exception in thread "Thread-12" java.lang.NoClassDefFoundError: org/lwjgl/system/FunctionProvider
at org.lwjgl.opengl.GL11.<clinit>(GL11.java:38)
at org.lwjgl.opengl.GLContext.getSupportedExtensions(GLContext.java:228)
at org.lwjgl.opengl.ContextCapabilities.initAllStubs(ContextCapabilities.java:5802)
at org.lwjgl.opengl.ContextCapabilities.<init>(ContextCapabilities.java:6240)
at org.lwjgl.opengl.GLContext.useContext(GLContext.java:374)
at org.lwjgl.opengl.ContextGL.makeCurrent(ContextGL.java:195)
at org.lwjgl.opengl.DrawableGL.makeCurrent(DrawableGL.java:110)
at org.lwjgl.opengl.Display.makeCurrent(Display.java:706)
at org.lwjgl.opengl.Display.makeCurrentAndSetSwapInterval(Display.java:1025)
at org.lwjgl.opengl.Display.create(Display.java:852)
at org.lwjgl.opengl.Display.create(Display.java:797)
...
从这里我会说它仍然不知道本地库在哪里 - 对吗?当我的应用程序通过 BAT 文件启动时,我仔细检查了本机库是否已从 JAR 成功提取到指定的创建目录...是的,它是。
我的 BAT 文件代码如下所示:
@ECHO OFF
java -XX:+UseG1GC -Xmx1G -server -jar data.jar %*
@if %errorlevel% neq 0 pause
当来自 NetBeansIDE 的 运行 但它无法 locate/load 已经成功提取并正确添加本机 lib 文件(它已经直接从应用程序内部通过上面的代码)?
任何人都可以告诉我我的代码有什么问题或如何使其正确吗?
也许 BAT 文件中的某些值在使用本机库时不应存在(请记住,我不需要将 -Djava.library.path
添加到我的 BAT 文件,因为我已将本机库打包到罐)?你看:我从来没有用过原生库,这是我第一次。
顺便说一句,我还尝试将 -Djava.library.path
添加到 BAT,其中包含带有本机库的文件夹的绝对路径(路径中没有空格),但结果完全相同。 .strange,就像 OS 中的某些东西阻止了访问虽然我不得不立即说可能不是,因为它从 NetBeansIDE 访问它没有问题 - 这确实只针对编译的最终 JAR 文件 + BAT(或至少我现在是这样看的。
更新 2022-05-06
经过其他几次测试后,根据 DLL file permissions causes not working [=58,我非常接近相信这个问题实际上可能是由 Windows 7 x64 OS 处理文件权限引起的=].
但如果是这样的话,为什么其他人在使用本机库部署他们的 java 应用程序时没有这样的问题......或者它真的是某种 Windows 7 x64 特定问题吗?
我也不知道如何让它工作,因为我已经为我的 java 项目的整个目录添加了完全权限,但它仍然表现相同。
首先给出一些好的建议:
- 从您的项目中删除所有不必要的 jar 文件(它们可能会导致与您类似的问题)
- 不要混合不同版本的 LWJGL jar 文件(可能会导致与您类似的问题)
- 不要混用不同版本的 LWJGL 原生 .dll 文件(可能会导致与您类似的问题)
- 只使用真正需要的本机 .dll 文件(有时不必要的文件可能会导致像您这样的错误)
现在这是我更新的代码,用于自动检测内部 natives 文件夹中的所有本机文件,无需手动定义它们+其余基本上是你的 - 我自己测试过,一切正常。
//INITIALIZATION OF NATIVE LIBS PACKED INSIDE THE COMPILLED JAR
static {
try {
String internalNativesFolder = "natives";
//EXTRACT NATIVE LIB(S) FROM JAR
File tmpDir = new File(App.FILE_APPPATH.getAbsolutePath() + App.LOMKA_R + internalNativesFolder);
if (tmpDir.exists()) {
FileUtils.deleteDirectory(tmpDir);
}
tmpDir.mkdir();
//GETS ALL FILES IN INTERNAL NATIVES FOLDER
URI uri = App.class.getResource(App.LOMKA_R + internalNativesFolder).toURI();
Path myPath;
if (uri.getScheme().equals("jar")) {
FileSystem fileSystem = FileSystems.newFileSystem(uri, Collections.<String, Object>emptyMap());
myPath = fileSystem.getPath(App.LOMKA_R + internalNativesFolder);
} else {
myPath = Paths.get(uri);
}
Stream<Path> walk = Files.walk(myPath, 1);
List<String> libNames = new ArrayList<>();
for (Iterator<Path> it = walk.iterator(); it.hasNext();) {
String fN = it.next().getFileName().toString();
if (!fN.equals(internalNativesFolder)) {
libNames.add(fN);
}
}
//COPIES THOSE FILES TO TEMPORARY LOCAL DIRECTORY
for (String libName : libNames) {
URL url = Class.class.getResource(App.LOMKA_R + internalNativesFolder + App.LOMKA_R + libName);
File nativeLibTmpFile = new File(tmpDir, libName);
try (InputStream in = url.openStream()) {
Files.copy(in, nativeLibTmpFile.toPath());
}
}
//ADD PATH TO EXTRACTED LIBS FOLDER TO JAVA
System.setProperty("java.library.path", tmpDir.getAbsolutePath());
Field fieldSysPath = ClassLoader.class.getDeclaredField("sys_paths");
fieldSysPath.setAccessible(true);
fieldSysPath.set(null, null);
//LOAD LIB DLL FILE(S)
libNames.forEach(libName -> {
System.load(new File(tmpDir, libName).getAbsolutePath());
});
} catch (IOException | SecurityException | IllegalArgumentException | NoSuchFieldException | IllegalAccessException | URISyntaxException ex) {
Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
}
}
(我稍微重新编辑了 post,因为我发现我只需要一个本机库,而不是之前认为的全部 4 个)
我在 Windows 7 x64 上,我尝试使用 this 代码将我的本机库打包到 JAR 中(顺便说一句,我也基本上尝试了该页面中的所有其他代码 - 结果就我而言,所有这些都一样!!!)。
我修改后的代码如下所示:
static {
try {
//EXTRACT NATIVE LIB(S) FROM JAR
String libName = "lwjgl64.dll";
URL url = Class.class.getResource("/natives/" + libName);
File tmpDir = new File(App.FILE_APPPATH.getAbsolutePath() + "/libs");
if (tmpDir.exists()) {
FileUtils.deleteDirectory(tmpDir);
}
tmpDir.mkdir();
File nativeLibTmpFile = new File(tmpDir, libName);
try (InputStream in = url.openStream()) {
Files.copy(in, nativeLibTmpFile.toPath());
}
//ADD PATH TO EXTRACTED LIBS FOLDER TO JAVA
System.setProperty("java.library.path", tmpDir.getAbsolutePath());
Field fieldSysPath = ClassLoader.class.getDeclaredField("sys_paths");
fieldSysPath.setAccessible(true);
fieldSysPath.set(null, null);
//System.err.println("'" + System.getProperty("java.library.path") + "'");
//LOAD LIB DLL FILE(S)
//System.err.println("'" + nativeLibTmpFile.getAbsolutePath() + "'");
System.load(nativeLibTmpFile.getAbsolutePath());
} catch (IOException | SecurityException | IllegalArgumentException | NoSuchFieldException | IllegalAccessException ex) {
Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
}
}
我将此代码添加到我的主 class 和 运行 我的 Java 应用程序中,来自 NetBeansIDE - 运行 像这样安装我的 java 应用程序一切似乎都正常并且工作正常,没有任何错误
但是当我将 project/App 编译为 JAR 时,运行 它(现在已经在由 BAT 文件启动的 NetBeansIDE 之外)然后只要有一个方法或函数需要它本机库我收到此错误:
Exception in thread "Thread-12" java.lang.NoClassDefFoundError: org/lwjgl/system/FunctionProvider
at org.lwjgl.opengl.GL11.<clinit>(GL11.java:38)
at org.lwjgl.opengl.GLContext.getSupportedExtensions(GLContext.java:228)
at org.lwjgl.opengl.ContextCapabilities.initAllStubs(ContextCapabilities.java:5802)
at org.lwjgl.opengl.ContextCapabilities.<init>(ContextCapabilities.java:6240)
at org.lwjgl.opengl.GLContext.useContext(GLContext.java:374)
at org.lwjgl.opengl.ContextGL.makeCurrent(ContextGL.java:195)
at org.lwjgl.opengl.DrawableGL.makeCurrent(DrawableGL.java:110)
at org.lwjgl.opengl.Display.makeCurrent(Display.java:706)
at org.lwjgl.opengl.Display.makeCurrentAndSetSwapInterval(Display.java:1025)
at org.lwjgl.opengl.Display.create(Display.java:852)
at org.lwjgl.opengl.Display.create(Display.java:797)
...
从这里我会说它仍然不知道本地库在哪里 - 对吗?当我的应用程序通过 BAT 文件启动时,我仔细检查了本机库是否已从 JAR 成功提取到指定的创建目录...是的,它是。
我的 BAT 文件代码如下所示:
@ECHO OFF
java -XX:+UseG1GC -Xmx1G -server -jar data.jar %*
@if %errorlevel% neq 0 pause
当来自 NetBeansIDE 的 运行 但它无法 locate/load 已经成功提取并正确添加本机 lib 文件(它已经直接从应用程序内部通过上面的代码)?
任何人都可以告诉我我的代码有什么问题或如何使其正确吗?
也许 BAT 文件中的某些值在使用本机库时不应存在(请记住,我不需要将 -Djava.library.path
添加到我的 BAT 文件,因为我已将本机库打包到罐)?你看:我从来没有用过原生库,这是我第一次。
顺便说一句,我还尝试将 -Djava.library.path
添加到 BAT,其中包含带有本机库的文件夹的绝对路径(路径中没有空格),但结果完全相同。 .strange,就像 OS 中的某些东西阻止了访问虽然我不得不立即说可能不是,因为它从 NetBeansIDE 访问它没有问题 - 这确实只针对编译的最终 JAR 文件 + BAT(或至少我现在是这样看的。
更新 2022-05-06
经过其他几次测试后,根据 DLL file permissions causes not working [=58,我非常接近相信这个问题实际上可能是由 Windows 7 x64 OS 处理文件权限引起的=]. 但如果是这样的话,为什么其他人在使用本机库部署他们的 java 应用程序时没有这样的问题......或者它真的是某种 Windows 7 x64 特定问题吗? 我也不知道如何让它工作,因为我已经为我的 java 项目的整个目录添加了完全权限,但它仍然表现相同。
首先给出一些好的建议:
- 从您的项目中删除所有不必要的 jar 文件(它们可能会导致与您类似的问题)
- 不要混合不同版本的 LWJGL jar 文件(可能会导致与您类似的问题)
- 不要混用不同版本的 LWJGL 原生 .dll 文件(可能会导致与您类似的问题)
- 只使用真正需要的本机 .dll 文件(有时不必要的文件可能会导致像您这样的错误)
现在这是我更新的代码,用于自动检测内部 natives 文件夹中的所有本机文件,无需手动定义它们+其余基本上是你的 - 我自己测试过,一切正常。
//INITIALIZATION OF NATIVE LIBS PACKED INSIDE THE COMPILLED JAR
static {
try {
String internalNativesFolder = "natives";
//EXTRACT NATIVE LIB(S) FROM JAR
File tmpDir = new File(App.FILE_APPPATH.getAbsolutePath() + App.LOMKA_R + internalNativesFolder);
if (tmpDir.exists()) {
FileUtils.deleteDirectory(tmpDir);
}
tmpDir.mkdir();
//GETS ALL FILES IN INTERNAL NATIVES FOLDER
URI uri = App.class.getResource(App.LOMKA_R + internalNativesFolder).toURI();
Path myPath;
if (uri.getScheme().equals("jar")) {
FileSystem fileSystem = FileSystems.newFileSystem(uri, Collections.<String, Object>emptyMap());
myPath = fileSystem.getPath(App.LOMKA_R + internalNativesFolder);
} else {
myPath = Paths.get(uri);
}
Stream<Path> walk = Files.walk(myPath, 1);
List<String> libNames = new ArrayList<>();
for (Iterator<Path> it = walk.iterator(); it.hasNext();) {
String fN = it.next().getFileName().toString();
if (!fN.equals(internalNativesFolder)) {
libNames.add(fN);
}
}
//COPIES THOSE FILES TO TEMPORARY LOCAL DIRECTORY
for (String libName : libNames) {
URL url = Class.class.getResource(App.LOMKA_R + internalNativesFolder + App.LOMKA_R + libName);
File nativeLibTmpFile = new File(tmpDir, libName);
try (InputStream in = url.openStream()) {
Files.copy(in, nativeLibTmpFile.toPath());
}
}
//ADD PATH TO EXTRACTED LIBS FOLDER TO JAVA
System.setProperty("java.library.path", tmpDir.getAbsolutePath());
Field fieldSysPath = ClassLoader.class.getDeclaredField("sys_paths");
fieldSysPath.setAccessible(true);
fieldSysPath.set(null, null);
//LOAD LIB DLL FILE(S)
libNames.forEach(libName -> {
System.load(new File(tmpDir, libName).getAbsolutePath());
});
} catch (IOException | SecurityException | IllegalArgumentException | NoSuchFieldException | IllegalAccessException | URISyntaxException ex) {
Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
}
}