我正在尝试创建一个行为如下的URLClassLoader:
我无法让这个工作。在下面的尝试中,我希望SpecialClassLoader
成功加载Test$Thing
。在这样做时,我希望它尝试加载Test$SuperThing
,我希望它可以替代加载虚拟类Nothing
。
然而,出现问题并且NoClassDefFoundError
会引发Test$SuperThing
。
有谁知道如何解决这个问题?
public class Test {
private static class SuperThing {}
private static class Thing extends SuperThing {}
public static void main(String[] args) {
Set<String> inclusions = new HashSet<String>();
inclusions.add("Test$Thing"); // note Test$SuperThing is excluded
URLClassLoader cl = (URLClassLoader)
Thread.currentThread().getContextClassLoader();
SpecialClassLoader cll =
new SpecialClassLoader(cl.getURLs(), inclusions);
try {
cll.loadClass("Test$Thing"); // line 22 (see stacktrace below)
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
private static class Nothing {}
private static final class SpecialClassLoader extends URLClassLoader {
private final Set<String> inclusions;
public SpecialClassLoader(URL[] urls, Set<String> inclusions) {
super(urls);
this.inclusions = inclusions;
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
if (inclusions.contains(name)) {
return findClass(name); // line 40 (see stacktrace below)
}
return Nothing.class;
}
}
}
编辑:这是我得到的堆栈跟踪(上面的清单中显示了第22和40行):
Exception in thread "main" java.lang.NoClassDefFoundError: Test$SuperThing at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClass(ClassLoader.java:620) at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:124) at java.net.URLClassLoader.defineClass(URLClassLoader.java:260) at java.net.URLClassLoader.access$000(URLClassLoader.java:56) at java.net.URLClassLoader$1.run(URLClassLoader.java:195) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(URLClassLoader.java:188) at Test$SpecialClassLoader.loadClass(Test.java:40) at Test.main(Test.java:22)
答案 0 :(得分:4)
以下是发生的事情。
当您尝试加载Test $ Thing时,defineClass方法会发现它需要加载Test $ Thing的超类;即测试$ SuperThing。
系统类加载器调用您的类加载器,它返回Nothing类。
系统类加载器说“哇!该类没有正确的完全限定类名!”,并抛出NoClassDefFoundError
。
基本上,这个系统类加载器可以防止那些会破坏JVM稳定性的东西。如果它允许自定义类加载器加载错误的类,则可能发生令人讨厌的事情。例如,考虑一下在您欺骗类加载器加载Nothing类之后,如果某些代码调用了为Test $ SuperThing类定义的某些方法,可能会发生什么。
如果你想做一些替代某个类的虚拟版本的脏技巧,你需要动态生成字节码,使用正确的完全限定类名,正确的超类和接口,以及带有正确签名的方法。简而言之,您必须满足二进制兼容性规则。如果不这样做,系统类加载器将拒绝加载您的类...无论您在自定义类加载器中尝试做什么。
坦率地说,你应该采用一种完全不同的方法来处理你想做的事情。答案 1 :(得分:-1)
可以在编译器的输出中找到名为Test $ Thing.class的类吗?由于代码在这里给出,它将永远不会直接引用,也许编译器优化了类。在这种情况下,反射无法找到类。
如果是这种情况,您只需在类中添加(非反射)引用,以便编译它。
编辑:在自己尝试之后,我想我找到了问题的根源。如果我从Thing-declaration中删除了'extends SuperThing',它抱怨没有找到java.lang.Object。类加载器可能会尝试加载所有依赖类(包括它扩展的类),但不能加载,因为它们不在白名单中。但这是一种理论。