在运行时更新JAR

如果jar在JVM中运行,则可以卸载当前运行的Jar并将其从系统中删除。 下载新版本并使用与最后一个Jar相同的名称重命名它,然后初始化新的Jar,在JVM中创建Jar的无缝更新。 甚至可以指示JVM执行此操作吗? 甚至可以在运行时更新Jar吗?

下载一个新版本并使用与最后一个Jar相同的名称重命名它,然后初始化新的Jar,在JVM中创建Jar的无缝更新……甚至可以在运行时更新Jar吗?

JAR文件没有“运行”,JVM正在运行。 您的JAR文件只包含使JVM执行有用工作的类信息(也称为字节代码指令)。 在大多数情况下,JVM实际上不会对您的JAR文件进行系统锁定,因此您可以将该文件替换为您的内容。

真正的问题当然是,一旦JVM加载你的JAR,无论你多少次覆盖它,它都会愉快地携带它加载的内容并且永远不会再次从你的JAR文件中读取。 这是默认类加载器的行为,无法更改 – 但正如其他人指出的那样 – 您不必使用默认的类加载器。 您可以实现自己的,类似于Web应用程序服务器使用的,以便从文件系统加载更新的JARS。 但要注意的是 – 定义自己的类加载器被认为是’坏主意’,除非你真的知道你在做什么。 你可以在这里和这里阅读更多。

这是我以前见过的很多次(也是我自己做过的)。 我列出了可能出现的问题/解决方案的一些要点。

  • 如果覆盖稍后将使用的JAR文件,JVM将与转储一起崩溃。
    • 到了后来我的意思是说,类的加载非常懒散,有些可能只会在程序的生命后期加载
    • JVM具有JAR文件的打开句柄,并且当JAR和指针出错时,lib将失败
    • 可以通过从JAR文件预加载所有类和资源来降低概率
    • 如果您有自定义类加载器,那么您可以自己关闭句柄。
  • 您需要了解如何完成类加载。 更好地掌控。
    • 一个自定义类加载器,它将为每个JAR创建一个类加载器并管理版本控制
    • 了解应用程序如何使用类加载器以及它如何对新JAR起作用(例如,检查Tomcat在覆盖WAR归档时的作用)
  • 在Windows上,您的JAR文件将被锁定,您无法覆盖它们。 如果您处于控制之中,那么您可以在使用后解锁它们(关闭它们)。 对于第三方系统,您必须找到相应的标志。 例如,您可以在Tomcat上下文配置中检查antiJARLocking
  • 总是更好地避免覆盖相同的文件,而是进行一些版本控制

总而言之,当您想要实现JAR重新加载时,可能会遇到许多问题。 幸运的是,如何最大限度地降低风险。 最安全的方法是做类似的事情以获得相同的效果。 Cleanest是自定义类加载器和JAR文件版本控制。

一般情况下你不能这样做,因为据我所知这种行为没有正式定义。

但是,您可以使用官方类路径之外的jar文件创建一个类加载器,然后根据需要从中加载类。 通过丢弃由类加载器加载的所有类实例,您可以删除当前资源,然后在新的jar文件上实例化新的类加载器,然后加载新类并创建新对象。

这是非常复杂的,所以你可能会改为将jar作为OSGi模块并通过OSGi-loader调用你的程序?

你不能写一个正在运行的jar。 写入没有与getResourceInputStream等效的东西。 我想如果你尝试使用FileOutputStream编写,当JVM使用它时,你将无法删除它,因为系统会阻止它。

无论如何,它仍然可以在不同的jar子里提供不同模块的更新。 所以你可以想象有一个应用程序的主jar文件可以通过一个包含更新程序的小的独立可运行jar文件进行更新。

也可以使用JNLP自动和无缝地更新应用程序。

服务器端应用程序也是隐藏用户更新的替代方法。

此致,Stéphane

答案在于Java类加载器。 这些人从JARS或.class文件,或byte[]值或URL或其他任何东西加载类。 无论何时访问类,您都隐式使用类加载器为您提供正确的类实例。

创建一个您选择的类加载器,只需在需要“刷新”类时切换类加载器。 看一下Thread.setContextClassLoader方法 – 这将改变Thread的类加载器。

定义自己的类加载器非常简单 – 只需inheritanceClassLoader类并覆盖其findClass方法 。

您可以使用HotswapAgent来实现它。 它支持一些广泛使用的框架的插件,也便于编写新的自定义插件

HotswapAgent – https://github.com/HotswapProjects/HotswapAgent