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

时间:2011-02-16 07:15:14

标签: java java-native-interface

在Java项目中,我使用的是通过

加载一些本机库的第三方库
System.loadLibrary("libName");

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

我试过

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

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

我还尝试在使用依赖于

的Java库之前加载本机库
System.load("fullPath/lib")

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

我找到的唯一方法如下:

  • 添加抽象外部库的整个API的接口。
  • 在其余代码中仅使用这些接口。
  • 添加实现接口并委托给库的类。
  • 写一个自己的ClassLoader,那个
    • 覆盖findLibary(),以便在正确的路径中找到本机库
    • 覆盖loadClass()并自行加载外部库和包装层的所有类,而不是像默认的ClassLoader那样尝试委托给它的父类
  • 确保使用普通的ClassLoader加载接口,并使用我自己的ClassLoader加载包装类和外部库。

这很有效,但我发现它非常复杂并且需要付出很多努力,因为我需要添加所有这些接口。有更简单的方法吗?

6 个答案:

答案 0 :(得分:5)

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

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

有关说明,请参阅original link

答案 1 :(得分:2)

最近才遇到此问题,并使用OpenJDK抛出NullPointerException(因为0-0提到了Samil的答案)。以下OpenJDK中的作品,应该与Oracle JDK一起使用。

(选项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});

(选项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()]));

答案 2 :(得分:1)

  1. 您无法更改正在运行的JVM的库路径。
  2. 您无法多次加载本机库...而且您无法卸载本机库以便可以再次加载它:http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4171986
  3. 根据您上面的评论(特别是第三方库的行为),我想说您最好的选择是在启动JVM时使库路径正确。

答案 3 :(得分:1)

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

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

答案 4 :(得分:0)

  

有更简单的方法吗?

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

答案 5 :(得分:0)

尽管从技术上讲这些答案正确无误。在Windows上设置环境变量PATH或在unix上设置环境变量LD_LIBRARY_PATH将会更改jvm查找库的位置: What is LD_LIBRARY_PATH and how to use it?

在Linux上

: 导出LD_LIBRARY_PATH = / usr /.../ 然后: Java ....