我如何重新启动Java应用程序?

我如何重新启动Java AWT应用程序? 我有一个我附加了一个事件处理程序的button。 我应该使用什么代码重新启动应用程序?

我想和Application.Restart()在C#应用程序中做同样的事情。

当然可以重启一个Java应用程序。

以下方法显示了重新启动Java应用程序的方法:

 public void restartApplication() { final String javaBin = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java"; final File currentJar = new File(MyClassInTheJar.class.getProtectionDomain().getCodeSource().getLocation().toURI()); /* is it a jar file? */ if(!currentJar.getName().endsWith(".jar")) return; /* Build command: java -jar application.jar */ final ArrayList<String> command = new ArrayList<String>(); command.add(javaBin); command.add("-jar"); command.add(currentJar.getPath()); final ProcessBuilder builder = new ProcessBuilder(command); builder.start(); System.exit(0); } 

基本上它有以下几点:

  1. findjava可执行文件(我在这里使用了java二进制文件,但这取决于你的要求)
  2. find应用程序(在我的情况下,一个jar,使用MyClassInTheJar类来查找jar位置本身)
  3. build立一个命令来重新启动jar(在这种情况下使用java二进制文件)
  4. 执行它! (并因此终止当前的应用程序并再次启动)
 import java.io.File; import java.io.IOException; import java.lang.management.ManagementFactory; public class Main { public static void main(String[] args) throws IOException, InterruptedException { StringBuilder cmd = new StringBuilder(); cmd.append(System.getProperty("java.home") + File.separator + "bin" + File.separator + "java "); for (String jvmArg : ManagementFactory.getRuntimeMXBean().getInputArguments()) { cmd.append(jvmArg + " "); } cmd.append("-cp ").append(ManagementFactory.getRuntimeMXBean().getClassPath()).append(" "); cmd.append(Main.class.getName()).append(" "); for (String arg : args) { cmd.append(arg).append(" "); } Runtime.getRuntime().exec(cmd.toString()); System.exit(0); } } 

献给所有那些说不可能的人。

这个程序收集所有可用的信息来重build原始的命令行。 然后,它启动它,因为它是相同的命令,您的应用程序第二次启动。 然后我们退出原来的程序,子程序仍然在运行(甚至在Linux下),并做同样的事情。

警告 :如果你运行这个,请注意,它永远不会创build新的进程,类似于一个叉形炸弹 。

基本上,你不能。 至less不是以可靠的方式。

要重新启动Java程序,您需要重新启动JVM。 要重新启动JVM,您需要

  1. find使用的java启动程序。 你可以尝试使用System.getProperty("java.home")但是不能保证这实际上指向用来启动应用程序的启动器。 (返回的值可能不会指向用于启动应用程序的JRE,也可能已被-Djava.home覆盖。

  2. 你大概会想要尊重原始的内存设置等( -Xmx-Xms ,…),所以你需要弄清楚哪些设置用于启动第一个JVM。 您可以尝试使用ManagementFactory.getRuntimeMXBean().getInputArguments()但不能保证这将反映使用的设置。 这甚至在该方法的文档中详细说明:

    通常,并不是所有的命令行选项都传递给Java虚拟机。 因此,返回的input参数可能不包括所有的命令行选项。

  3. 如果您的程序从Standard.in读取input,则原始stdin将在重新启动时丢失。

  4. 许多这些技巧和黑客将在SecurityManager的存在下失败。


另一方面:你不应该需要。

我build议你devise你的应用程序,这样很容易清理所有的东西,然后创build一个“主”类的新实例。

许多应用程序被devise为除了在main方法中创build一个实例之外什么也不做:

 public class MainClass { ... public static void main(String[] args) { new MainClass().launch(); } ... } 

通过使用这种模式,应该很容易做到这样的事情:

 public class MainClass { ... public static void main(String[] args) { boolean restart; do { restart = new MainClass().launch(); } while (restart); } ... } 

并让launch()返回true,当且仅当应用程序以需要重新启动的方式closures。

严格地说,一个Java程序不能自行重启,因为这样做必须杀死正在运行的JVM,然后重新启动它,但是一旦JVM不再运行(死亡),就不能采取任何行动。

您可以使用自定义类加载器来加载,打包并重新启动AWT组件,但是这可能会导致GUI事件循环方面的麻烦。

根据应用程序的启动方式,您可以在包含do / while循环的包装脚本中启动JVM,这个循环在JVM退出时使用特定的代码继续执行,那么AWT应用程序将不得不调用System.exit(RESTART_CODE) 。 例如,在编写伪代码的脚本中:

 DO # Launch the awt program EXIT_CODE = # Get the exit code of the last process WHILE (EXIT_CODE == RESTART_CODE) 

AWT应用程序应使用除“正常”终止(不需要重新启动)上的RESTART_CODE之外的其他内容退出JVM。

Eclipse通常在安装插件后重新启动。 他们使用包装eclipse.exe(启动应用程序)的窗口。 此应用程序执行核心eclipse runner jar,如果eclipse java应用程序以重新启动代码终止,则eclipse.exe将重新启动工作台。 您可以构build一个类似的本地代码,shell脚本或另一个Java代码包装来实现重启。

如果你真的需要重新启动你的应用程序,你可以写一个单独的应用程序启动它…

这个页面为不同的场景提供了很多不同的例子

http://www.rgagnon.com/javadetails/java-0014.html

虽然这个问题已经很老了,但我却偶然发现了一些解决scheme的问题,并决定把我的build议join到这个组合中。

一些解决scheme的问题是,他们build立一个单一的命令string。 这在一些参数包含空格时会产生问题,特别是java.home

例如,在Windows上,行

 final String javaBin = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java"; 

可能返回如下所示: C:\Program Files\Java\jre7\bin\java

由于Program Files的空间,此string必须用引号括起或转义。 不是一个巨大的问题,但有些烦人和容易出错,特别是在跨平台的应用程序。

因此,我的解决scheme构build命令作为一个命令数组

 public static void restart(String[] args) { ArrayList<String> commands = new ArrayList<String>(4 + jvmArgs.size() + args.length); List<String> jvmArgs = ManagementFactory.getRuntimeMXBean().getInputArguments(); // Java commands.add(System.getProperty("java.home") + File.separator + "bin" + File.separator + "java"); // Jvm arguments for (String jvmArg : jvmArgs) { commands.add(jvmArg); } // Classpath commands.add("-cp"); commands.add(ManagementFactory.getRuntimeMXBean().getClassPath()); // Class to be executed commands.add(BGAgent.class.getName()); // Command line arguments for (String arg : args) { commands.add(arg); } File workingDir = null; // Null working dir means that the child uses the same working directory String[] env = null; // Null env means that the child uses the same environment String[] commandArray = new String[commands.size()]; commandArray = commands.toArray(commandArray); try { Runtime.getRuntime().exec(commandArray, env, workingDir); System.exit(0); } catch (IOException e) { e.printStackTrace(); } } 

当我遇到这个问题时,我正在研究这个问题。

不pipe答案是否已被接受,我仍然希望提供一个完整性的替代方法。 具体而言,Apache Ant是一个非常灵活的解决scheme。

基本上,所有东西都归结为一个Ant脚本文件,其中包含一个Java执行任务(请参阅这里和这里 ),从Java代码调用(请参阅此处 )。 这个可以作为方法启动的 Java代码可能是需要重启的应用程序的一部分。 应用程序需要依赖Apache Ant库(jar)。

每当应用程序需要重新启动,它应该调用方法启动并退出虚拟机。 Ant java任务应该有选项forkspawn设置为true。

这里是一个Ant脚本的例子:

 <project name="applaucher" default="launch" basedir="."> <target name="launch"> <java classname="package.MasinClass" fork="true" spawn="true"> <jvmarg value="-splash:splash.jpg"/> <jvmarg value="-D other VM params"/> <classpath> <pathelement location="lib-1.jar" /> ... <pathelement location="lib-n.jar" /> </classpath> </java> </target> </project> 

启动方法的代码可能如下所示:

 public final void launch(final String antScriptFile) { /* configure Ant and execute the task */ final File buildFile = new File(antScriptFile); final Project p = new Project(); p.setUserProperty("ant.file", buildFile.getAbsolutePath()); final DefaultLogger consoleLogger = new DefaultLogger(); consoleLogger.setErrorPrintStream(System.err); consoleLogger.setOutputPrintStream(System.out); consoleLogger.setMessageOutputLevel(Project.MSG_INFO); p.addBuildListener(consoleLogger); try { p.fireBuildStarted(); p.init(); final ProjectHelper helper = ProjectHelper.getProjectHelper(); p.addReference("ant.projectHelper", helper); helper.parse(p, buildFile); p.executeTarget(p.getDefaultTarget()); p.fireBuildFinished(null); } catch (final BuildException e) { p.fireBuildFinished(e); } /* exit the current VM */ System.exit(0); 

}

这里的一个非常方便的事情是相同的脚本用于初始应用程序启动以及重新启动。

视窗

 public void restartApp(){ // This launches a new instance of application dirctly, // remember to add some sleep to the start of the cmd file to make sure current instance is // completely terminated, otherwise 2 instances of the application can overlap causing strange // things:) new ProcessBuilder("cmd","/c start /min c:/path/to/script/that/launches/my/application.cmd ^& exit").start(); System.exit(0); } 

/ min在最小化窗口中启动脚本

完成后退出closurescmd窗口

一个示例cmd脚本可能是

 @echo off rem add some sleep (eg 10 seconds) to allow the preceding application instance to release any open resources (like ports) and exit gracefully, otherwise the new instance could fail to start sleep 10 set path=C:\someFolder\application_lib\libs;%path% java -jar application.jar 

睡10次睡10秒

只是添加其他答案中不存在的信息。

如果procfs /proc/self/cmdline可用

如果你在一个提供procfs的环境中运行,因此/proc文件系统可用(这意味着这不是一个可移植的解决scheme),你可以让Java读取/proc/self/cmdline来重新启动自己,如下所示:

 public static void restart() throws IOException { new ProcessBuilder(getMyOwnCmdLine()).inheritIO().start(); } public static String[] getMyOwnCmdLine() throws IOException { return readFirstLine("/proc/self/cmdline").split("\u0000"); } public static String readFirstLine(final String filename) throws IOException { try (final BufferedReader in = new BufferedReader(new FileReader(filename))) { return in.readLine(); } } 

/proc/self/cmdline可用的系统上,这可能是如何从Java重新启动当前Java进程的最优雅方法。 不涉及JNI,也不需要猜测path和东西。

包括GNU / Linux(包括Android)在内的许多UNIX系统现在都有procfs但是在一些像FreeBSD这样的系统上,它已经被淘汰并被淘汰了。 Mac OS X是一个例外,它没有procfsWindows也没有procfs 。 Cygwin有procfs,但是它对于Java来说是不可见的,因为它只对使用Cygwin DLL而不是Windows系统调用的应用程序可见,而Java不知道Cygwin。

不要忘记使用ProcessBuilder.inheritIO()

缺省情况下,启动的Process的stdin / stdout / stderr (在Java中称为System.in / System.out / System.err )设置为允许当前正在运行的进程与新启动的进程进行通信的pipe道 。 如果你想重新启动当前进程,这很可能不是你想要的 。 相反,你会希望stdin / stdout / stderr与当前虚拟机的相同。 这被称为inheritance 。 您可以通过调用ProcessBuilder实例的inheritIO()来完成。

Windows的陷阱

restart()函数的一个常见用例是在更新后重新启动应用程序。 最后一次我在Windows上尝试这个是有问题的。 当使用新版本覆盖应用程序的.jar文件时,应用程序开始行为不端并给出.jar文件的例外。 我只是告诉,如果这是你的用例。 当时我通过将应用程序包装在一个batch file中,并使用从batch file中查询的System.exit()中的魔术返回值来解决该问题,并使batch file重新启动应用程序。

老问题和所有这一切。 但是这是另一种提供一些优点的方式。

在Windows上,您可以要求任务计划程序为您再次启动您的应用程序。 这具有在应用重新启动之前等待特定时间的优点。 你可以去任务pipe理器并删除任务,并停止重复。

 SimpleDateFormat hhmm = new SimpleDateFormat("kk:mm"); Calendar aCal = Calendar.getInstance(); aCal.add(Calendar.SECOND, 65); String nextMinute = hhmm.format(aCal.getTime()); //Task Scheduler Doesn't accept seconds and won't do current minute. String[] create = {"c:\\windows\\system32\\schtasks.exe", "/CREATE", "/F", "/TN", "RestartMyProg", "/SC", "ONCE", "/ST", nextMinute, "/TR", "java -jar c:\\my\\dev\\RestartTest.jar"}; Process proc = Runtime.getRuntime().exec(create, null, null); System.out.println("Exit Now"); try {Thread.sleep(1000);} catch (Exception e){} // just so you can see it better System.exit(0); 
 System.err.println("Someone is Restarting me..."); setVisible(false); try { Thread.sleep(600); } catch (InterruptedException e1) { e1.printStackTrace(); } setVisible(true); 

我想你不是真的想停止应用程序,而是要“重新启动”它。 为此,您可以使用此function,并在睡眠之前和隐藏窗口之后添加“重置”。

Interesting Posts