如何使URLClassLoader表现得像这样?

时间:2010-01-25 10:58:00

标签: java classloader

我正在尝试创建一个行为如下的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)

2 个答案:

答案 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。类加载器可能会尝试加载所有依赖类(包括它扩展的类),但不能加载,因为它们不在白名单中。但这是一种理论。