在单独的进程中执行Java应用程序

Java应用程序是否可以以独立于平台的方式使用其名称(而不是其位置)在单独的进程中加载​​?

我知道你可以通过执行一个程序…

Process process = Runtime.getRuntime().exec( COMMAND ); 

…这个方法的主要问题是这样的调用是特定于平台的。

理想情况下,我会把一个方法包装成一些简单的…

 EXECUTE.application( CLASS_TO_BE_EXECUTED ); 

…并作为CLASS_TO_BE_EXECUTED应用程序类的完全限定名称。

两个提示:

System.getProperty("java.home") + "/bin/java"为您提供了一个java可执行文件的path。

((URLClassLoader() Thread.currentThread().getContextClassLoader()).getURL()帮助您重build当前应用程序的类path。

那么你的EXECUTE.application就是(伪代码):

Process.exec(javaExecutable, "-classpath", urls.join(":"), CLASS_TO_BE_EXECUTED)

这是一些已经提供的其他答案的综合。 Java系统属性提供了足够的信息来提供java命令和类path的path,我认为它是一种独立于平台的方式。

 public final class JavaProcess { private JavaProcess() {} public static int exec(Class klass) throws IOException, InterruptedException { String javaHome = System.getProperty("java.home"); String javaBin = javaHome + File.separator + "bin" + File.separator + "java"; String classpath = System.getProperty("java.class.path"); String className = klass.getCanonicalName(); ProcessBuilder builder = new ProcessBuilder( javaBin, "-cp", classpath, className); Process process = builder.start(); process.waitFor(); return process.exitValue(); } } 

你可以这样运行这个方法:

 int status = JavaProcess.exec(MyClass.class); 

我认为传递实际的类而不是名称的string表示是有意义的,因为无论如何这个类都必须在classpath中才能工作。

这可能是一个矫枉过正的事情,但项目Akuma做你想要的,甚至更多。 我通过Kohsuke(Sun的摇滚开始程序员之一)这个非常有用的博客发现了它。

扩展@ stepancheg的答案,实际的代码看起来像这样(以testing的forms)。

 import org.junit.Test; import java.io.File; import java.net.URL; import java.net.URLClassLoader; import java.util.Arrays; import java.util.stream.Collectors; public class SpinningUpAJvmTest { @Test public void shouldRunAJvm() throws Exception { String classpath = Arrays.stream(((URLClassLoader) Thread.currentThread().getContextClassLoader()).getURLs()) .map(URL::getFile) .collect(Collectors.joining(File.pathSeparator)); Process process = new ProcessBuilder( System.getProperty("java.home") + "/bin/java", "-classpath", classpath, MyMainClass.class.getCanonicalName() // main class arguments go here ) .inheritIO() .start(); int exitCode = process.waitFor(); System.out.println("process stopped with exitCode " + exitCode); } } 

你检查过ProcessBuilder API吗? 从1.5开始可用

http://java.sun.com/javase/6/docs/api/java/lang/ProcessBuilder.html

你真的必须在本地推出它们吗? 你可以直接调用他们的“main”方法吗? 关于main的唯一特别的事情是VM启动器调用它,没有什么能阻止你自己调用main。

 public abstract class EXECUTE { private EXECUTE() { /* Procedural Abstract */ } public static Process application( final String CLASS_TO_BE_EXECUTED ) { final String EXEC_ARGUMENT = new StringBuilder(). append( java.lang.System.getProperty( "java.home" ) ). append( java.io.File.separator ). append( "bin" ). append( java.io.File.separator ). append( "java" ). append( " " ). append( new java.io.File( "." ).getAbsolutePath() ). append( java.io.File.separator ). append( CLASS_TO_BE_EXECUTED ). toString(); try { return Runtime.getRuntime().exec( EXEC_ARGUMENT ); } catch ( final Exception EXCEPTION ) { System.err.println( EXCEPTION.getStackTrace() ); } return null; } } 

继TofuBeer不得不说的:你确定你真的需要从另一个JVM中分离出来吗? JVM现在对并发性有很好的支持,所以你可以通过分离一个或者两个新线程(可能需要或者不需要调用Foo#main(String []))来获得相当便宜的很多function。 查看java.util.concurrent获取更多信息。

如果你决定分叉,你会为自己找一些与find所需资源相关的复杂性。 也就是说,如果您的应用程序频繁更改并依赖于一堆jar文件,则需要跟踪这些文件,以便将它们传递给classpath arg。 此外,这种方法需要推断(当前正在执行的)JVM的位置(可能不准确)和当前类path的位置(根据产卵的方式,这更不可能是准确的线程已被调用 – jar,jnlp,爆炸.classes目录,一些容器等)。

另一方面,链接到静态#main方法也有其缺陷。 静态修饰符具有泄漏到其他代码中的令人讨厌的倾向,并且通常由具有devise意识的人员所诟病。

从java GUI运行时发生的问题是在后台运行。 所以你根本看不到命令​​提示符。

为了解决这个问题,你必须通过“cmd.exe”和“start”运行java.exe。 我不知道为什么,但如果你把“CMD / C开始”面前它显示命令提示符,因为它运行。

然而,“开始”的问题是,如果在应用程序的path中有一个空格(java程序的path通常是C:\ Program Files \ Java \ jre6 \ bin \ java.exe或类似),然后开始只是失败,“无法findC:\ Program”

所以你必须把引号放在C:\ Program Files \ Java \ jre6 \ bin \ java.exe现在开始抱怨你传递给java.exe的参数:“系统找不到文件-cp。

用“反斜杠”转义“Program Files”中的空格也不起作用。 所以这个想法是不使用空间。 用蝙蝠扩展名生成一个临时文件,然后将命令与空格放在那里,然后运行蝙蝠。 但是,通过启动运行bat,完成后不会退出,因此必须将“exit”放在batch file的末尾。

这似乎还是很糟糕。

所以,寻找替代品,我发现在“程序文件”的空间中使用引号空间引用实际上与开始工作。

在上面的EXECUTE类中,string生成器附加到:

 append( "cmd /C start \"Some title\" " ). append( java.lang.System.getProperty( "java.home" ).replaceAll(" ", "\" \"") ). append( java.io.File.separator ). append( "bin" ). append( java.io.File.separator ). append( "java" ). append( " " ). append( new java.io.File( "." ).getAbsolutePath() ). append( java.io.File.separator ). append( CLASS_TO_BE_EXECUTED ).