在 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 的路径),我可以用这个。我的问题是,我能以更好的方式做到这一点吗?这有点难以维护并且很容易损坏,我也不知道我是否可以用它加载已经加载的 类。
以下是一些可能会损坏的内容:
Windows 可能会阻止您更新 JAR 文件,因为 Java 在 class 加载程序处于活动状态时锁定了 JAR 文件。
内存泄漏。每次您创建一个新的 class 加载程序时,您都可能会泄漏前一个加载程序。它所需要的只是让前一个加载程序加载的一个 class 的一个实例仍然可以访问,最终加载程序和它加载的所有 class 也可以访问。
可能会出现 "strange" 行为,因为您有两个或更多 class 同名。你没有从根本上破坏 运行time 类型系统(JVM 比那个更聪明)但是你发现类型转换等意外失败。
如果您的重新部署出现问题并且您需要完全重新启动,您将不再是“24 / 7”。如果您重新部署有错误的代码并且需要完全重新启动才能恢复,也是如此。
有时,您需要升级 OS、Java 安装、应用服务器安装...以及其他需要(至少)重启的东西您服务的 JVM。
如果您的服务器出现硬件故障,那么您的“24 / 7”服务将中断。
我建议您 运行 您的服务的两个或三个实例(例如)前面有一个 HA 代理。当你想升级时,你可以关闭一个实例,升级它,然后 "flip" HA 代理使升级后的实例成为 "primary"。然后重复直到升级所有实例。如果在升级最后一个实例之前出现问题,您可以选择返回旧版本。
(在集群应用服务器平台上使用 Web 容器实施此方法是另一种同样有效的方法。)
显然,它比这要复杂一些,但是 运行设置服务的多个实例是实现高可用性的正常方法。如果您负担不起 运行 多个实例,那么巧妙地避免服务器重启并不能完全满足“24 / 7”的要求。
我想做一个 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 的路径),我可以用这个。我的问题是,我能以更好的方式做到这一点吗?这有点难以维护并且很容易损坏,我也不知道我是否可以用它加载已经加载的 类。
以下是一些可能会损坏的内容:
Windows 可能会阻止您更新 JAR 文件,因为 Java 在 class 加载程序处于活动状态时锁定了 JAR 文件。
内存泄漏。每次您创建一个新的 class 加载程序时,您都可能会泄漏前一个加载程序。它所需要的只是让前一个加载程序加载的一个 class 的一个实例仍然可以访问,最终加载程序和它加载的所有 class 也可以访问。
可能会出现 "strange" 行为,因为您有两个或更多 class 同名。你没有从根本上破坏 运行time 类型系统(JVM 比那个更聪明)但是你发现类型转换等意外失败。
如果您的重新部署出现问题并且您需要完全重新启动,您将不再是“24 / 7”。如果您重新部署有错误的代码并且需要完全重新启动才能恢复,也是如此。
有时,您需要升级 OS、Java 安装、应用服务器安装...以及其他需要(至少)重启的东西您服务的 JVM。
如果您的服务器出现硬件故障,那么您的“24 / 7”服务将中断。
我建议您 运行 您的服务的两个或三个实例(例如)前面有一个 HA 代理。当你想升级时,你可以关闭一个实例,升级它,然后 "flip" HA 代理使升级后的实例成为 "primary"。然后重复直到升级所有实例。如果在升级最后一个实例之前出现问题,您可以选择返回旧版本。
(在集群应用服务器平台上使用 Web 容器实施此方法是另一种同样有效的方法。)
显然,它比这要复杂一些,但是 运行设置服务的多个实例是实现高可用性的正常方法。如果您负担不起 运行 多个实例,那么巧妙地避免服务器重启并不能完全满足“24 / 7”的要求。