Java 运行时更新JAR

Java 运行时更新JAR,java,jvm,classpath,classloader,Java,Jvm,Classpath,Classloader,如果jar在JVM中运行,则可以卸载当前运行的jar并将其从系统中删除。下载一个新版本并用上一个Jar的相同名称重命名它,然后初始化新Jar,在JVM中创建Jar的无缝更新。甚至可以指示JVM执行此操作吗?甚至可以在Jar运行时更新它吗?通常您不能这样做,因为据我所知,这种行为还没有正式定义 但是,您可以在正式类路径之外使用jar文件创建一个类加载器,然后根据需要从中加载类。通过丢弃类加载器加载的所有类实例,您可以删除当前资源,然后在新jar文件上实例化一个新的类加载器,然后加载新类并创建新对象

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

通常您不能这样做,因为据我所知,这种行为还没有正式定义

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


这是相当复杂的,所以您可能会改为将jar作为OSGi模块,并通过OSGi加载程序调用您的程序

无法写入正在运行的jar。没有与getResourceInputStream等效的文件可供编写。我猜,若您尝试使用FileOutputStream进行编写,就像JVM使用它一样,您将无法删除它,因为系统会阻止它

无论如何,仍然可以在不同的jar中提供不同模块的更新。因此,您可以想象拥有一个应用程序的主jar文件,该文件可以通过一个包含更新程序的小型独立可运行jar文件进行更新

还可以使用JNLP自动无缝地更新应用程序

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

问候,,
Stéphane

答案在于Java类加载器。这些家伙从JAR、或.class文件、或
byte[]
值、URL或任何其他文件加载类。每当您访问一个类时,您都隐式地使用类加载器为您提供正确的类实例

创建您选择的类加载器,当您需要“刷新”类时,只需切换类加载器。看看这个方法——这将改变线程的类加载器

定义自己的类加载器非常困难——只需对
ClassLoader
类进行子类化,并重写它的类

下载新版本并重命名它 与最后一个罐子同名,并且 然后初始化新的Jar,创建 内部Jar的无缝更新 JVM。。。甚至有可能 在Jar运行时更新它

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


当然,真正的问题是,一旦JVM加载了您的JAR,它将愉快地执行加载的内容,并且不再从您的JAR文件中读取内容,无论您覆盖它多少次。这是默认类加载器的行为,不能更改-但是正如其他人所指出的-您不必使用默认类加载器。您可以实现自己的,类似于Web应用程序服务器使用的,以便从文件系统加载更新的JAR。不过需要注意的是,定义自己的类加载器被认为是一个“坏主意”™' 除非你真的知道自己在做什么。你可以阅读更多的内容。

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

  • 如果您覆盖稍后将使用的JAR文件,JVM将因转储而崩溃。
    • 我指的是类的加载非常缓慢,有些可能只是在程序生命的后期加载
    • JVM有一个JAR文件的开放句柄,当JAR和指针出错时,库将失败
    • 通过预加载JAR文件中的所有类和资源,可以降低这种可能性
    • 如果您有一个自定义类加载器,那么您可以自己关闭句柄
  • 您需要了解类加载是如何完成的。最好还是控制住局面。
    • 一个自定义类加载器,它将为每个JAR创建一个类加载器并管理版本控制
    • 了解您的应用程序如何使用类加载器以及它如何作用于新JAR(例如,检查Tomcat在覆盖WAR归档文件时所做的工作)
  • 在Windows上,您的JAR文件将被锁定,您无法覆盖它们。如果您处于控制状态,则可以在使用后解锁(关闭)。对于第三方系统,您必须找到相应的标志。例如,您可以在Tomcat上下文配置中检查防jarlocking
  • 最好避免覆盖同一个文件,而是进行一些版本控制

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

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


HotswapAgent-

在我的软件套件中,我添加了更新软件的功能,该套件是一个复杂的模块化客户端网络,连接到一个中央服务器,用于长期拍摄timelapse照片

byte[] file = recieve();

FileOutputStream fos = new FileOutputStream("software.jar");
fos.flush(); fos.write(file); fos.close();
此过程完成后,客户端将在重新启动之前执行一系列步骤。在这些进程中,它们包括长时间的线程休眠、文件读写和网络交互

我没有指出错误的具体原因,但是,在某些情况下,我观察到hs_err_pid.log崩溃

我还有一个变量,最终的和静态的,叫做“软件版本”。我已确认此变量确实会更新(当obser
Runtime.getRuntime().exec("sudo java -jar software.jar");
System.exit(0);
Runtime.getRuntime().exec("run_software.sh")
System.exit(0);
sleep 1000
sudo java -jar software.jar
    public static void main(String[] args) throws InterruptedException {
        long startTime = System.currentTimeMillis();
        while (true) {
            long now = System.currentTimeMillis();
            long delta = (now - startTime);
            System.out.println("Running... delta is " + delta);
            if (TimeUnit.MILLISECONDS.toSeconds(delta) > 30) {
                Me1 m1 = new M1();
                me1.method1(); // ###
            }
            Thread.sleep(1000);
        }
    }
    Exception in thread "main" java.lang.NoClassDefFoundError: Me1
    at Me.main(Me.java:16)
    Caused by: java.lang.ClassNotFoundException: Me1
    at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:335)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    ... 1 more