java如何在JDK11+中运行时动态加载JAR文件?
我有一个应用程序,它使用以下解决方案在运行时动态加载jar文件:
File file = ...
URL url = file.toURI().toURL();
URLClassLoader classLoader = (URLClassLoader)ClassLoader.getSystemClassLoader();
Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
method.setAccessible(true);
method.invoke(classLoader, url);
这是使用How to load JAR files dynamically at Runtime?处的答案完成的
我现在想要一个工作于JDK11+的解决方案,它与我使用的原始解决方案相当。因此,无需使用第三方库/框架或加载/调用单个类,就可以通过编程实现
我尝试了以下方法:
- 创建了扩展UrlClassLoader的DynamicClassLoader:
public final class DynamicClassLoader extends URLClassLoader {
public DynamicClassLoader(URL[] urls, ClassLoader parent) {
super(urls, parent);
}
public DynamicClassLoader(String name, ClassLoader parent) {
super(name, new URL[0], parent);
}
public DynamicClassLoader(ClassLoader parent) {
this("classpath", parent);
}
public void addURL(URL url) {
super.addURL(url);
}
}
- 然后我用java启动我的应用程序。系统班加载程序标志:
java -Djava.system.class.loader=com.example.DynamicClassLoader
然后我有一个jar文件作为path对象,我用以下方法调用它:
public void loadJar(Path path) throws Exception {
URL url = path.toUri().toURL();
DynamicClassLoader classLoader = (DynamicClassLoader)ClassLoader.getSystemClassLoader();
Method method = DynamicClassLoader.class.getDeclaredMethod("addURL", URL.class);
method.setAccessible(true);
method.invoke(classLoader, url);
}
调用此方法时,我得到以下cast类异常:
class jdk.internal.loader.ClassLoaders$AppClassLoader cannot be cast to class com.example.classloader.DynamicClassLoader (jdk.internal.loader.ClassLoaders$AppClassLoader is
in module java.base of loader 'bootstrap'; com.example.classloader.DynamicClassLoader is in unnamed module of loader 'app')
我在OpenJDK11(build 11.0.10+9)上使用Spring Boot(2.4)
# 1 楼答案
更改正在运行的应用程序的
ClassLoader
是一个糟糕的主意。特别是当你试图改变系统时JVM在执行时延迟加载类,然后在第一次加载时缓存它们。干扰该过程可能会对应用程序的稳定性造成严重问题
使用反射来访问
URLClassLoader
的内部方法,就像你正在做的那样,只会导致你的应用程序绑定到特定的JDK版本,而你的代码恰好可以工作,但是当你升级到较新的JDK版本,甚至是来自不同供应商的JDK时,它不能保证继续工作尽管有很多方法可以做到这一点,但这绝非小事
例如,OSGi是一个将此功能添加到JVM的系统,但您必须更改应用程序以适应OSGi模型,这并不容易。它的主要吸引力之一就是,它允许您安装捆绑包(带有OSGi元数据的JAR),删除它们,甚至升级它们,而无需重新启动应用程序
它通过将所有JAR隔离到它们自己的
ClassLoader
中(简化一些事情),并计算每个包导出/导入哪些类,然后确保每个ClassLoader
都可以在必要时从其他类中看到必要的类(这很难!)不过,通过为要在运行时加载的每组JAR创建^{} 的新实例,您可以有一个简单的动态加载机制
它的工作原理大致如下:
# 2 楼答案
根据评论部分的讨论,我找到了一个解决方案。这可能不像我希望的那样通用,并且假设您知道要使用的类(与只将Maven依赖项添加到pom.xml或旧的JDK8解决方案相反)
就像Renato指出的那样,你需要知道使用它的类。在我的例子中,它是一个Camel组件,我需要将其转换并添加到这个框架中。这些类是您在第二步中检索到的,scheme是component的名称
因此,第二部分是使其动态可用。添加了第一部分和第三部分,以获得完整解决方案的示例