Как я могу начать "главное" в новом процессе на Java?
Вопрос довольно прост. Как запустить основной метод в другом Java-процессе? Теперь я делаю это так:
startOptions = new String[] {"java", "-jar", "serverstart.jar"};
new ProcessBuilder(startOptions).start();
Но они попросили меня сделать это не с внешним .jar файлом. У serverstart.jar, очевидно, есть основной метод, но можно ли вызвать этот основной метод в другом процессе, не вызывая файл .jar?
Я думаю о чем-то вроде этого:
new ProcessBuilder(ServerStart.main(startOptions)).start();
Но я не знаю, существует ли что-то подобное.
С уважением,
Ответы
Ответ 1
Предполагая, что новый поток с новым загрузчиком классов недостаточно (я бы проголосовал за это решение, хотя), я понимаю, что вам нужно создать отдельный процесс, который вызывает основной метод в классе, не указав его как "основной метод jar" "в файле манифеста - поскольку у вас больше нет отдельного serverstart.jar.
В этом случае вы можете просто вызвать java -cp $yourClassPath your.package.ServerStart
, как и для запуска любого java-приложения, если у вас нет (или не хотите использовать) манифеста Main-Class.
Ответ 2
Создание нового java-процесса из java невозможно, поскольку два процесса не могут использовать один JVM. (См. Этот вопрос и принятый ответ).
Если вы можете жить с созданием нового Thread
вместо Process
, вы можете сделать это с помощью настраиваемого ClassLoader
. Это как закрыть, вы можете перейти к новому процессу. Все статические и конечные поля будут повторно инициализированы!
Также обратите внимание, что класс "ServerStart
(для примера ниже) должен находиться в пути класса текущего исполняемого JVM):
public static void main(String args[]) throws Exception {
// start the server
start("ServerStart", "arg1", "arg2");
}
private static void start(final String classToStart, final String... args) {
// start a new thread
new Thread(new Runnable() {
public void run() {
try {
// create the custom class loader
ClassLoader cl = new CustomClassLoader();
// load the class
Class<?> clazz = cl.loadClass(classToStart);
// get the main method
Method main = clazz.getMethod("main", args.getClass());
// and invoke it
main.invoke(null, (Object) args);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
И это пользовательский загрузчик классов:
private static class CustomClassLoader extends URLClassLoader {
public CustomClassLoader() {
super(new URL[0]);
}
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, (CodeSource) null);
} catch(Exception e){
throw new ClassNotFoundException(e.getMessage(), e);
}
}
}
Ответ 3
Я бы предложил вызвать shellscript из java и использовать его для запуска нового процесса (если вы вообще не работаете с другим потоком).
Ответ 4
Вы можете сделать это, используя Reflection (пакет java.lang.reflect).
public static void main(String[] args) throws Exception {
Class c = Class.forName("ServerStart");
Class[] argTypes = { args.getClass() };
Method m = c.getMethod("main", argTypes);
Object passedArgv[] = { args };
m.invoke(null, passedArgv);
}