为什么这个URLClassLoader有时会工作,有时却不行?

时间:2016-06-24 14:44:48

标签: java classloader urlclassloader

我有一个相当复杂的Java软件,它必须动态加载一个类,创建一个对象并调用它的一个方法。事实证明,在某些情况下,如果加载的类引用另一个类,则会引发NoClassDefFoundError(由ClassNotFoundException引起)。

说我们有:

public interface Go {
    void go();
}

假设要加载的类是:

package foo;
import static baz.Baz.BAZ;
public class Foo implements Go {
    @Override
    void go() { 
        ...
        something = BAZ;
        ...
    }
}

,其中

package baz;
public class Baz {
    public static final int BAZ = 111;
    ...
}

假设该类以这种方式加载:

try (final URLClassLoader loader = new URLClassLoader(new URL[] { url })) {
    final Class<? extends Foo> clazz =  
        loader.loadClass("foo.Foo").asSubclass(Go.class);
    this.obj = clazz.newInstance();
    this.obj.go();
} catch ...

然后一切正常。比如说你用另一种方式做事:

try (final URLClassLoader loader = new URLClassLoader(new URL[] { url })) {
    final Class<? extends Foo> clazz =  
        loader.loadClass("foo.Foo").asSubclass(Go.class);
    this.obj = clazz.newInstance();
    //we do not invoke go() now...
} catch ...

//later, in another method...
this.obj.go();

现在调用go()会引发NoClassDefFoundError,抱怨它无法加载baz.Baz。当然网址是一样的。当然,foo.Foobaz.Baz都位于url指示的路径中。我能看到的唯一区别是调用go()方法的那一刻。

这里有什么问题?

2 个答案:

答案 0 :(得分:2)

  

这里有什么问题?

我认为问题是:

try (final URLClassLoader loader = new URLClassLoader(new URL[] { url })) {

这将导致在退出try块时关闭类加载器。 close()方法的javadoc说:

  

关闭此URLClassLoader,以便它不再可用于加载此加载程序定义的新类或资源。

调用go()方法正在触发Go类的类初始化,这就是导致Baz类被加载和初始化的原因。如果在类加载器关闭后发生后一类加载,它将失败。

答案 1 :(得分:1)

如果一个类依赖于其他类,则这些延迟加载

这意味着加载foo.FOO不会同时加载baz.BAZ。后者在首次访问时加载。在您的情况下,对go()的方法调用也会加载它。

在第二个示例中,在调用方法go()时,URLClassLoader已经关闭 - 通过try-with-resources语句。因此,它不能再加载类了。