Является ли -Djava.library.path=... эквивалентом System.setProperty("java.library.path", ...)?
Я загружаю внешнюю библиотеку, которая находится в ./lib
. Являются ли два приведенных решения для установки параметра java.library.path
эквивалентными?
Установить путь в консоли при выполнении jar:
java -Djava.library.path=./lib -jar myApplication.jar
Установить путь в коде перед загрузкой библиотеки:
System.setProperty("java.library.path", "./lib");
Если они эквивалентны, то почему во втором решении Java не может найти библиотеку, тогда как первое работает корректно?
Если не эквивалентны, есть ли способ установить путь в коде таким образом, чтобы это работало?
4 ответ(ов)
В общем, оба подхода имеют один и тот же конечный эффект, так как системное свойство java.library.path
устанавливается в значение ./lib
.
Тем не менее, некоторые системные свойства оцениваются только в определенные моменты времени, такие как запуск JVM. Если java.library.path
попадает в число таких свойств (что и подтверждает ваш эксперимент), то использование второго подхода не приведет к заметным изменениям, кроме как возвращения нового значения при будущих вызовах метода getProperty()
.
Как правило, использование свойства командной строки -D
работает для всех системных свойств, в то время как System.setProperty()
срабатывает только для свойств, которые проверяются не только при запуске.
Вы можете добавить три строки кода, чтобы решить проблему с загрузкой библиотек в Java:
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
. Это решение позволит обновить системный путь для загрузки библиотек без перезапуска приложения.
Это дополнение к умному ответу Джесси Уэбба выше: https://stackoverflow.com/a/6408467/257299
Для 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
обычно недоступен, вам нужно будет добавить "exports" и "opens" как для аргументов компилятора, так и для аргументов выполнения 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
Подробнее можно прочитать здесь:
--add-exports
: https://stackoverflow.com/a/53647605/257299--add-opens
: https://stackoverflow.com/a/61663667/257299- Удаление модификатора
final
на Java 12+: https://stackoverflow.com/a/56043252/257299
В Temurin JDK установка sys_paths
в null
не работает и приводит к возникновению NullPointerException
.
Следующий код будет работать в Temurin:
Method initLibraryPaths = ClassLoader.class.getDeclaredMethod("initLibraryPaths");
initLibraryPaths.setAccessible(true);
initLibraryPaths.invoke(null);
Этот подход вызывает метод initLibraryPaths
через рефлексию и позволяет корректно инициализировать пути к библиотекам, избегая проблемы с NullPointerException
.
Инициализация ArrayList в одну строчку
Почему нет ConcurrentHashSet, если есть ConcurrentHashMap?
Что такое «сырые типы» и почему их не следует использовать?
Создание репозитория Spring без сущности
Как сгенерировать уникальный хеш-код для строкового ввода в Android?