Java应用程序是否可以使用其名称(而不是其位置)以独立于平台的方式在单独的进程中加载?
我知道你可以通过......执行一个程序。
Process process = Runtime.getRuntime().exec( COMMAND );
......这种方法的主要问题是此类调用是特定于平台的。
理想情况下,我将方法包装成一些简单的东西......
EXECUTE.application( CLASS_TO_BE_EXECUTED );
...并将应用程序类的完全限定名称传递为CLASS_TO_BE_EXECUTED
。
答案 0 :(得分:64)
这是已经提供的一些其他答案的综合。 Java系统属性提供了足够的信息来提供java命令和类路径的路径,我认为这是一种独立于平台的方式。
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.getName();
ProcessBuilder builder = new ProcessBuilder(
javaBin, "-cp", classpath, className);
Process process = builder.inheritIO().start();
process.waitFor();
return process.exitValue();
}
}
您可以像这样运行此方法:
int status = JavaProcess.exec(MyClass.class);
我认为传递实际的类而不是名称的String表示是有意义的,因为类必须在类路径中才能使其工作。
答案 1 :(得分:41)
两个提示:
System.getProperty("java.home") + "/bin/java"
为您提供了java可执行文件的路径。
((URLClassLoader) Thread.currentThread().getContextClassLoader()).getURL()
可帮助您重建当前应用程序的类路径。
然后你的EXECUTE.application
只是(伪代码):
Process.exec(javaExecutable, "-classpath", urls.join(":"), CLASS_TO_BE_EXECUTED)
答案 2 :(得分:5)
扩展@ 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);
}
}
答案 3 :(得分:4)
这对你来说可能是一种矫枉过正,但Project Akuma可以满足您的需求。 我在Kohsuke's(Sun的摇滚开始程序员之一)通过this entry找到了它,这是非常有用的博客。
答案 4 :(得分:3)
你真的必须本地推出它们吗?你能直接称他们的“主要”方法吗?关于main的唯一特殊之处是VM启动器调用它,没有什么能阻止你自己调用main。
答案 5 :(得分:3)
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;
}
}
答案 6 :(得分:3)
您是否查看了ProcessBuilder API?自1.5以来可用。
http://java.sun.com/javase/6/docs/api/java/lang/ProcessBuilder.html
答案 7 :(得分:1)
关注TofuBeer所说的话:你确定你真的需要分叉另一个JVM吗? JVM现在对并发性有很好的支持,所以只需关闭一个或两个新线程(可能需要也可能不需要调用Foo#main(String [])),你可以相对便宜地获得很多功能。查看java.util.concurrent了解更多信息。
如果您决定进行分叉,那么您需要为自己找到与查找所需资源相关的一些复杂性。也就是说,如果你的应用程序频繁更改并依赖于一堆jar文件,你需要跟踪它们,以便它们可以传递给类路径arg。此外,这种方法需要推断(当前正在执行的)JVM的位置(可能不准确)和当前类路径的位置(根据产生的方式,它更不可能准确)线程已被调用 - jar,jnlp,爆炸.classes dir,某些容器等)。
另一方面,链接到静态#main方法也有其缺陷。静态修饰符具有泄漏到其他代码中的恶劣趋势,并且通常被具有设计意识的人所厌恶。
答案 8 :(得分:1)
从java GUI运行时出现的问题是它在后台运行。 所以你根本看不到命令提示符。
要解决此问题,您必须通过“cmd.exe”和“start”运行java.exe。 我不知道为什么,但如果你把“cmd / c start”放在前面,它会在运行时显示命令提示符。
但是,“start”的问题是如果应用程序的路径中有空格 (java exe的路径通常都是这样的 C:\ Program Files \ Java \ jre6 \ bin \ java.exe或类似的), 然后开始失败,“找不到c:\ Program”
所以你必须在C:\ Program Files \ Java \ jre6 \ bin \ java.exe周围加上引号 现在开始抱怨你传递给java.exe的参数: “系统无法找到文件-cp。”
使用反斜杠转义“Program Files”中的空格也不起作用。 所以我的想法是不使用空间。 使用bat扩展生成一个临时文件,然后在其中放入带空格的命令 并运行蝙蝠。 但是,通过启动运行蝙蝠,完成时不会退出, 所以你必须在批处理文件的末尾加上“退出”。
这似乎仍然令人讨厌。
因此,在寻找替代方案时,我发现在“Program Files”空间中使用引用空间引用实际上与start一起使用。
在上面的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 ).