我可以动态卸载和重新加载(相同的其他版本)JAR吗?

我正在编写一个服务器程序,用于运行API的unit testing(显示大量信息并提供Web访问来控制/监控整个事物)…

此API 在编译期间为服务器所知,并作为JAR提供。

为了能够比较不同版本 API的unit testing结果(无需重新启动服务器),我希望能够卸载API的“当前”版本,并重新加载较新版本(或较旧的版本) 。

我不想使用URLClassLoader并按名称调用每个方法
(使用getDeclaredMethod("someMethod") ),
因为服务器在很大程度上依赖于API,以这种脏的方式“包装”每个方法调用会很复杂。

我在想:由于JAR的所有版本的所有接口都是相同的 ,我不能通过某种方式重新加载其他版本的JAR(没有那个by-name-invokation?)。

注意:我使用的是最新的Java SE(6)和Java EE(5)。

如果您认为,我想要实现的目标是不可能的,请提出“解决方法”或不同的概念。

我想如果你加载一个类使用

 Class.forName(clsname, init, classloader); 

(这里是Javadoc )您将获得给定类加载器提供的类的实例。 由于该类而加载的所有内容也将通过相同的类加载器加载。

只要你非常小心从这一点开始实例化的对象(允许GC),你应该能够重新加载不同的版本。 我之前用Java 1.3做了一次,它花了很多调试,但最后我有一个“bootstrap”应用程序,它按名称加载了一个Runnable类,并且能够通过实例化一个新的类加载器来“软重启”不同的URL,然后再去。

您可以以编程方式修改类路径以反映您的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的版本。

你可以使用opensource包: JclLoader ,它有助于加载同一个jar的不同版本。 这也是我们的一个系统需要进行测试的。

链接: http : //sourceforge.net/projects/jcloader/

OSGi是一个允许您这样做的框架。 JSR 277 Java模块系统也是为这样做而设计的(我认为)。 我没有遵循OSGi-JS-JSR 277的辩论,所以我不知道他们是否试图让他们感到愤怒。

你可以用类加载器自己动手,但它不那么“有趣”。

是。 我在NFJS会议上看过它。 Web容器之类的东西支持应用程序的热部署,并涉及利用类加载器的范围。 为了实现它,你需要创建一个新的类加载器并使用它来加载有问题的库..然后抛弃加载器(或不加载)并在你想重新加载时创建另一个加载器。 您可能还必须覆盖类加载器的行为(我记得有些类加载器默认情况下首先通过父类获取类。)另外,我还记得一个警告,即不同类加载器创建的对象兼容(不是相同的)即使.class文件完全相同,也互相输入。

不过,这对我来说大部分都是神奇的 。 😉

可能不会。 Java类加载器并不真正支持运行时加载; 甚至可用的类加载器都是使用代理对象的黑客。