【问题标题】:Java - How to replace a running jar fileJava - 如何替换正在运行的 jar 文件
【发布时间】:2019-02-13 14:13:49
【问题描述】:

我希望我的 java 应用程序能够自动保持最新状态,我已经编写了所有代码来下载最新的 jar 文件并将其放在指定的路径中,但是由于我的程序必须打开才能实际检查是否有可用的更新,然后更新它们它给了我这个错误:

Exception in thread "main" java.nio.file.FileSystemException: name.jar: The process cannot access the file because it is being used by another process

现在我的问题不是我为什么会收到此错误,因为原因很明显,问题是:我将如何成功地实际更新 .jar 文件,因为它必须打开才能实际下载更新?如果有其他选择,我宁愿不制作另一个 .jar 来充当独立更新程序。

我用来测试的代码示例:

    URL url = new URL("<working url to the .jar file>");
    InputStream in = url.openStream();
    Files.copy(in, Paths.get("app.jar"), StandardCopyOption.REPLACE_EXISTING);
    in.close();

【问题讨论】:

  • 这种技术被称为热交换 - 在DZone上有一个有用的当前解决方案概要
  • 这似乎是由stackoverflow.com/questions/60764/…回答的。
  • 虽然我不想在运行时添加一个 jar,但我正在寻找一种解决方案,在它运行时用新的 jar 覆盖原来的 jar。

标签: java


【解决方案1】:

首先:另一个答案是正确的,有办法静默更新/重启 JAR 文件。并下载新的 JAR,完全重启,这很好。

但问题是关于更新“正在使用”的 JAR,对此我有一个明确的非答案:你走错了兔子洞!

即使您以某种方式破解覆盖文件系统上的 JAR 文件同时一个(或多个!)已加载(或未加载)的 JVM该 JAR 中的类正在运行,结果不是您所期望的:替换 JAR 文件内容不会神奇地将所有类重新加载到正在运行的 JVM 中。类已经加载!在文件系统中更改它们的“加载来源”不会影响“运行”类。

如果有的话,您可以启用以下情况:

  • 类 A 是从 Jar X(版本 N)加载的
  • Jar X 已更新
  • 从 Jar X(版本 N+m)加载 B 类

但是,B 类希望 A 类看起来不同。突然之间,您在该 JVM 实例中发生了版本控制冲突。因为它从 X(版本 N)加载了一些类,从 X(版本 N+1 或 N+5,因为该客户跳过下载 2 周)加载了其他类。更糟糕的是:确切的错误场景可能完全取决于您的 JVM 到目前为止所看到的工作负载,以及更新后如何使用它。一位客户可能没有任何问题,而另一位客户可能会在 5 分钟内崩溃(但很可能在某个时候崩溃)。

长话短说:不要这样做。而是:

  • 要么告诉您的用户,当更新到来时他们必须重新启动应用程序
  • 或查看“真实”JVM hotswapping 机制。但老实说:这更像是一个调试功能,您可以在开发环境中执行此操作,例如使用 JRebel 之类的工具。您确实想在客户的生产环境中使用它。因为如前所述,运行时版本控制问题很糟糕

【讨论】:

    【解决方案2】:

    首先,在不重新启动的情况下更换正在运行的应用程序几乎是不可能的,但有一些技术可以流畅地过渡。这是一个流行的(简单的):

    • 您启动application.exe,它会检查版本并发现有新版本。
    • application.exe 启动 updater.exe 并自行关闭(或等待,直到下载更新版本,然后自行关闭。
    • updater.exe 替换文件并再次启动 application.exe

    因此,据我所知,这部分是主食(替换核心应用程序),没有某种硬编码内存黑客。

    如果您不需要更新实际的核心应用程序并且愿意花时间在您的应用程序中开发动态库/资产管理,您基本上可以卸载库或资产,从application.exe 更新它并然后在完成更新后重新加载它,而无需重新启动应用程序。

    这可能是您正在寻找的东西,因为如果您的 application.exe 只是一个加载器,并且应用程序的核心逻辑是其中一个库,那么您基本上可以替换应用程序的任何部分。您仍然需要“重新启动”应用程序的那一部分,但您可以在重新启动之前保存和恢复重要数据,然后在重新启动之后恢复它,以使过渡变得更快、更轻松。

    学习和实施很可能很耗时。

    Here's an answer 对答案的第二部分有所了解。

    PS:我到处都使用“.exe”作为参考。想象一下它是关于罐子的,同样的原则也适用。

    【讨论】:

    • 谢谢,我觉得我必须这样做,但我只是想先排除其他方式。
    • 是的。看看 Minecraft,它也有一个本地启动器来更新和启动 Java 版本。最好在启动时进行软件更新,而不是在任何情况下运行时进行更新:) 后者听起来像是等待发生的糟糕的用户体验。
    • @Gimby 这取决于应用程序,许多 MMO 实现游戏内下载或“游戏流式传输”,其中下载游戏核心(相比之下它很小)并在后台下载资产。激战 2 做同样的事情,但是当您尝试输入未下载/更新的地图时,它会优先考虑该地图的资产并告诉您该地图何时已下载/更新。这是我见过的对用户最友好的更新方法,但我敢肯定它花费了很多时间来设计。
    猜你喜欢
    • 2014-05-13
    • 2015-06-28
    • 1970-01-01
    • 2017-05-21
    • 2011-08-12
    • 2014-06-07
    • 1970-01-01
    • 2010-12-15
    • 2011-04-06
    相关资源
    最近更新 更多