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);
    }
}