Добавление новых путей для собственных библиотек во время выполнения в Java

Можно ли добавить новый путь для собственных библиотек во время выполнения? (Вместо запуска Java со свойством java.library.path), поэтому вызов System.loadLibrary(nativeLibraryName) будет включать этот путь при попытке найти nativeLibraryName, Возможно ли это, или эти пути заморожены после запуска JVM?

1 ответ

Решение

Кажется невозможным без небольшого взлома (то есть доступа к закрытым полям класса ClassLoader)

Этот блог предоставляет 2 способа сделать это.

Для записи, вот короткая версия.

Вариант 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);
}

Я использовал это в Java 12/13, который должен работать для любой JVM с MethodHandles:

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);
Другие вопросы по тегам