java 在运行时更新 JAR

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6477318/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 16:02:09  来源:igfitidea点击:

Updating a JAR whilst running

javajvmclasspathclassloader

提问by James Moore

Given a jar runs within a JVM would it be possible to unload the current running Jar and remove it from the system. Download a new version and rename it with the same name of the last Jar and then initialise the new Jar, creating a seamless update of the Jar within the JVM. Is it even possible to instruct the JVM to perform this action? Is it even possible to update a Jar whilst its running?

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

回答by Perception

Download a new version and rename it with the same name of the last Jar and then initialise the new Jar, creating a seamless update of the Jar within the JVM ... Is it even possible to update a Jar whilst its running?

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

The JAR file is not 'running', the JVM is running. Your JAR file just contains the class information (aka byte code instructions) that make the JVM do useful work. In most cases the JVM will actually not put a system lock on your JAR file, so you can replace that file to your hearts content.

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

The real problem of course is that once the JVM loads your JAR it will carry along happily with what it loaded and never read from your JAR file again, no matter how many times you overwrite it. This is the behavior of the default class loader and cannot be changed - however as others have pointed out - you do NOT have to use the default class loader. You can implement your own, similar to what Web Application Servers use, in order to load updated JARS from the filesystem. Caveat though - defining your own classloader is considered a 'Bad Idea?' unless you really know what your doing. You can read more hereand here.

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

回答by toomasr

This is something that I've seen done many times before (and also done myself). I composed some points of the problems/solutions that might arise.

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

  • JVM will crash with a dump if you overwrite a JAR file that it will use later on.
    • By later I mean classes are loaded quite lazily and some might only be loaded later in your program's life
    • JVM has an open handle for the JAR file and the lib will fail as the JAR and pointers become wrong
    • The probability can be decreased by pre-loading all classes and resources from the JAR file
    • If you have a custom classloader then you can close the handles yourself.
  • You will need to know about how class loading is done. Better yet be in control.
    • A custom classloader that will create a classloaders per JAR and manage the versioning
    • Know how your application uses classloaders and how it acts on new JARs (for example check what Tomcat does when you overwrite a WAR archive)
  • On Windows your JAR files will be locked and you cannot overwrite them. If you are in control then you can unlock them after use (close them). For 3rd party systems you have to find the respective flags. For example you can check the antiJARLockingin Tomcat context configuration.
  • Always better to avoid overwriting the same file and rather have some versioning going on
  • 如果覆盖稍后将使用的 JAR 文件,JVM 将因转储而崩溃。
    • 稍后我的意思是类的加载非常缓慢,有些可能只会在程序生命周期的后期加载
    • JVM 有一个 JAR 文件的打开句柄,当 JAR 和指针出错时,lib 将失败
    • 可以通过从 JAR 文件中预加载所有类和资源来降低概率
    • 如果您有自定义类加载器,则可以自己关闭句柄。
  • 您将需要了解类加载是如何完成的。最好还是控制一下。
    • 一个自定义类加载器,它将为每个 JAR 创建一个类加载器并管理版本控制
    • 了解您的应用程序如何使用类加载器以及它如何作用于新的 JAR(例如,检查覆盖 WAR 存档时 Tomcat 的行为)
  • 在 Windows 上,您的 JAR 文件将被锁定,您无法覆盖它们。如果您处于控制之中,那么您可以在使用后解锁它们(关闭它们)。对于第 3 方系统,您必须找到相应的标志。例如,您可以在 Tomcat 上下文配置中检查antiJARLocking
  • 最好避免覆盖同一个文件,而是进行一些版本控制

All in all there are many issues that you might run into when you want to achieve JAR reloading. Luckily there are ways how to minimise the risks. The safest way is to do something similar to get the same effect. Cleanest is custom classloaders and JAR file versioning.

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

回答by Thorbj?rn Ravn Andersen

Generally you cannot do this as this behaviour to my knowledge is not officially defined.

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

You CAN however create a classloader using a jar file outsideyour official classpath and then load classes from that as you need. By discarding all instances of classes loaded by the classloader you can remove the current resources and then instantiate a new classloader on the new jar file and then load the new classes and create new objects.

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

This is quite complicated so perhaps you would instead make the jar an OSGi module and invoke your program through an OSGi-loader?

这很复杂,所以也许您会将 jar 变成一个 OSGi 模块并通过 OSGi 加载器调用您的程序?

回答by Snicolas

You can't write to a jar that is running. There is no equivalent to getResourceInputStream for writing. I guess that if you try to write using an FileOutputStream, as JVM uses it, you won't be able to delete it as System will prevent it.

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

Anyhow, it's still possible to offer updates of different modules in different jars. So you could imagine having an application's main jar file that could be updated through a small independant runnable jar file containing the updater.

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

It's also possible to use JNLP for automatic and seamless updates of an application.

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

Server side applications are also an alternative to hide updates to user.

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

Regards, Stéphane

问候, 斯蒂芬

回答by Nick

The answer lies in Java Class Loaders. These guys load classes from JARS, or .class files, or a byte[]value or a URL or anything else. Whenever you access a class, you are implicitly using a class loader to give you the right instance of a class.

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

Create a class loader of your choice and just switch the class loader when you need a "refresh" of your classes. Take a look at the Thread.setContextClassLoadermethod -- this will change a Thread's classloader.

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

Defining your own Class Loader is very straighforward -- just subclass the ClassLoaderclass and override its findClass method.

定义您自己的类加载器非常简单——只需对ClassLoader类进行子类化并覆盖其findClass 方法即可

回答by Piyush Katariya

You can use HotswapAgent to achieve it. It supports plugins for a few widely used frameworks and also facilitates writing new custom plugins

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

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

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

回答by VocoJax

In my software suite, which is a complex mesh of modular clients linked to a central server taking long-term timelapse photography, I have added the ability to update the software.

在我的软件套件中,这是一个复杂的模块化客户端网格,链接到中央服务器进行长期延时摄影,我添加了更新软件的功能。

byte[] file = recieve();

FileOutputStream fos = new FileOutputStream("software.jar");
fos.flush(); fos.write(file); fos.close();

After this process is completed, there are a series of steps the client takes before it reboots. Of these processes, they include long periods of thread sleeping, file read and writing, and networking interactions.

此过程完成后,客户端在重新启动之前需要执行一系列步骤。在这些进程中,它们包括长时间的线程休眠、文件读写以及网络交互。

I have not pinpointed what may or may not be the error, however, in some cases, I have observed a crash with an hs_err_pid.log.

我还没有指出可能是也可能不是错误的原因,但是,在某些情况下,我观​​察到了 hs_err_pid.log 的崩溃。

I also have a variable, final and static, called "SOFTWARE_VERSION". I have confirmed that this variable does update (when observing from the server interface) without a reboot of the software after I replace it.

我还有一个变量,final 和 static,称为“SOFTWARE_VERSION”。我已经确认这个变量会更新(从服务器界面观察时),在我替换它后没有重新启动软件。

After some careful consideration, however, I've decided to immediately reboot the machine after a software update (this program will execute on startup). Since the integrity of an update has been observed to not be reliable, I found it best to give it a fresh start. It's possible (not tested) to run an action like this:

然而,经过仔细考虑,我决定在软件更新后立即重新启动机器(该程序将在启动时执行)。由于观察到更新的完整性不可靠,我发现最好重新开始。可以(未测试)运行这样的操作:

Runtime.getRuntime().exec("sudo java -jar software.jar");
System.exit(0);

However, I don't know how reliable that would be. You could also try to run something like:

但是,我不知道这有多可靠。你也可以尝试运行类似的东西:

Runtime.getRuntime().exec("run_software.sh")
System.exit(0);

Then in run_software.sh:

然后在 run_software.sh 中:

sleep 1000
sudo java -jar software.jar

I would be interested to know if that would work.

我很想知道这是否可行。

Hope this helps!

希望这可以帮助!