【问题标题】:Adding new paths for native libraries at runtime in Java在 Java 运行时为本地库添加新路径
【发布时间】:2013-03-14 12:23:53
【问题描述】:

是否可以在运行时为本地库添加新路径? (而不是使用属性 java.library.path 启动 Java),因此在尝试查找 nativeLibraryName 时,对 System.loadLibrary(nativeLibraryName) 的调用将包含该路径。 是否有可能,或者一旦 JVM 启动,这些路径就会被冻结?

【问题讨论】:

标签: java java-native-interface native


【解决方案1】:

[此解决方案不适用于 Java 10+]

如果没有很少的黑客攻击,这似乎是不可能的(即访问 ClassLoader 类的私有字段)

这个blog 提供了两种方法。

为了记录,这里是简短的版本。

选项 1: 将 java.library.path 完全替换为新值)

public static void setLibraryPath(String path) throws Exception {
    System.setProperty("java.library.path", path);

    //set sys_paths to null so that java.library.path will be reevalueted next time it is needed
    final Field sysPathsField = ClassLoader.class.getDeclaredField("sys_paths");
    sysPathsField.setAccessible(true);
    sysPathsField.set(null, null);
}

选项 2:向当前 java.library.path 添加新路径

/**
* Adds the specified path to the java library path
*
* @param pathToAdd the path to add
* @throws Exception
*/
public static void addLibraryPath(String pathToAdd) throws Exception{
    final Field usrPathsField = ClassLoader.class.getDeclaredField("usr_paths");
    usrPathsField.setAccessible(true);

    //get array of paths
    final String[] paths = (String[])usrPathsField.get(null);

    //check if the path to add is already present
    for(String path : paths) {
        if(path.equals(pathToAdd)) {
            return;
        }
    }

    //add the new path
    final String[] newPaths = Arrays.copyOf(paths, paths.length + 1);
    newPaths[newPaths.length-1] = pathToAdd;
    usrPathsField.set(null, newPaths);
}

【讨论】:

  • 确实是一个 hack :) 感谢您发布它。它不应该那么hacky,可能出于安全原因很难改变这条路径,不确定。我会避免这样做,但很高兴知道它存在。
  • 提到的Field类是:import java.lang.reflect.Field;
  • Java9 记录了“非法反射访问操作”。
  • 在 Java-9 或 Java-10 中有没有更好的方法来做到这一点?这种类型的反射访问会在前者中导致警告或异常,而在后者中是不允许的。
【解决方案2】:

我在 Java 12/13 中使用了它,它应该适用于任何带有 MethodHandles 的 JVM:

Lookup cl = MethodHandles.privateLookupIn(ClassLoader.class, MethodHandles.lookup());
VarHandle sys_paths = cl.findStaticVarHandle(ClassLoader.class, "sys_paths", String[].class);
sys_paths.set(null);

它具有作为 Java API 的优势。

它取代了:

    final Field sysPathsField = ClassLoader.class.getDeclaredField("sys_paths");
    sysPathsField.setAccessible(true);
    sysPathsField.set(null, null);

【讨论】:

  • 这也已停止使用 JDK 16。我认为没有更多的解决方案 - 干净的方法是使用 System.load。
  • 最好的方法是坚持使用旧版本的 java 并进行破解......很明显,最新的 java 没有什么好处,除了更多的限制让开发人员窒息
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-11-03
  • 2010-09-28
  • 2012-01-26
  • 2017-04-18
  • 2016-10-08
  • 1970-01-01
  • 2011-11-29
相关资源
最近更新 更多