Добавление новых путей для собственных библиотек во время выполнения в 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);