Перезагрузите себя - могу ли я все заново инициализировать?
У меня есть что-то вроде этого:
public static final String path;
static {
path = loadProperties("config.conf").getProperty("path");
}
public static void main(String... args) {
// ... do stuff (starting threads that reads the final path variable)
// someone want's to update the path (in the config.conf file)
restart(); // ???
}
Я хочу повторно инициализировать JVM, снова вызывая статический инициализатор, а затем main(...)
!
Это можно сделать?
6 ответов
Вы можете запустить свое приложение, используя пользовательский загрузчик классов, это позволит вам загружать и выгружать статические переменные.
Тем не менее, в основном это очень плохой дизайн, чтобы сделать это. Мне нравится делать поля окончательными, но вы не должны делать их окончательными, если хотите изменить их.
Если ваша цель - просто перезагрузить некоторые файлы конфигурации, почему бы не реализовать монитор изменений файлов?
Вот хороший урок на эту тему:
http://download.oracle.com/javase/tutorial/essential/io/notification.html
Я думаю, что то, что вы предлагаете (автоматический перезапуск приложения), было бы немного более громоздким, чем просто наблюдение за обновлениями файлов.
Я принимаю ответ Питера Лоури, но опубликую полный пример для всех!
Я не собираюсь использовать это в производственном коде... есть другие способы сделать это!
public class Test {
public static void main(String args[]) throws Exception {
start();
Thread.sleep(123);
start();
}
private static void start() throws Exception {
ClassLoader cl = new ClassLoader(null) {
protected java.lang.Class<?> findClass(String name)
throws ClassNotFoundException {
try{
String c = name.replace('.', File.separatorChar) +".class";
URL u = ClassLoader.getSystemResource(c);
String classPath = ((String) u.getFile()).substring(1);
File f = new File(classPath);
FileInputStream fis = new FileInputStream(f);
DataInputStream dis = new DataInputStream(fis);
byte buff[] = new byte[(int) f.length()];
dis.readFully(buff);
dis.close();
return defineClass(name, buff, 0, buff.length, null);
} catch(Exception e){
throw new ClassNotFoundException(e.getMessage(), e);
}
}
};
Class<?> t = cl.loadClass("Test$Restartable");
Object[] args = new Object[] { new String[0] };
t.getMethod("main", new String[0].getClass()).invoke(null, args);
}
public static class Restartable {
private static final long argument = System.currentTimeMillis();
public static void main(String args[]) throws Exception {
System.out.println(argument);
}
}
}
Более простой подход - просто не использовать статический инициализатор для этого. Почему бы просто не сделать path
не финал и загрузить его в main
?
Как насчет этой структуры
public static void main(String... args) {
boolean restart = true;
while (restart )
{
retart = runApplication();
}
}
Если вы когда-нибудь обнаружите необходимость перезапустить ваше приложение, пусть runApplication вернет true. Если пришло время выйти, верните false;
Если у вас есть пользовательский интерфейс или демон, чтобы вы могли контролировать вывод в stdout, вы можете создать оболочку снаружи, которая запускает вашу программу.
Если программа при выходе выдает "RESTART", вы можете перезапустить вашу программу снова из этой оболочки. Если нет, это просто заканчивается.
Или, если вам нужен чистый Java-способ, вы можете воспользоваться решением для загрузчиков классов, как упомянул Питер Лори в своем посте. Перед тем как идти по этому пути, вы должны по-настоящему переосмыслить свой дизайн (если это ваш код) и сделать так, чтобы ваш код мог себя очистить.