如何在Java中更改默认的类加载器?

11 浏览
0 Comments

如何在Java中更改默认的类加载器?

假设我有三个类,example.ClassA、example.ClassB和example.ClassLoader。ClassA打印出HelloWorld,ClassB导入example.ClassA并调用其main()方法。如果我这样做:

java -cp Example.jar -Djava.system.class.loader=example.ClassLoader example.ClassA

它可以工作并使用我的类加载器。然而,如果我这样做:

java -cp Example.jar -Djava.system.class.loader=example.ClassLoader example.ClassB

ClassB使用我的类加载器,但是ClassA(被ClassB导入的类)是使用默认的类加载器加载的。

有没有办法强制Java始终使用我的类加载器(除非明确指定另一个类加载器)?

编辑:感谢Paŭlo Ebermann下面的答案,我发现问题在于我调用了父类加载器(URLClassLoader)来加载我不需要触及的类,而这些加载的类将其设置为上下文类加载器,因此从中导入的类使用了我的自定义加载器的父类加载器。(有点混乱,抱歉)现在我可以通过手动读取每个类来使其工作,但是这似乎是多余的,因为我直接复制了URLClassLoader的代码。有没有办法告诉父类加载器找到并定义类,但将类的上下文类加载器设置为您自定义的加载器?

0
0 Comments

如何在Java中更改默认的类加载器?

问题的出现原因是:如果你的类加载器实现得正确,它会首先询问其父类加载器是否有需要加载的类。你的加载器的父类加载器很可能是通常的应用程序类加载器。这意味着你的类加载器加载的每个类都会首先在应用程序类加载器上搜索,只有在找不到的情况下才会在你的类加载器上搜索。由你的类加载器定义的所有类也会在你的类加载器上搜索它们所需的类。如果它们没有这样做,那么你的ClassA实际上并不是由你的加载器加载的。

解决方法如下:

class ModifyingClassLoader extends URLClassLoader {
    // TODO: add constructors
    private boolean needsModifying(String name) {
        // TODO
    }
    private byte[] modifyClass(InputStream original) throws IOException {
        // TODO
    }
    public Class findClass(String name) throws {
        if(needsModifying(name)) {
            try {
                InputStream classData = getResourceAsStream(name.replace('.', '/') + ".class");
                if(classData == null) {
                    throw new ClassNotFoundException("class " + name + " is not findable");
                }
                byte[] array = modifyClass(classData);
                return defineClass(name, array, 0, array.length);
            }
            catch(IOException io) {
                throw new ClassNotFoundException(io);
            }
        }
        else {
            return super.findClass(name);
        }
    }
}

关于你的问题:是否有办法告诉父类加载器查找和定义类,但将类的上下文类加载器设置为你自定义的类加载器?

答案是否定的。一个类的类加载器始终是调用其`defineClass`方法创建类的类加载器。(上下文类加载器是另一回事 - 它是线程特定的,仅由显式想要使用它的类使用,而不是由解析其自身直接依赖关系的类使用。)

你是否在说,因为我在我的类加载器上调用了`getParent.loadClass(name)`,导入的类使用的是我的类加载器的父类加载器等等?

如果是这样的话,是否有办法让父类加载器加载类,但设置上下文类加载器(这是正确的术语吗?)为我自定义的类加载器?我不想编写文件读取器并完成所有加载工作。

你可以继承`URLClassLoader`并在那里重写`loadClass()`方法。但你为什么需要自定义类加载器呢?

我正在修改特定包中某些类的字节码,然后加载它们,并使用父类加载其余的类。

我也尝试了继承`URLClassLoader`,但结果是一样的。我查看了`Class.getClassLoader()`,发现它调用一个本地函数来查找它的类加载器,所以它不会存储自己的加载器。

"为什么你需要自定义类加载器呢?" 这有一百万零五个原因。

0