我正在编写一个服务器程序,用于运行API的单元测试 (显示大量信息并提供Web控制权 /监控整个事情)......
此API在编译期间 并且已提供 作为JAR。
能够比较不同版本的单元测试结果 的API(无需重新启动服务器), 我希望能够卸载API的“当前”版本, 并重新加载较新的(或较旧的)。
我不想使用URLClassLoader并调用每一个
方法名称
(使用 getDeclaredMethod("someMethod")
),
因为服务器在很大程度上依赖于API而且它会是
很难以这种肮脏的方式“包装”每个方法调用。
我在想:由于JAR的所有版本的所有接口 是相同,我不能通过某种方式重新加载其他版本 JAR(没有那个by-name-invokation?)。
注意:我使用的是最新的Java SE(6)和Java EE(5)。
如果您认为,我想要实现的目标是不可能的, 请提出“解决方法”或不同的概念。
答案 0 :(得分:10)
我认为如果你使用
加载一个类Class.forName(clsname, init, classloader);
(Javadoc此处)您将获得给定类加载器提供的类的实例。由于该类而加载的所有内容也将通过相同的类加载器加载。
只要你非常小心从这一点开始实例化的对象(允许GC),你应该可以重新加载不同的版本。我之前用Java 1.3做了一次,它花了很多调试,但最后我有一个“bootstrap”应用程序,它按名称加载Runnable
类,并且能够通过实例化“软重启”新的类加载器针对不同的URL并重新开始。
答案 1 :(得分:5)
您可以以编程方式修改类路径以反映您的JAR更改。 我将如何做到这一点:
URLClassLoader urlClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
Method m = URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{URL.class});
m.setAccessible(true);
m.invoke(urlClassLoader, jarFile.toURI().toURL());
String cp = System.getProperty("java.class.path");
if (cp != null) {
cp += File.pathSeparatorChar + jarFile.getCanonicalPath();
} else {
cp = jarFile.toURI().getPath();
}
System.setProperty("java.class.path", cp);
其中 jarFile 是您要使用/覆盖的jar的版本。
答案 2 :(得分:4)
您可以使用opensource包: JclLoader ,这有助于加载同一jar的不同版本。在我们的一个系统中,这也是进行测试的需要。
答案 3 :(得分:1)
OSGi是一个允许您这样做的框架。 JSR 277 the Java Module System也是为了这样做而设计的(我认为)。我没有遵循OSGi-JS-277争论,所以我不知道他们是否正试图让他们感到厌烦。
你可以使用类加载器自己滚动,但它不那么“有趣”。
答案 4 :(得分:0)
是。我在NFJS会议上看过它。 Web容器之类的东西支持应用程序的热部署,并涉及利用类加载器的范围。为了实现它,你需要创建一个新的类加载器并使用它来加载有问题的库..然后抛弃加载器(或不加载)并在你想重新加载时创建另一个加载器。您可能还必须覆盖类加载器的行为(我记得有些类加载器默认情况下首先通过父级获取类。)另外,我还记得一个警告,即不同类加载器创建的对象不即使.class文件完全相同,也相互兼容(不是相同类型)。
但对我而言,这主要是deep magic。 ; - )
答案 5 :(得分:-3)
可能不是。 Java类加载器并不真正支持运行时加载;甚至可用的类加载器都是使用代理对象的黑客。