在 Java 中加载 类 运行时

Loading classes runtime in Java

我想做一个 24/7 全天候运行的服务,无需重新启动即可加载新模块或已加载模块的更新版本。

我可以使用类似这样的 jar 文件加载 类:

@FunctionalInterface
public interface IWorker {
    void doStuff();
}
IWorker worker;
try {
    URL[] urls = { new URL("jar:file:" +  "C:\Users\...\out\artifacts\workers_jar\somethingworker.jar"  +"!/") };
    ClassLoader cl = new URLClassLoader(urls);
    Class cls = cl.loadClass("hu.test.worker.SomethingWorker");
    worker = (IWorker) cls.newInstance();
    worker.doStuff();
} catch (MalformedURLException e) {
    e.printStackTrace();
} catch (ClassNotFoundException e) {
    e.printStackTrace();
} catch (InstantiationException e) {
    e.printStackTrace();
} catch (IllegalAccessException e) {
    e.printStackTrace();
}

该软件已经使用数据库 table 对从模块加载的 类 进行某些配置,所以如果我在那里存储完整的类名(可能还有 jars 的路径),我可以用这个。我的问题是,我能以更好的方式做到这一点吗?这有点难以维护并且很容易损坏,我也不知道我是否可以用它加载已经加载的 类。

以下是一些可能会损坏的内容:

  1. Windows 可能会阻止您更新 JAR 文件,因为 Java 在 class 加载程序处于活动状态时锁定了 JAR 文件。

  2. 内存泄漏。每次您创建一个新的 class 加载程序时,您都可能会泄漏前一个加载程序。它所需要的只是让前一个加载程序加载的一个 class 的一个实例仍然可以访问,最终加载程序和它加载的所有 class 也可以访问。

  3. 可能会出现 "strange" 行为,因为您有两个或更多 class 同名。你没有从根本上破坏 运行time 类型系统(JVM 比那个更聪明)但是你发现类型转换等意外失败。

  4. 如果您的重新部署出现问题并且您需要完全重新启动,您将不再是“24 / 7”。如果您重新部署有错误的代码并且需要完全重新启动才能恢复,也是如此。

  5. 有时,您需要升级 OS、Java 安装、应用服务器安装...以及其他需要(至少)重启的东西您服务的 JVM。

  6. 如果您的服务器出现硬件故障,那么您的“24 / 7”服务将中断。

我建议您 运行 您的服务的两个或三个实例(例如)前面有一个 HA 代理。当你想升级时,你可以关闭一个实例,升级它,然后 "flip" HA 代理使升级后的实例成为 "primary"。然后重复直到升级所有实例。如果在升级最后一个实例之前出现问题,您可以选择返回旧版本。

(在集群应用服务器平台上使用 Web 容器实施此方法是另一种同样有效的方法。)

显然,它比这要复杂一些,但是 运行设置服务的多个实例是实现高可用性的正常方法。如果您负担不起 运行 多个实例,那么巧妙地避免服务器重启并不能完全满足“24 / 7”的要求。