如果jar在JVM中运行,则可以卸载当前运行的Jar并将其从系统中删除。下载一个新版本并使用与最后一个Jar相同的名称重命名它,然后初始化新的Jar,在JVM中创建Jar的无缝更新。甚至可以指示JVM执行此操作吗?甚至可以在运行时更新Jar吗?
答案 0 :(得分:24)
下载新版本并重命名 与最后一个Jar和同名的 然后初始化新的Jar,创建 Jar内的无缝更新 JVM ......甚至可能 在运行时更新Jar?
JAR文件未“正在运行”,JVM正在运行。您的JAR文件只包含使JVM执行有用工作的类信息(也称为字节代码指令)。在大多数情况下,JVM实际上不会对您的JAR文件进行系统锁定,因此您可以将该文件替换为您的内容。
真正的问题当然是,一旦JVM加载你的JAR,无论你多少次覆盖它,它都会愉快地携带它加载的内容并且永远不会再次从你的JAR文件中读取。这是默认类加载器的行为,无法更改 - 但正如其他人指出的那样 - 您不必使用默认的类加载器。您可以实现自己的,类似于Web应用程序服务器使用的,以便从文件系统加载更新的JARS。但要注意的是 - 定义自己的类加载器被认为是'坏主意',除非你真的知道你在做什么。您可以阅读更多here和here。
答案 1 :(得分:21)
这是我以前见过的很多次(也是我自己做过的)。我列出了可能出现的问题/解决方案的一些要点。
总而言之,当您想要实现JAR重新加载时,可能会遇到许多问题。幸运的是,如何最大限度地降低风险。最安全的方法是做类似的事情以获得相同的效果。 Cleanest是自定义类加载器和JAR文件版本。
答案 2 :(得分:1)
一般情况下,你不能这样做,因为据我所知,这种行为尚未正式定义。
但是,您可以使用正式类路径中的jar文件 创建一个类加载器,然后根据需要从中加载类。通过丢弃由类加载器加载的所有类的实例,您可以删除当前资源,然后在新的jar文件上实例化一个新的类加载器,然后加载新类并创建新对象。
这很复杂,所以你可能会改为将jar作为OSGi模块并通过OSGi-loader调用你的程序?
答案 3 :(得分:1)
您无法写入正在运行的jar。用于写入的getResourceInputStream没有等价物。我想如果你尝试使用FileOutputStream编写,当JVM使用它时,你将无法删除它,因为系统会阻止它。
无论如何,仍然可以在不同的罐子里提供不同模块的更新。所以你可以想象有一个应用程序的主jar文件可以通过一个包含更新程序的小的独立可运行jar文件进行更新。
还可以使用JNLP自动更新应用程序。
服务器端应用程序也是隐藏用户更新的替代方法。
此致 斯特凡
答案 4 :(得分:1)
答案在于Java类加载器。这些人从JARS,.class文件或byte[]
值或URL或其他任何东西加载类。无论何时访问类,都隐式使用类加载器为您提供正确的类实例。
创建一个您选择的类加载器,只需在需要“刷新”类时切换类加载器。看看Thread.setContextClassLoader方法 - 这将改变Thread的类加载器。
定义自己的类加载器非常简单 - 只需继承ClassLoader
类并覆盖其findClass method。
答案 5 :(得分:1)
请注意汤姆哈伯德的评论。 如果某个类尚未在 Runtime 中使用(因此未加载),如果您在新 JAR 中删除该类 - 您会遇到一些问题。
例如,如果您将执行以下代码:
System.Text.Json
如果您将上述内容放入 Jar 并执行它,并且在 30 秒过去之前,您将更改 Jar,使得类“Me1”根本不包含“method1”(当然您将删除标有“###”的行),执行 30 秒后会出现异常:
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);
}
}
答案 6 :(得分:0)
您可以使用HotswapAgent实现它。它支持一些广泛使用的框架的插件,也有助于编写新的自定义插件
HotswapAgent-https://github.com/HotswapProjects/HotswapAgent
答案 7 :(得分:0)
在我的软件套件中,它是一个复杂的模块化客户端网格,链接到一个进行长时间间隔摄影的中央服务器,它添加了更新软件的功能。
byte[] file = recieve();
FileOutputStream fos = new FileOutputStream("software.jar");
fos.flush(); fos.write(file); fos.close();
此过程完成后,客户端需要执行一系列步骤才能重新启动。在这些过程中,它们包括长时间的线程休眠,文件读取和写入以及网络交互。
我没有查明可能是或不是什么错误,但是,在某些情况下,我发现hs_err_pid.log导致崩溃。
我还有一个变量final和static,称为“ SOFTWARE_VERSION”。我确认该变量确实可以更新(从服务器界面观察时),而在更换软件后无需重新启动软件。
但是,经过慎重考虑,我决定在软件更新后立即重新启动计算机(此程序将在启动时执行)。由于观察到更新的完整性不可靠,因此我发现最好重新开始。可能(未经测试)执行以下操作:
Runtime.getRuntime().exec("sudo java -jar software.jar");
System.exit(0);
但是,我不知道那将有多可靠。您还可以尝试运行以下内容:
Runtime.getRuntime().exec("run_software.sh")
System.exit(0);
然后在run_software.sh中:
sleep 1000
sudo java -jar software.jar
我想知道是否行得通。
希望这会有所帮助!
答案 8 :(得分:0)
正如人们所提到的,JVM 进程将打开 jar 文件的文件句柄,直到进程生命周期结束。如果 Linux 是您的目标操作系统,那么您可以执行以下技巧。
unlink(2)
或等效方法删除原始 JAR 文件。路径将立即从文件系统元数据中删除,但物理文件数据将保留直到最后一个文件句柄关闭,因此不会发生损坏。然后更新类的唯一方法是 - 即使使用重新定义的类加载器 - 强制 JVM 首先卸载类。这里的问题是 JVM 只会在垃圾收集类加载器时这样做。使用标准实现,几乎无法控制该过程。