是-Djava.library.path = ...等效于System.setProperty("java.library.path",...)

czu*_*zuk 48 java

我加载了一个放在里面的外部库./lib.这两个解决方案是否设置java.library.path等效?

  1. 执行jar时在控制台中设置路径:

    java -Djava.library.path=./lib -jar myApplication.jar
    
    Run Code Online (Sandbox Code Playgroud)
  2. 在加载库之前在代码中设置路径:

    System.setProperty("java.library.path", "./lib");
    
    Run Code Online (Sandbox Code Playgroud)

如果它们是等价的,为什么在第二个解决方案中Java可以找不到库而第一个可以?

如果没有,有没有办法在代码中设置路径?

Jes*_*ebb 51

虽然没有详细记录,但java.library.pathSystem.loadLibrary()方法而言,系统属性是"只读"属性.这是一个报告的错误,但它被Sun关闭而不是被修复.问题是JVM的ClassLoader在启动时读取此属性一次然后缓存它,不允许我们以后以编程方式更改它.System.setProperty("java.library.path", anyVal);System.getProperty()方法调用外,该行无效.

幸运的是,有人在Sun论坛上发布了一个解决方法.不幸的是,该链接不再有效,但我确实在另一个源上找到了代码.以下是可用于解决无法设置java.library.path系统属性的代码:

public static void addDir(String s) throws IOException {
    try {
        // This enables the java.library.path to be modified at runtime
        // From a Sun engineer at http://forums.sun.com/thread.jspa?threadID=707176
        //
        Field field = ClassLoader.class.getDeclaredField("usr_paths");
        field.setAccessible(true);
        String[] paths = (String[])field.get(null);
        for (int i = 0; i < paths.length; i++) {
            if (s.equals(paths[i])) {
                return;
            }
        }
        String[] tmp = new String[paths.length+1];
        System.arraycopy(paths,0,tmp,0,paths.length);
        tmp[paths.length] = s;
        field.set(null,tmp);
        System.setProperty("java.library.path", System.getProperty("java.library.path") + File.pathSeparator + s);
    } catch (IllegalAccessException e) {
        throw new IOException("Failed to get permissions to set library path");
    } catch (NoSuchFieldException e) {
        throw new IOException("Failed to get field handle to set library path");
    }
}
Run Code Online (Sandbox Code Playgroud)

警告:这可能不适用于所有平台和/或JVM.

  • 查看@luyifan的[这个更新的答案](http://stackoverflow.com/a/24988095/346561),这似乎与这个答案完成相同的事情,代码更少. (5认同)

Joa*_*uer 50

一般来说,两种方法都具有相同的净效果,因为系统属性java.library.path设置为该值./lib.

但是,某些系统属性仅在特定时间点进行评估,例如JVM的启动.如果 java.library.path属于这些属性(并且您的实验似乎表明了这一点),则使用第二种方法将没有明显的效果,除了在将来的调用中返回新值getProperty().

根据经验,使用-D命令行属性适用于所有系统属性,而System.setProperty()仅适用于不仅在启动期间检查的属性.


小智 38

你可以添加三行

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

并导入java.lang.reflect.Field可以解决问题

  • 可悲的是,它在 Java 10 中是被禁止的,将被删除(“发生了非法的反射访问操作”)。 (3认同)
  • 这很有效(并且比Jesse Webb的解决方案所描述的那样自己构建临时路径更简单) (2认同)
  • 这个是救命稻草。Sun/Oracle 似乎草率地不提供从特定的、运行时定义的目录加载库的更直接的方法。 (2认同)