Является -Djava.library.path=... эквивалентно System.setProperty("java.library.path", ...)

Я загружаю внешнюю библиотеку, которая находится в ./lib, Являются ли эти два решения для установки эквивалента java.library.path?

  1. Установить путь в консоли при выполнении jar:

    java -Djava.library.path=./lib -jar myApplication.jar
    
  2. Установите путь в коде перед загрузкой библиотеки:

    System.setProperty("java.library.path", "./lib");
    

Если они эквивалентны, почему во втором решении Java не может найти библиотеку, в то время как с первым все в порядке?

Если нет, есть ли способ установить путь в коде?

5 ответов

Решение

Вообще говоря, оба подхода имеют одинаковый чистый эффект в том, что системное свойство java.library.path установлен в значение ./lib,

Однако некоторые системные свойства оцениваются только в определенные моменты времени, например при запуске JVM. Если java.library.path входит в число этих свойств (и ваш эксперимент, по-видимому, указывает на это), то использование второго подхода не будет иметь заметного эффекта, за исключением возврата нового значения при будущих вызовах getProperty(),

Как правило, используя -D свойство командной строки работает для всех системных свойств, в то время как System.setProperty() работает только со свойствами, которые проверяются не только при запуске.

Хотя это не очень хорошо задокументировано, java.library.path Системное свойство - это свойство "только для чтения", поскольку System.loadLibrary() метод обеспокоен. Это сообщенная ошибка, но она была закрыта Sun, а не исправлена. Проблема состоит в том, что ClassLoader JVM читает это свойство один раз при запуске, а затем кэширует его, не позволяя нам изменить его программно впоследствии. Линия 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");
    }
}

ВНИМАНИЕ: Это может не работать на всех платформах и / или JVM.

Вы можете добавить три строки

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

а также импортировать java.lang.reflect.Field Это нормально, чтобы решить проблему

Это дополнение к этому ответу на удивительный ответ Джесси Уэбба выше: /questions/8084343/yavlyaetsya-djavalibrarypath-ekvivalentno-systemsetpropertyjavalibrarypath/8084357#8084357

Для Java 17:

      import jdk.internal.loader.NativeLibraries;
final Class<?>[] declClassArr = NativeLibraries.class.getDeclaredClasses();
final Class<?> libraryPaths =
    Arrays.stream(declClassArr)
        .filter(klass -> klass.getSimpleName().equals("LibraryPaths"))
        .findFirst()
        .get();
final Field field = libraryPaths.getDeclaredField("USER_PATHS");
final MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(Field.class, MethodHandles.lookup());
final VarHandle varHandle = lookup.findVarHandle(Field.class, "modifiers", int.class);
varHandle.set(field, field.getModifiers() & ~Modifier.FINAL);

Так как пакет jdk.internal.loaderиз модуля java.baseобычно недоступен, вам нужно будет добавить «экспорты» и «открытия» как в аргументы компилятора, так и во время выполнения JVM.

      --add-exports=java.base/jdk.internal.loader=ALL-UNNAMED
--add-opens=java.base/jdk.internal.loader=ALL-UNNAMED
--add-opens=java.base/java.lang.reflect=ALL-UNNAMED

Подробнее здесь:

В настройке Temurin JDKsys_pathsкnullне работает. Это приводит кNullPointerException.

В Темурине работают следующие работы:

      Method initLibraryPaths = ClassLoader.class.getDeclaredMethod("initLibraryPaths");
initLibraryPaths.setAccessible(true);
initLibraryPaths.invoke(null);
Другие вопросы по тегам