为什么这个 URLClassLoader 有时有效有时无效?
Why this URLClassLoader sometimes works and sometimes doesn't?
我有一个相当复杂的 Java 软件,它必须动态加载一个 class,创建一个对象并调用其中一个方法。事实证明,在某些情况下,如果加载的 class 引用另一个 NoClassDefFoundError
(由 ClassNotFoundException
引起)。
假设我们有:
public interface Go {
void go();
}
说要加载的class是:
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;
...
}
说 class 是这样加载的:
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
。当然 url 是一样的。当然foo.Foo
和baz.Baz
都在url
所指的路径中。我能看到的唯一区别是调用 go()
方法的时刻。
这里有什么问题?
如果 class 依赖于其他 class,则这些 延迟加载。
也就是说加载foo.FOO
不会同时加载baz.BAZ
。后者在首次访问时加载。在您的情况下,对 go()
的方法调用也会加载它。
在您的第二个示例中,在调用方法 go()
时,URLClassLoader
已经 closed - 通过 try-with-资源声明。因此,它无法再加载 classes。
What is wrong here?
我认为问题是这样的:
try (final URLClassLoader loader = new URLClassLoader(new URL[] { url })) {
这将导致 classloader 在您退出 try 块时关闭。 close()
方法的 javadoc 表示:
Closes this URLClassLoader, so that it can no longer be used to load new classes or resources that are defined by this loader.
调用 go()
方法触发 class 对 Go
class 的初始化,这就是导致 Baz
class 加载和初始化。如果后面的 class 加载发生在 classloader 已经关闭之后,它将失败。
我有一个相当复杂的 Java 软件,它必须动态加载一个 class,创建一个对象并调用其中一个方法。事实证明,在某些情况下,如果加载的 class 引用另一个 NoClassDefFoundError
(由 ClassNotFoundException
引起)。
假设我们有:
public interface Go {
void go();
}
说要加载的class是:
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;
...
}
说 class 是这样加载的:
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
。当然 url 是一样的。当然foo.Foo
和baz.Baz
都在url
所指的路径中。我能看到的唯一区别是调用 go()
方法的时刻。
这里有什么问题?
如果 class 依赖于其他 class,则这些 延迟加载。
也就是说加载foo.FOO
不会同时加载baz.BAZ
。后者在首次访问时加载。在您的情况下,对 go()
的方法调用也会加载它。
在您的第二个示例中,在调用方法 go()
时,URLClassLoader
已经 closed - 通过 try-with-资源声明。因此,它无法再加载 classes。
What is wrong here?
我认为问题是这样的:
try (final URLClassLoader loader = new URLClassLoader(new URL[] { url })) {
这将导致 classloader 在您退出 try 块时关闭。 close()
方法的 javadoc 表示:
Closes this URLClassLoader, so that it can no longer be used to load new classes or resources that are defined by this loader.
调用 go()
方法触发 class 对 Go
class 的初始化,这就是导致 Baz
class 加载和初始化。如果后面的 class 加载发生在 classloader 已经关闭之后,它将失败。