如何通过 Java 代码影响 System.loadLibrary() 的搜索路径?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5013547/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 09:03:41  来源:igfitidea点击:

How to influence search path of System.loadLibrary() through Java code?

javajava-native-interface

提问by Philipp Wendler

In a Java project, I am using a third-party library that loads some native library via

在 Java 项目中,我使用第三方库通过加载一些本机库

System.loadLibrary("libName");

I'd like to be able to influence the search path of this method from within my application, so that the user doesn't need to specify a correct java.library.path value on the command line (this value depends on the current OS and architecture). E.g on Windows I want to set it to "lib/native/windows", on Linux 32bit to "lib/native/linux32" etc.

我希望能够在我的应用程序中影响这个方法的搜索路径,这样用户就不需要在命令行上指定正确的 java.library.path 值(这个值取决于当前的操作系统和建筑)。例如,在 Windows 上我想将它设置为“lib/native/windows”,在 Linux 32bit 上设置为“lib/native/linux32”等。

I tried

我试过

System.setProperty("java.library.path", ...)

but this is ignored, apparently because the JVM reads this property only once before my code is run.

但这被忽略了,显然是因为 JVM 在我的代码运行之前只读取了一次这个属性。

I also tried to load the native libray before using the Java library that depends on it with

我还尝试在使用依赖于它的 Java 库之前加载本机库

System.load("fullPath/lib")

This call succeeds, but there will still be an UnsatisfiedLinkError when the native library is loaded again with System.loadLibrary().

此调用成功,但当使用 System.loadLibrary() 再次加载本机库时,仍然会出现 UnsatisfiedLinkError。

The only way I found is the following:

我发现的唯一方法如下:

  • Add interfaces that abstract the whole API of the external library.
  • Use only these interfaces in the rest of the code.
  • Add classes that implement the interfaces and delegate to the library.
  • Write an own ClassLoader, that
    • overwrites findLibary() so that the native library is found in the correct path
    • overwrites loadClass() and loads all classes of the external library and the wrapper layer by itself instead of trying to delegate to its parent like the default ClassLoader would do
  • Ensure that the interfaces are loaded with the normal ClassLoader and the wrapping classes and the external library are loaded with my own ClassLoader.
  • 添加抽象外部库的整个 API 的接口。
  • 在其余代码中仅使用这些接口。
  • 添加实现接口和委托给库的类。
  • 编写一个自己的类加载器,即
    • 覆盖 findLibary() 以便在正确的路径中找到本机库
    • 覆盖 loadClass() 并自行加载外部库和包装层的所有类,而不是像默认 ClassLoader 那样尝试委托给其父类
  • 确保使用普通 ClassLoader 加载接口,并使用我自己的 ClassLoader 加载包装类和外部库。

This works, but I find it very complicated and it is much effort because I need to add all those interfaces. Is there a simpler way?

这行得通,但我发现它非常复杂,而且很费力,因为我需要添加所有这些接口。有没有更简单的方法?

采纳答案by Stephen C

  1. There is no approved way to change the library path for a running JVM.
  2. You cannot load a native library more than once ... and you cannot unload a native library so that you can load it again: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4171986
  1. 没有经过批准的方法可以更改正在运行的 JVM 的库路径。
  2. 您不能多次加载本机库......并且您无法卸载本机库以便您可以再次加载它:http: //bugs.sun.com/bugdatabase/view_bug.do?bug_id=4171986

Based on your comments above (particularly, the behavior of the 3rd-party library), I'd say that your best option is to get the library path right when you launch the JVM.

根据您上面的评论(特别是第 3 方库的行为),我认为您最好的选择是在启动 JVM 时获得正确的库路径。

Note that there is a hacky way to change the library path (see https://stackoverflow.com/a/24258955/139985) but it involves nasty reflection, and it reportedly doesn't work for all Java releases. Certainly, it relies on undocumented private implementation details of ClassLoaderthat couldchange from one release to the next.

请注意,有一种更改库路径的黑客方法(请参阅https://stackoverflow.com/a/24258955/139985),但它涉及讨厌的反射,据报道它不适用于所有 Java 版本。当然,这依赖于无证私人实现细节ClassLoader可以改变从一个版本到下一个。

回答by Samil

I needed to change the dll path for my unit tests. I tried the following hack and it worked:

我需要更改单元测试的 dll 路径。我尝试了以下 hack,它奏效了:

System.setProperty( "java.library.path", "/path/to/libs" ); 
Field fieldSysPath = ClassLoader.class.getDeclaredField( "sys_paths" );
fieldSysPath.setAccessible( true );
fieldSysPath.set( null, null );

For explanation, see the original link.

有关说明,请参阅原始链接

回答by fimez

I tried to following to load a native Growl library for a Java application on my Mac where the lib is in the root of the classpath of my application:

我尝试在 Mac 上为 Java 应用程序加载本机 Growl 库,其中该库位于我的应用程序类路径的根目录中:

System.load(GrowlUtils.class.getResource("/libgrowl.jnilib").getFile().toString());

回答by Donny

Just recently ran into this issue and using OpenJDK where a NullPointerException is thrown (as 0-0 mentioned the Samil's answer). The following works in OpenJDK and shouldwork with Oracle JDK as well.

就在最近遇到这个问题并使用 OpenJDK 时抛出 NullPointerException (如 0-0 提到了 Samil 的答案)。以下内容适用于 OpenJDK,适用于 Oracle JDK。

(Option 1) Replace the java.library.path

(选项 1)替换 java.library.path

System.setProperty("java.library.path", newPath);
Field field = ClassLoader.class.getDeclaredField("sys_paths");
field.setAccessible(true);
field.set(ClassLoader.getSystemClassLoader(), new String[]{newPath});

(Option 2) Add to existing java.library.path

(选项 2)添加到现有的 java.library.path

String libPath = System.getProperty("java.library.path");
String newPath;

if (libPath == null || libPath.isEmpty()) {
    newPath = path;
} else {
    newPath = path + File.pathSeparator + libPath;
}

System.setProperty("java.library.path", newPath);

Field field = ClassLoader.class.getDeclaredField("sys_paths");
field.setAccessible(true);

// Create override for sys_paths
ClassLoader classLoader = ClassLoader.getSystemClassLoader(); 
List<String> newSysPaths = new ArrayList<>();
newSysPaths.add(path);  
newSysPaths.addAll(Arrays.asList((String[])field.get(classLoader)));

field.set(classLoader, newSysPaths.toArray(new String[newSysPaths.size()]));

回答by Petr

While technically correct these answers are missleading. Setting the environment variables PATH on Windows, or LD_LIBRARY_PATH on unix will change where the jvm looks for libraries: What is LD_LIBRARY_PATH and how to use it?

虽然在技术上是正确的,但这些答案具有误导性。在 Windows 上设置环境变量 PATH 或在 unix 上设置 LD_LIBRARY_PATH 将改变 jvm 查找库的位置: 什么是 LD_LIBRARY_PATH 以及如何使用它?

on linux: export LD_LIBRARY_PATH=/usr/.../ then: java ....

在 linux 上:export LD_LIBRARY_PATH=/usr/.../ 然后:java ....

回答by Andreas Dolk

Is there a simpler way?

有没有更简单的方法?

Yes, provide batch/script files to start the application. Then you can set the correct path in the batch/shell file or even read the value from an environment variable. Much easier then trying to do it from inside the application.

是的,提供批处理/脚本文件来启动应用程序。然后您可以在批处理/shell 文件中设置正确的路径,甚至可以从环境变量中读取值。比尝试从应用程序内部执行要容易得多。