Java-如何加载同一类的不同版本?

Java-如何加载同一类的不同版本?,java,class,jvm,classloader,Java,Class,Jvm,Classloader,我读了很多关于Java类加载器的书,但到目前为止,我还没有找到这个简单问题的答案: 我在jarsv1.jar和v2.jar中有两个版本的com.abc.Hello.class。我想在我的应用程序中使用这两种方法。最简单的方法是什么 我不希望这么简单,但沿着这些思路做的事情会很棒: Classloader myClassLoader = [magic that includes v1.jar and ignores v2.jar] Hello hello = myclassLoader.load[

我读了很多关于Java类加载器的书,但到目前为止,我还没有找到这个简单问题的答案:

我在jarsv1.jarv2.jar中有两个版本的com.abc.Hello.class。我想在我的应用程序中使用这两种方法。最简单的方法是什么

我不希望这么简单,但沿着这些思路做的事情会很棒:

Classloader myClassLoader = [magic that includes v1.jar and ignores v2.jar]
Hello hello = myclassLoader.load[com.abc.Hello]
在另一个班级:

Classloader myClassLoader = [magic that includes v2.jar and ignores v1.jar]
Hello hello = myclassLoader.load[com.abc.Hello]

我希望避免使用OSGi。

您几乎已经编写了解决方案。 我希望下面的代码片段会有所帮助

ClassLoader cl = new URLClassLoader(new URL[] {new File("v1.jar").toURL()}, Thread.currentThread().getContextClassLoader());
Class<?> clazz = cl.loadClass("Hello");
ClassLoader cl=newURLClassloader(新URL[]{new File(“v1.jar”).toURL()},Thread.currentThread().getContextClassLoader());
Class clazz=cl.loadClass(“你好”);

v2.jar
替换
v1.jar
,此代码将加载版本2

这取决于您将如何处理这两个版本以及如何处理,但一般来说,您至少可以在不同的类加载器中加载2个版本的类,然后设置线程。contextClassloader()并播放


看,你走的路是对的。你必须考虑到一些事情

通常情况下,会使用父类加载器中存在的类。因此,如果您想要两个版本,那么这些类不能存在

但是,如果您想进行交互,可以使用反射,或者更好地使用公共接口。所以我要这样做:

common.jar:
BaseInterface

v1.jar:
SomeImplementation implements BaseInterface

v2.jar:
OtherImplementation implements BaseInterface

command-line:
java -classpath common.jar YourMainClass
// you don't put v1 nor v2 into the parent classloader classpath

Then in your program:

loader1 = new URLClassLoader(new URL[] {new File("v1.jar").toURL()}, Thread.currentThread().getContextClassLoader());
loader2 = new URLClassLoader(new URL[] {new File("v2.jar").toURL()}, Thread.currentThread().getContextClassLoader());

Class<?> c1 = loader1.loadClass("com.abc.Hello");
Class<?> c2 = loader2.loadClass("com.abc.Hello");

BaseInterface i1 = (BaseInterface) c1.newInstance();
BaseInterface i2 = (BaseInterface) c2.newInstance();
common.jar:
基本接口
v1.jar:
一些实现实现了BaseInterface
v2.jar:
Other实现实现BaseInterface
命令行:
java-classpath common.jar YourMainClass
//您不会将v1或v2放入父类加载器类路径
然后在您的程序中:
loader1=newURLClassLoader(新URL[]{new File(“v1.jar”).toURL()},Thread.currentThread().getContextClassLoader());
loader2=newURLClassLoader(新URL[]{new File(“v2.jar”).toURL()},Thread.currentThread().getContextClassLoader());
c1类=loader1.loadClass(“com.abc.Hello”);
c2类=loader2.loadClass(“com.abc.Hello”);
BaseInterface i1=(BaseInterface)c1.newInstance();
BaseInterface i2=(BaseInterface)c2.newInstance();

对于@helios checkout接受的答案的示例实现,我个人认为我不会想要这样的魔法,因为它会导致维护/调试噩梦。。。您能详细说明一下为什么需要这个吗?多个插件依赖于同一共享库的不同版本可能是原因之一。@beny23您还没有听说过jar地狱吗?我又打了一次。selenium使用的是一个版本,google组件使用的是另一个版本的guava,selenium失败了,所以我需要selenium使用它测试时使用的jar,google组件使用的是它测试时使用的不同版本。+1。我已经在我的帖子中添加了你的新URLClassLoader行:)这就是我所缺少的…因为有人解释说主类路径中没有这两个版本。如果您还可以解释他们如何在代码中使用c1和c2,以及如何编译,这将非常有用。也就是说,它们如何调用
c1.someMethod()
?谢谢!还有一个细节:“common”类可以沿着主代码和v1/v2代码传递。公共类是由父类加载器加载的类,因此它们对其中任何一个都可用(父类、基于v1和基于v2)。所有JRE类都属于这一类。这就是为什么您可以通过示例将java.lang.String传递给方法。@CPerkins:嗯,也许我已经将其隐式化了。无论如何:您总是使用BaseInterface,它存在于父类加载器中,因此,可以从主代码(字符串或任何其他JRE类)获得。您从不使用仅在子类加载器中加载的主代码类。一句话:BaseInterface必须具有主代码中需要的方法。我发现上面的代码总是加载类路径中的第一个类,尽管要求它从两个不同的JAR加载。原因可能是类加载器父代扩展。通过将父类设置为null,我能够从两个不同的jar加载相同的类:loader1=newurlclassloader(newurl[]{newfile(“v1.jar”).toURL()},null);loader2=新的URLClassLoader(新的URL[]{new File(“v2.jar”).toURL()},null)@helios I已实施您的建议,以处理elasticsearch客户端使用的非向后兼容版本。Checkout这个例子似乎有一些问题:1:它抛出异常:“org.elasticsearch.client.transport.NoNodeAvailableException:没有配置的节点可用”,2:有一个“CustomClassLoader”似乎根本没有使用。一个好的例子应该是一个没有无用的额外误导信息的工作案例。。。