【问题标题】:ClassLoader doesn't seem to be returning object correctlyClassLoader 似乎没有正确返回对象
【发布时间】:2014-05-20 14:58:51
【问题描述】:

我有一个实现接口 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。

有什么想法吗?

谢谢

【问题讨论】:

  • 信息不足。有没有把一些 jar 文件放到系统类路径中,而另一些放到 webapp 中?
  • 不确定我是否理解问题的相关性 - ClassLoader 应该从指定位置拾取类,这也是部署 .class 文件的位置?
  • 我很想投反对票...你为什么要创建自己的类加载器?
  • 我的类加载器是以后在应用中加载一个动态创建的类
  • 所以你打算生成 Java 源代码,编译它,将它复制到 web 应用程序类加载器上,以后可能会删除或修改它......祝你好运;)。

标签: java jakarta-ee tomcat classloader


【解决方案1】:

这是因为如果你用不同的类加载器加载同一个类,它就会被认为是不同的类。甚至可以简化您的问题:

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 相同的 ToTest

来自JLS

“在运行时,具有相同二进制名称的多个引用类型可能由不同的类加载器同时加载。这些类型可能代表也可能不代表相同的类型声明。即使两个这样的类型确实代表相同的类型声明,它们被认为是不同的。”

“两个引用类型是相同的运行时类型,如果 [...] 它们都是类或两个接口类型,由相同的类加载器定义,并且具有相同的二进制名称 [...]”

正如 bmargulies 指出的那样,您可以通过将当前类加载器设置为自定义类加载器的父级来避免这种情况:

URLClassLoader classLoader = new URLClassLoader(new URL[]{new File("c:\\tomcatfolder\\wtpwebapps\\MyApp\\WEB-INF\\classes").toURI().toURL()}, getClass().getClassLoader());

【讨论】:

  • 好东西——我将父类加载器添加到我的类加载器中,问题就解决了。感谢您的帮助。
  • 伙计们,拜托......建议的解决方案是无稽之谈。您不能在与父类加载器相同的路径上定义类加载器。这让孩子毫无用处。
  • @PavelHoral 我同意。由于我不知道 OPs 类路径的结构,因此很难判断它是否有效。例如。如果接口和实现类位于类路径中的不同位置,那将是有意义的。这就是我写“可能”的原因;-)
  • 其实我也同意——我可以使用 getClass().getClassLoader() 来加载类的实例。这让我对类加载器有所启发,所以不要觉得你的时间被浪费了!谢谢。
【解决方案2】:

您没有将新的类加载器设置为从旧的类加载器继承。因此,它不会与您现有的类加载器共享任何类。因此,从您的类加载器反射创建的任何对象都不会成为您的任何类的实例。

我不明白你为什么需要这个类加载器,但如果你需要,你需要设置父类。

【讨论】:

    猜你喜欢
    • 2014-03-05
    • 1970-01-01
    • 2019-10-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多