97

我想实现一个 java 应用程序(服务器应用程序),它可以从给定的 url 下载新版本(.jar 文件),然后在运行时自行更新。

最好的方法是什么,这可能吗?

我猜该应用程序可以下载一个新的 .jar 文件并启动它。但是我应该如何进行切换,例如知道新应用程序何时启动然后退出。还是有更好的方法来做到这一点?

4

9 回答 9

74

解决方案的基本结构如下:

  • 有一个主循环负责重复加载应用程序的最新版本(如果需要)并启动它。

  • 应用程序会做它的事情,但会定期检查下载 URL。如果它检测到新版本,它会退出到启动器。

有多种方法可以实现这一点。例如:

  • 启动器可以是一个包装脚本或二进制应用程序,它启动一个新的 JVM 以从被替换的 JAR 文件运行应用程序。

  • 启动器可以是一个 Java 应用程序,它为新 JAR 创建一个类加载器,加载一个入口点类并在其上调用一些方法。如果你这样做,你必须注意类加载器存储泄漏,但这并不难。(您只需要确保在重新启动后无法访问具有从 JAR 加载的类的对象。)

外部包装方法的优点是:

  • 你只需要一个 JAR,
  • 您可以替换整个 Java 应用程序,
  • 应用程序创建的任何辅助线程等将在没有特殊关闭逻辑的情况下消失,并且
  • 您还可以处理从应用程序崩溃等中恢复。

第二种方法需要两个 JAR,但具有以下优点:

  • 该解决方案是纯 Java 且可移植的,
  • 转换会更快,并且
  • 您可以更轻松地在重新启动期间保留状态(模泄漏问题)。

“最佳”方式取决于您的具体要求。

还应该注意的是:

  • 自动更新存在安全风险。一般来说,如果提供更新的服务器受到攻击,或者提供更新的机制容易受到攻击,那么自动更新可能会导致客户端受到攻击。

  • 向客户推送对客户造成损害的更新可能会带来法律风险,并对您的企业声誉造成风险。


如果你能找到一种方法来避免重新发明轮子,那就太好了。有关建议,请参阅其他答案。

于 2010-10-23T06:01:46.087 回答
45

我目前正在开发一个 JAVA Linux 守护进程,并且还需要实现自动更新机制。我想将我的应用程序限制为一个 jar 文件,并提出了一个简单的解决方案:

将更新程序应用程序打包到更新本身中。

应用程序:当应用程序检测到较新版本时,它会执行以下操作:

  1. 下载更新(Zipfile)
  2. 提取 Application和 ApplicationUpdater(都在 zipfile 中)
  3. 运行更新程序

ApplicationUpdater:当更新程序运行时,它会执行以下操作:

  1. 停止应用程序(在我的例子中是一个通过 init.d 的守护进程)
  2. 复制下载的jar文件覆盖当前应用
  3. 启动应用程序
  4. 清理。

希望它可以帮助某人。

于 2012-11-12T10:51:56.377 回答
16

我最近创建了update4j,它与 Java 9 的模块系统完全兼容。

它将无缝启动新版本而无需重新启动。

于 2018-03-12T19:05:23.200 回答
14

这是一个已知问题,我建议不要重新发明轮子——不要编写自己的 hack,只需使用其他人已经做过的。

您需要考虑两种情况:

  1. 应用程序需要是可自我更新的,并且即使在更新期间也能保持运行(服务器应用程序、嵌入式应用程序)。使用 OSGi:BundlesEquinox p2

  2. 应用程序是一个桌面应用程序,并有一个安装程序。有许多带有更新选项的安装程序。检查安装程序列表

于 2010-10-26T07:53:49.150 回答
11

受 jEdit 中类似机制的启发,我编写了一个 Java 应用程序,它可以在运行时加载插件并立即开始使用它们。jEdit 是开源的,因此您可以选择查看它是如何工作的。

该解决方案使用自定义 ClassLoader 从 jar 加载文件。一旦它们被加载,你就可以从新的 jar 中调用一些方法来作为它的main方法。然后棘手的部分是确保您摆脱对旧代码的所有引用,以便它可以被垃圾收集。我不是这方面的专家,我已经做到了,但这并不容易。

于 2010-10-23T04:34:30.970 回答
6
  1. 第一种方式:使用tomcat及其部署工具。
  2. 第二种方式:将应用程序拆分为两部分(功能和更新),让更新部分替换功能部分。
  3. 第三种方式:在你的服务器应用中只需下载新版本,然后旧版本释放绑定端口,然后旧版本运行新版本(启动进程),然后旧版本向新版本发送应用端口请求删除旧版本,旧版本终止,新版本删除旧版本。像这样: 替代文字
于 2010-10-23T18:21:56.860 回答
2

这不一定是最好的方法,但它可能对你有用。

您可以编写一个引导应用程序(如果您玩过《魔兽世界》,那是魔兽世界的启动器)。该引导程序负责检查更新。

  • 如果有可用更新,它将提供给用户,处理下载、安装等。
  • 如果应用程序是最新的,它将允许用户启动应用程序
  • 或者,您可以允许用户启动应用程序,即使它不是最新的

这样您就不必担心强制退出您的应用程序。

如果您的应用程序是基于 Web 的,并且如果他们拥有最新的客户端很重要,那么您还可以在应用程序运行时进行版本检查。您可以在与服务器(部分或全部调用)进行正常通信时,或两者同时执行这些操作。

对于我最近开发的产品,我们在启动时(没有引导程序应用程序,但在主窗口出现之前)以及调用服务器期间进行了版本检查。当客户端过期时,我们依靠用户手动退出,但禁止对服务器进行任何操作。

请注意,在您打开主窗口之前,我不知道 Java 是否可以调用 UI 代码。我们使用的是 C#/WPF。

于 2010-10-23T04:23:39.160 回答
2

如果您使用Equinox插件构建应用程序,则可以使用P2 Provisioning System来获得针对此问题的现成解决方案。这将要求服务器在更新后自行重启。

于 2010-10-26T07:38:57.433 回答
1

我在下载新 jar(等)时发现了一个安全问题,例如,中间人攻击。您始终必须签署可下载的更新。

在 JAX2015 上,Adam Bien 讲述了使用 JGit 更新二进制文件。可悲的是我找不到任何教程。

来源为德语。

Adam Bien 创建了更新程序,请参见此处

在这里用一些 javaFX 前端分叉了它。我也在研究自动签名。

于 2015-09-17T08:15:22.057 回答