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

Azf*_*iaz 92 java restart application-restart

如何重新启动Java AWT应用程序?我有一个按钮,我附加了一个事件处理程序.我应该使用什么代码来重启应用程序?

我想做与Application.Restart()C#应用程序相同的事情.

Veg*_*ger 103

当然可以重新启动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);
}
Run Code Online (Sandbox Code Playgroud)

基本上它执行以下操作:

  1. 找到java可执行文件(我在这里使用了java二进制文件,但这取决于你的要求)
  2. 找到应用程序(在我的情况下,使用MyClassInTheJar类来查找jar位置本身的jar)
  3. 构建一个命令来重启jar(在这种情况下使用java二进制文件)
  4. 执行吧!(从而终止当前的应用程序并重新启动它)

  • @Veger质疑`System.exit(0)`是否终止子进程与这个答案是否真的有效以及原因有相同的答案.如果你不能提供合理的解释和你的答案,你做得不好.提供比答案更多问题的答案不是彻底答案的一个例子.好的答案不只是展示代码,而且还解释了它们的工作原理和原因,缺点是什么以及有哪些替代方案.你甚至没有试图掩盖这些事情. (14认同)
  • 自己回答我的问题.样品不起作用!System.exit(0)立即终止客户端进程. (10认同)
  • 很多评论都在争论是否回答@ Horcrux7的问题.你们可以从一开始就告诉他答案lol.好吧,我会继续这样做(有点儿我知道):不,它没有.那里. (7认同)
  • 是不是有一个小时间框架,同一个应用程序的两个版本同时运行? (5认同)
  • System.exit(0)会不会终止子进程吗? (4认同)
  • @Veger所有问题都与答案非常相关. (2认同)
  • 我正在更改答案的第一句话“当然不可能重新启动 Java 应用程序。”这只是“有限场景的解决方法”。类路径变量 jvm 参数和其他变量在哪里。如果其他进程没有启动并且此进程结束怎么办?如果其他实例尝试使用相同的资源“文件/端口”怎么办?这不是问题中提出的对应解决方案。java 中不存在 dotnet 的 Aplication.Restart() 。我会去找另一个应用程序来处理这个问题。 (2认同)

Mei*_*bur 35

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

致力于所有那些说不可能的人.

该程序收集可用于重建原始命令行的所有信息.然后,它启动它,因为它是完全相同的命令,您的应用程序第二次启动.然后我们退出原始程序,子程序仍在运行(即使在Linux下)也做同样的事情.

警告:如果你运行它,请注意它永远不会结束创建新进程,类似于fork炸弹.

  • 如果子 VM 和父 VM 不会通过管道相互连接,则父 VM 可能会结束,这就是启动子 VM 的方式。通过使用“ProcessBuilder”和“inheritIO()”,子虚拟机可以以父虚拟机结束的方式启动。 (3认同)
  • 有什么不同?重新启动PC和关闭操作系统+再次启动它有什么区别吗? (3认同)
  • **可能的改进** `ManagementFactory.getRuntimeMXBean().getInputArguments()` 只会为您提供传递给 JVM 的输入参数。它丢失了传递给您的应用程序的参数。例如,“java -jar start.jar -MISSED_PARAM=true”。在 Oracle jvm 上,您可以使用 System.getProperty("sun.java.command")` 检索这些参数。 (2认同)
  • 我得到了一个版本。此注释是告诉您如何阻止它:重命名包含 java.exe 的路径中的某些内容。 (2认同)

aio*_*obe 22

基本上,你不能.至少不是以可靠的方式.

要重新启动Java程序,需要重新启动JVM.要重新启动JVM

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

  2. 您可能希望尊重原始内存设置等(-Xmx,-Xms...),因此您需要确定用于启动第一个JVM的设置.您可以尝试使用,ManagementFactory.getRuntimeMXBean().getInputArguments()但不能保证这将反映使用的设置.这甚至在方法的文档中详细说明:

    通常,并非'java'命令的所有命令行选项都传递给Java虚拟机.因此,返回的输入参数可能不包括所有命令行选项.

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

  4. 很多这些技巧和黑客都会在存在的情况下失败SecurityManager.


另一方面:你不应该这样做.

我建议你设计你的应用程序,这样就可以很容易地清理每一个东西,然后创建一个新的"main"类实例.

许多应用程序只是在main方法中创建一个实例而不做任何事情:

public class MainClass {
    ...
    public static void main(String[] args) {
        new MainClass().launch();
    }
    ...
}
Run Code Online (Sandbox Code Playgroud)

通过使用这种模式,它应该很容易做类似的事情:

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

launch()当且仅它需要重新启动如果应用程序的方式关闭返回true.

  • +1以获得更好的设计建议; 虽然,有时它是不可能的,特别是如果使用JNI,例如. (3认同)
  • 但是你*确实*使用了一个外部应用程序:`java`!您忘记了 Java 是一种语言规范,而不是程序。如果我使用其他一些 jvm 运行您的程序,例如 [kaffe](http://www.kaffe.org/),该怎么办?无论如何更新了我的答案:-) (3认同)
  • 您的答案是不正确的,因为即使没有 Meinersbur 和我自己的答案所示的外部应用程序/守护程序,这也是完全可能的。出于自我更新的目的,重新启动应用程序是一个很好的解决方案,因此实际上也需要重新启动应用程序。 (2认同)
  • @洛伦佐,这是正确的。这里最初的问题基本上是应用程序是否可以_重新启动自身_。答案基本上是否定的,您需要其他人(例如操作系统、包装脚本或类似的东西)的合作来生成新实例。 (2认同)

mae*_*ics 8

严格来说,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)
Run Code Online (Sandbox Code Playgroud)

AWT应用程序应该在"正常"终止时使用RESTART_CODE之外的其他东西退出JVM,这不需要重新启动.


wha*_*ick 7

Eclipse通常在安装插件后重新启动.他们使用包装器eclipse.exe(启动器应用程序)为Windows执行此操作.这个应用程序执行核心eclipse运行程序jar,如果eclipse java应用程序以重新启动代码终止,eclipse.exe将重新启动工作台.您可以构建类似的本机代码,shell脚本或其他Java代码包装器来实现重启.


Amr*_*tfy 6

视窗

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);
}
Run Code Online (Sandbox Code Playgroud)

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

^&完成后退出以关闭 cmd 窗口

一个示例 cmd 脚本可能是

@echo off
rem add some sleep (e.g. 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
Run Code Online (Sandbox Code Playgroud)

sleep 10 sleep 10 秒


Mal*_*alt 5

虽然这个问题很老而且已经回答了,但我偶然发现了一些解决方案的问题,并决定将我的建议添加到组合中。

一些解决方案的问题在于它们构建了单个命令字符串。当某些参数包含空格时,这会产生问题,尤其是java.home

例如,在 Windows 上,该行

final String javaBin = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java";
Run Code Online (Sandbox Code Playgroud)

可能会返回这样的东西:C:\Program Files\Java\jre7\bin\java

由于Program Files. 不是一个大问题,但有点烦人且容易出错,尤其是在跨平台应用程序中。

因此,我的解决方案将命令构建为命令数组

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();
        }
    }
Run Code Online (Sandbox Code Playgroud)


Chr*_*jer 5

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

如果procfs /proc/self/cmdline可用

如果您在提供procfs的环境中运行并因此具有/proc可用的文件系统(这意味着这不是可移植的解决方案),您可以读取 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();
    }
}
Run Code Online (Sandbox Code Playgroud)

/proc/self/cmdline可用的系统上,这可能是如何从 Java“重新启动”当前 Java 进程的最优雅的方式。不涉及 JNI,也不需要猜测路径和所需的东西。这还将处理传递给java二进制文件的所有 JVM 选项。命令行将与当前 JVM 进程之一完全相同。

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

不要忘记使用 ProcessBuilder.inheritIO()

缺省值是stdin/ stdout/ stderr(在Java中称为System.in/ System.out/ System.err)启动的过程被设置为,其允许当前运行的进程与新启动的过程进行通信。如果您想重新启动当前进程,这很可能不是您想要的。相反,你想的是stdin/ stdout/stderr是相同当前VM的。这称为继承。您可以通过调用inheritIO()您的ProcessBuilder实例来实现。

Windows 上的陷阱

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