Как изменить CLASSPATH Java-процесса из Java-процесса?
Прежде чем вы спросите меня: «Зачем вам это нужно?» Я объясню это в ближайшее время.
When you have a Clojure REPL running it is common to need more jars in your CLASSPATH to load a Clojure source file, and I'd like to do it without having to restart Clojure itself (which is not really an option when using it on Slime on Emacs).
Это причина, но я не хочу, чтобы этот вопрос был помечен как some-weird-language some-weird-editor и игнорировался большинством разработчиков Java, у которых может быть ответ.




Я не верю, что вы можете - правильнее (я считаю) создать новый загрузчик классов с новым путем. В качестве альтернативы вы можете написать свой собственный загрузчик классов, который позволяет динамически изменять путь к классам (для этого загрузчика).
Вы можете изучить использование java.net.URLClassLoader. Это позволяет вам программно загружать классы, которые изначально не были в вашем пути к классам, хотя я не уверен, что это именно то, что вам нужно.
Обновление Q4 2017: как прокомментировал ниже от vda8888, в Java 9 система java.lang.ClassLoader больше не является java.net.URLClassLoader.
См. "Руководство по миграции на Java 9: семь наиболее распространенных проблем"
The class loading strategy that I just described is implemented in a new type and in Java 9 the application class loader is of that type.
That means it is not aURLClassLoaderanymore, so the occasional(URLClassLoader) getClass().getClassLoader()or(URLClassLoader) ClassLoader.getSystemClassLoader()sequences will no longer execute.
java.lang.ModuleLayer будет альтернативным подходом, используемым для воздействия на путь к модулю (вместо пути к классам). См., Например, «Модули Java 9 - основы JPMS».
Для Java 8 или ниже:
Некоторые общие комментарии:
вы не можете (переносимым способом, который гарантированно работает, см. ниже) изменить системный путь к классам. Вместо этого вам нужно определить новый ClassLoader.
ClassLoaders работают иерархически ... поэтому любой класс, который делает статическую ссылку на класс X, должен быть загружен в тот же ClassLoader, что и X, или в дочерний ClassLoader. Вы НЕ можете использовать какой-либо пользовательский ClassLoader для правильной загрузки кода по системной ссылке ClassLoader, если это не было сделано раньше. Таким образом, вам необходимо организовать запуск основного кода приложения в пользовательском ClassLoader в дополнение к дополнительному коду, который вы обнаружите.
(При этом треснувший упоминает в комментариях этот пример расширение URLClassLoader)
И вы можете подумать о том, чтобы не писать свой собственный ClassLoader, а просто использовать вместо этого URLClassLoader. Создайте URLClassLoader с URL-адресом нет в URL-адресе родительского загрузчика классов.
URL[] url = {new URL("file://foo")};
URLClassLoader loader = new URLClassLoader(url);
более полное решение будет:
ClassLoader currentThreadClassLoader
= Thread.currentThread().getContextClassLoader();
// Add the conf dir to the classpath
// Chain the current thread classloader
URLClassLoader urlClassLoader
= new URLClassLoader(new URL[]{new File("mtFile").toURL()},
currentThreadClassLoader);
// Replace the thread classloader - assumes
// you have permissions to do so
Thread.currentThread().setContextClassLoader(urlClassLoader);
Если вы предполагаете, что системный загрузчик классов JVM является URLClassLoader (что может быть не так для всех JVM), вы также можете использовать отражение для фактического изменения пути к системному классу ... (но это взлом;)):
public void addURL(URL url) throws Exception {
URLClassLoader classLoader
= (URLClassLoader) ClassLoader.getSystemClassLoader();
Class clazz= URLClassLoader.class;
// Use reflection
Method method= clazz.getDeclaredMethod("addURL", new Class[] { URL.class });
method.setAccessible(true);
method.invoke(classLoader, new Object[] { url });
}
addURL(new File("conf").toURL());
// This should work now!
Thread.currentThread().getContextClassLoader().getResourceAsStream("context.xml");
Это работает, чтобы добавить банку. Но есть ли решение удалить банку? Например, мне нужно обновить банку новым выпуском.
@dmarrazzo 6+ лет спустя, я не знаю. Попробуйте задать отдельный вопрос.
Я встречаю это решение в другом потоке, но с вашим объяснением я четко знаю, как его использовать. 1+
Второе решение (предположим, что системный загрузчик классов JVM является URLClassLoader) перестает работать в JRE 9. Загрузчик системных классов больше не является загрузчиком классов URL.
@ vda8888 Спасибо. Я включил ваш комментарий в ответ для большей наглядности. А также некоторые ссылки.
Это возможно, как видно из двух приведенных ниже ссылок, метод, который предлагает VonC, кажется лучшим, но посмотрите некоторые из этих сообщений и Google для «Java Dynamic Classpath» или «Java Dynamic Class Loading» и узнайте оттуда некоторую информацию.
Я бы опубликовал более подробную информацию, но VonC в значительной степени справился со своей задачей.
От Динамическая загрузка файлов классов и Jar.
Также проверьте этот сообщение на форуме солнца.
Нет необходимости писать собственный загрузчик классов! Есть clojure.lang.DynamicClassLoader.
http://blog.japila.pl/2011/01/dynamically-redefining-classpath-in-clojure-repl/
String s = "java -classpath abcd/ "+pgmname+" "+filename;
Process pro2 = Runtime.getRuntime().exec(s);
BufferedReader in = new BufferedReader(new InputStreamReader(pro2.getInputStream()));
это пример изменения пути к классам в java-программе
Путь к классам изменяется только для дочернего java-процесса, а не для среды, в которой работает ваш код. Это несколько творчески, но для большинства бесполезно.
Это пример запуска нового процесса Java с новым путем к классам. Не пример изменения чего-либо.
«Более полное» решение не работает для меня при использовании Java 1.6.0_13 (64-разрядная версия) в Linux. С другой стороны, метод addURL работает должным образом.