Как я могу начать "главное" в новом процессе на 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);
}