如何防止调用System.exit()终止JVM?

34

我几乎可以确定这是不可能的,但值得一试。

我正在为某个工具编写命令行界面。我在谈论一个调用另一个Java应用程序的Java应用程序。该工具在执行后调用System.exit,从而终止了我的执行环境。我不希望出现这种情况。

有没有办法忽略System.exit调用?


3
可能是Java: How to test methods that call System.exit()?的重复问题。尽管那个问题的标题可能会给人带来误导,但实际上它是同一个问题:防止外部代码执行System.exit()。 - R. Martinho Fernandes
5个回答

27

可以使用SecurityManager来实现这个功能。尝试以下操作:

class MySecurityManager extends SecurityManager {
  @Override public void checkExit(int status) {
    throw new SecurityException();
  }

  @Override public void checkPermission(Permission perm) {
      // Allow other activities by default
  }
}

在你的类中使用以下调用:

myMethod() {
    //Before running the external Command
    MySecurityManager secManager = new MySecurityManager();
    System.setSecurityManager(secManager);

    try {
       invokeExternal();
    } catch (SecurityException e) {
       //Do something if the external code used System.exit()
    }
}

3
我建议还要覆盖SecurityManager的checkPermission方法,以避免与其他特权操作相关的安全异常。 - Emmanuel Bourg
@theomega 我使用了你的代码,但现在我想读取的每个文件都显示访问被拒绝。 - Elad Benda
1
@EmmanuelBourg 我使用了答案中的代码,但现在我想读取的每个文件都出现了“访问被拒绝”的错误。 - Elad Benda
可能通常正确的解决方案是获取先前的安全管理器,如果存在,则将所有其他方法委托给它。 - Ondřej Fischer
类似 system-lambda https://github.com/stefanbirkner/system-lambda 这样的库会负责将安全管理器调用委托给先前安装的安全管理器(如果有)。此外,setSecurityManager() 在 Java 17 中已被弃用,因此这种方法在未来将无法使用。 - Fr Jeremy Krieg

3
你可以将应用程序分为两部分。第一部分由工具启动。然后,您将应用程序的第二部分作为新进程启动。然后,主机应用程序会终止您的第一部分,但第二部分仍在运行。
这样,您的应用程序的第一部分只是第二部分的启动程序,而第二部分才是您真正的应用程序。

3

Java 17更新:

之前回答这个问题的很多人都提到了使用自定义的SecurityManager。在当时这是正确的做法。但是从Java 17开始,System.setSecurityManager()已被弃用并将来会被移除,所以这种方法在未来将无法使用。


4
是的,故事在这里:https://openjdk.java.net/jeps/411 这个JEP中涉及的新API,旨在解决System::exit未定义的问题... https://bugs.openjdk.java.net/browse/JDK-8199704 也许在其他地方。 - pleutre

2

SecurityManager设置为忽略System.exit(),除非它来自于您的代码。


1

关于这个(@Vonc的答案),你应该使用安全管理器:

尝试修改TestCase以运行具有防止调用System.exit的安全管理器,然后捕获SecurityException。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接