我有一个实现接口ToTest的MyClass类,我想通过在Tomcat上运行的Web应用程序中的ClassLoader进行检索。
该类如下:
public class MyClass implements ToTest {
public String doStuff(String input) {
return null;
}
}
界面是
public interface ToTest {
public String doStuff(String input);
}
当我实例化该类的普通实例时,它可以正常工作(即看起来是ToTest的实例),但是,当我使用我的类加载器时,该对象不会解析为ToTest的实例。
即
Object myObject = new MyClass();
if (myObject instanceof ToTest) {
System.out.println("This is a ToTest");
}
将打印出消息,而:
URLClassLoader classLoader = new URLClassLoader(new URL[]{new File("c:\\tomcatfolder\\wtpwebapps\\MyApp\\WEB-INF\\classes").toURI().toURL()});
Class<?> loadedClass = classLoader.loadClass("MyClass");
Object myOtherObj = loadedClass.newInstance();
if (myOtherObj instanceof ToTest) {
System.out.println("This is a ToTest");
}
返回一个看似是MyClass对象的对象,但是'if instanceof'返回false。
有任何想法吗?
谢谢
最佳答案
这是因为如果您使用不同的类加载器加载相同的类,则将其视为不同的类。一个甚至可以简化您的问题:
URLClassLoader classLoader = new URLClassLoader(new URL[]{new File("c:\\tomcatfolder\\wtpwebapps\\MyApp\\WEB-INF\\classes").toURI().toURL()});
Class<MyClass> loadedClass = classLoader.loadClass("MyClass");
Object myOtherObj = loadedClass.newInstance(); //loaded by URLClassLoader
Object myObject = new MyClass(); // loaded by default classloader
// at this point the two objects don't have the same type
如果查看您的实际问题,就会发现
myOtherObj
是从与ToTest
进行比较的其他类加载器加载的。这意味着它的类MyClass
没有实现与用于比较的类相同的ToTest
。从JLS:
“在运行时,具有相同二进制名称的多个引用类型可能由不同的类加载器同时加载。这些类型可能代表也可能不代表相同的类型声明。即使两个这样的类型确实代表相同的类型声明,也将它们视为不同的类型。 ”。
和
“如果两个引用类型都是类或两个接口类型,由相同的类加载器定义并且具有相同的二进制名称,则它们是相同的运行时类型。
正如bmargulies指出的那样,您可以通过将当前的类加载器设置为自定义类的父级来避免这种情况:
URLClassLoader classLoader = new URLClassLoader(new URL[]{new File("c:\\tomcatfolder\\wtpwebapps\\MyApp\\WEB-INF\\classes").toURI().toURL()}, getClass().getClassLoader());