问题 如何在Java中的新进程中启动“main”?


问题很简单。如何在另一个java进程中启动main方法?现在我这样做:

startOptions = new String[] {"java", "-jar", "serverstart.jar"};
new ProcessBuilder(startOptions).start();

但他们要求我不要使用外部.jar文件。 serverstart.jar显然有一个main方法,但是可以在不调用.jar文件的情况下在另一个进程中调用该main方法吗?

我在考虑这样的事情:

new ProcessBuilder(ServerStart.main(startOptions)).start();

但我不知道是否存在类似的东西。

亲切的问候,


5170
2018-05-13 08:19


起源

在ProcessBuilder中将当前类路径复制为参数是否有用或被允许? .. System.getProperty(“java.class.path”) - laher
我不知道,我该怎么做呢? - Walle
然后你将有你的classpath你只需要传递给它 -cp 并加载 java - Jigar Joshi


答案:


假设一个带有新类加载器的新线程是不够的(我会投票支持这个解决方案),我知道你需要创建一个独特的进程来调用类中的main方法,而不必在其中声明为“jar main方法”。清单文件 - 因为您不再具有不同的serverstart.jar。

在这种情况下,您只需致电 java -cp $yourClassPath your.package.ServerStart,就像你没有(或不想使用)清单Main-Class时运行任何java应用程序一样。


6
2018-05-13 09:01



我这样做了: startOptions = new String[] {"java", "-cp", System.getProperty("user.dir"), "target.classes." + ServerStart.class.getName()}; 但它给了我 java.lang.NoClassDefFoundError: target/classes/sample/plugin/hello_maven_plugin/ ServerStart (wrong name: sample/plugin/hello_maven_plugin/ServerStart) 所以我想我在正确的目录中,我猜他找不到合适的课程。 - Walle
假设你的 user.dir 是maven项目的根目录, target/classes 不应该用作包前缀,而是添加到文件系统路径: new String[] {"java", "-cp", System.getProperty("user.dir") + "/target/classes", ServerStart.class.getName()} - Costi Ciudatu
太好了!我现在在正确的班级,但现在我遇到了他无法找到指定的.jar文件的问题。在我的ServerStart中,他启动了从hsqldb导入的服务器,但他找不到它。 Caused by: java.lang.ClassNotFoundException: org.hsqldb.Server - Walle
但是,依靠从maven项目根目录运行应用程序并不是一个好主意,所以我认为更好的解决方案是简单地重用当前进程的类路径(并使用当前目录作为后备): new String[] {"java", "-cp", System.getProperty("java.class.path", "."), ServerStart.class.getName()} - Costi Ciudatu
在我看到你的回复之前我写了我之前的评论,但它似乎也解决了你提到的最新问题,因为将使用当前进程的FULL类路径。 - Costi Ciudatu


从java创建一个新的“java”进程是  可能以后 两个进程无法共享一个JVM。 (见这个 问题和接受的答案)。


如果你能够创造一个新的 Thread 代替 Process 你可以用自定义做 ClassLoader。就是这样 关闭你可以进入一个新的过程。所有静态和最终字段都将重新初始化!

还要注意 "ServerStart class(以下示例)必须位于当前执行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);
        }
    }
}

7
2018-05-13 08:51



Creating a new "java" process from java is not possible since two processes can't share one JVM. - 也许我错过了一些东西,但我没有看到在这里共享一个JVM。 - Costi Ciudatu
@Costi,不,但是ServerStart.main(startOptions)表明他有一个可能性,他想从JVM中调用main方法。 - aioobe
当然,但这只是一个方法调用,它与任何新进程无关。 - Costi Ciudatu
你是不是更好地继承URLClassLoader而不是ClassLoader?然后你就可以毫不费力地处理Jars和类目录了。 - Paul Cager
@aioobe:我现在知道了, ProcessBuilder 与...相结合 main() 方法调用确实造成了这种混乱,因此dacwe从一开始就澄清这一点是正确的。 - Costi Ciudatu


我建议从java调用一个shellcript并使用它来启动新进程(如果你不能只使用另一个线程)。


1
2018-05-13 09:43





您可以使用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);
}

-2
2018-05-13 08:28



这不会启动新进程,也可能不会重新初始化静态和最终变量。 - dacwe
然后只需在新线程中运行反射代码? - Datajam
不行。你需要一个自定义 ClassLoader 重新初始化所有变量。 - dacwe