Является -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 не может найти библиотеку, в то время как с первым все в порядке?
Если нет, есть ли способ установить путь в коде?
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
Подробнее здесь:
-
--add-exports
: /questions/4157490/javalangillegalaccesserror-nevozmozhno-poluchit-dostup-k-klassu-poskolku-modul-ne-eksportiruetsya-v-nenazvannyij-modul/4157496#4157496 -
--add-opens
: /questions/51929955/javalangreflectinaccessibleobjectexception-ne-mozhet-sdelat-jdkinternalloadercla/51929970#51929970 - Удалять
final
модификатор на Java12+: /questions/49163194/poluchit-obyavlennyie-polya-javalangreflectfields-v-jdk12/49163209#49163209
В настройке Temurin JDKsys_paths
кnull
не работает. Это приводит кNullPointerException
.
В Темурине работают следующие работы:
Method initLibraryPaths = ClassLoader.class.getDeclaredMethod("initLibraryPaths");
initLibraryPaths.setAccessible(true);
initLibraryPaths.invoke(null);