在单独的进程中执行 Java 应用程序
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/636367/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Executing a Java application in a separate process
提问by Ande Turner
Can a Java application be loaded in a separate process using its name, as opposed to its location, in a platform independent manner?
Java 应用程序能否以独立于平台的方式使用其名称而不是其位置在单独的进程中加载?
I know you can execute a program via ...
我知道您可以通过...执行程序
Process process = Runtime.getRuntime().exec( COMMAND );
... the main issue of this method is that such calls are then platform specific.
... 这种方法的主要问题是这样的调用是特定于平台的。
Ideally, I'd wrap a method into something as simple as...
理想情况下,我会将一个方法包装成一个简单的东西......
EXECUTE.application( CLASS_TO_BE_EXECUTED );
... and pass in the fully qualified name of an application class as CLASS_TO_BE_EXECUTED
.
...并将应用程序类的完全限定名称作为CLASS_TO_BE_EXECUTED
.
采纳答案by stepancheg
Two hints:
两个提示:
System.getProperty("java.home") + "/bin/java"
gives you a path to the java executable.
System.getProperty("java.home") + "/bin/java"
为您提供 java 可执行文件的路径。
((URLClassLoader) Thread.currentThread().getContextClassLoader()).getURL()
helps you to reconstruct the classpath of current application.
((URLClassLoader) Thread.currentThread().getContextClassLoader()).getURL()
帮助您重建当前应用程序的类路径。
Then your EXECUTE.application
is just (pseudocode):
那么你EXECUTE.application
的只是(伪代码):
Process.exec(javaExecutable, "-classpath", urls.join(":"), CLASS_TO_BE_EXECUTED)
Process.exec(javaExecutable, "-classpath", urls.join(":"), CLASS_TO_BE_EXECUTED)
回答by TofuBeer
Do you really have to launch them natively? Could you just call their "main" methods directly? The only special thing about main is that the VM launcher calls it, nothing stops you from calling main yourself.
你真的必须在本地启动它们吗?你能直接调用他们的“主要”方法吗?main 的唯一特别之处在于 VM 启动器会调用它,没有什么可以阻止您自己调用 main。
回答by Ande Turner
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;
}
}
回答by jasonnerothin
Following on what TofuBeer had to say: Are you sure you really need to fork off another JVM? The JVM has really good support for concurrency these days, so you can get a lot of functionality for relatively cheap by just spinning off a new Thread or two (that may or may not require calling into Foo#main(String[])). Check out java.util.concurrent for more info.
按照 TofuBeer 的说法:你确定你真的需要分叉另一个 JVM 吗?现在 JVM 对并发的支持非常好,因此您可以通过分拆一个或两个新线程(可能需要也可能不需要调用 Foo#main(String[]))以相对便宜的价格获得很多功能。查看 java.util.concurrent 了解更多信息。
If you decide to fork, you set yourself up for a bit of complexity related to finding required resources. That is, if your app is changing frequently and depends upon a bunch of jar files, you'll need to keep track of them all so that they can be passed out to the classpath arg. Additionally, such an approach requires to to infer both the location of the (currently executing) JVM (which may not be accurate) and the location of the current classpath (which is even less likely to be accurate, depending upon the way that the spawning Thread has been invoked - jar, jnlp, exploded .classes dir, some container, etc.).
如果您决定分叉,那么您将面临与查找所需资源相关的一些复杂性。也就是说,如果您的应用程序频繁更改并依赖于一堆 jar 文件,则您需要跟踪所有这些文件,以便将它们传递给类路径 arg。此外,这种方法需要推断(当前正在执行的)JVM 的位置(这可能不准确)和当前类路径的位置(甚至不太可能准确,这取决于生成的方式)线程已被调用 - jar、jnlp、爆炸的 .classes 目录、一些容器等)。
On the other hand, linking into static #main methods has its pitfalls as well. static modifiers have a nasty tendency of leaking into other code and are generally frowned upon by design-minded folks.
另一方面,链接到静态 #main 方法也有其缺陷。静态修饰符有泄漏到其他代码的令人讨厌的趋势,并且通常被具有设计意识的人所反对。
回答by lothar
Did you check out the ProcessBuilder API? It's available since 1.5
您是否检查过 ProcessBuilder API?它从 1.5 开始可用
http://java.sun.com/javase/6/docs/api/java/lang/ProcessBuilder.html
http://java.sun.com/javase/6/docs/api/java/lang/ProcessBuilder.html
回答by hallidave
This is a synthesis of some of the other answers that have been provided. The Java system properties provide enough information to come up with the path to the java command and the classpath in what, I think, is a platform independent way.
这是已提供的一些其他答案的综合。Java 系统属性提供了足够的信息来以我认为独立于平台的方式提出 java 命令和类路径的路径。
public final class JavaProcess {
private JavaProcess() {}
public static int exec(Class klass, List<String> args) 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.getName();
List<String> command = new LinkedList<String>();
command.add(javaBin);
command.add("-cp");
command.add(classpath);
command.add(className);
if (args != null) {
command.addAll(args);
}
ProcessBuilder builder = new ProcessBuilder(command);
Process process = builder.inheritIO().start();
process.waitFor();
return process.exitValue();
}
}
You would run this method like so:
你会像这样运行这个方法:
int status = JavaProcess.exec(MyClass.class, args);
I thought it made sense to pass in the actual class rather than the String representation of the name since the class has to be in the classpath anyways for this to work.
我认为传递实际的类而不是名称的字符串表示是有意义的,因为无论如何类必须在类路径中才能工作。
回答by StaxMan
This might be an overkill for you, but Project Akumadoes what you want and more. I found it via this entryat Kohsuke's (one of Sun's rock start programmers) fabulously useful blog.
这对您来说可能有点矫枉过正,但Akuma 项目可以满足您的需求,甚至更多。我通过Kohsuke(Sun 的摇滚入门程序员之一)非常有用的博客中的这篇文章找到了它。
回答by Matiaan
A problem that occurs when you run this from a java GUI is it runs in the background. So you cannot see the command prompt at all.
从 Java GUI 运行它时出现的问题是它在后台运行。所以你根本看不到命令提示符。
To get around this, you have to run the java.exe through "cmd.exe" AND "start". I dont know why, but if you put "cmd /c start" infront it shows the command prompt as it runs.
要解决这个问题,您必须通过“cmd.exe”和“start”运行 java.exe。我不知道为什么,但是如果你把“cmd / c start”放在前面,它会在运行时显示命令提示符。
However, the problem with "start" is that if there is a space in the path to the application (which the path to the java exe usually have as it is in C:\Program Files\Java\jre6\bin\java.exe or similar), then start just fails with "cannot find c:\Program"
但是,“开始”的问题在于,如果应用程序的路径中有空格(java exe 的路径通常在 C:\Program Files\Java\jre6\bin\java.exe或类似),然后启动失败并显示“找不到 c:\Program”
So you have to put quotes around C:\Program Files\Java\jre6\bin\java.exe Now start complains about parameters that you pass to java.exe: "The system cannot find the file -cp."
所以你必须在 C:\Program Files\Java\jre6\bin\java.exe 周围加上引号 现在开始抱怨你传递给 java.exe 的参数:“系统找不到文件 -cp。”
Escaping the space in "Program Files" with a backslash also does not work. So the idea is to not use space. Generate a temporary file with the bat extension and then put your command with spaces in there and run the bat. However, running a bat through start, does not exit when done, so you have to put "exit" at the end of the batch file.
用反斜杠转义“程序文件”中的空间也不起作用。所以这个想法是不使用空间。生成一个带有 bat 扩展名的临时文件,然后将带有空格的命令放入其中并运行 bat.bat 。但是,通过 start 运行 bat,完成后不会退出,因此您必须将“exit”放在批处理文件的末尾。
This still seems yucky.
这看起来还是很恶心。
So, looking for alternatives, I have found that using quote space quote in the space of "Program Files" actually works with start.
因此,在寻找替代方案时,我发现在“程序文件”空间中使用引用空间引用实际上与 start 一起使用。
In the EXECUTE class above change the string builder appends to:
在上面的 EXECUTE 类中,将字符串生成器附加到:
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 ).
回答by andrej
Expanding on @stepancheg's answer the actual code would look like so (in the form of a test).
扩展@stepancheg 的答案,实际代码看起来像这样(以测试的形式)。
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.getName()
// main class arguments go here
)
.inheritIO()
.start();
int exitCode = process.waitFor();
System.out.println("process stopped with exitCode " + exitCode);
}
}