如何在Java中更改默认类装入器?

2022-09-04 22:48:03

假设我有三个类,例如。例如,A 类。类 B 和示例。类加载器。ClassA打印出HelloWorld和ClassB导入示例。ClassA 并调用其 main() 方法。如果我这样做:

java -cp Example.jar -Djava.system.class.loader=example.类加载器示例。A类

它工作并使用我的类加载器。但是,如果我这样做:

java -cp Example.jar -Djava.system.class.loader=example.类加载器示例。B类

ClassB 使用我的类装入器,但 ClassA(由 ClassB 导入)是使用缺省类装入器装入的。

有没有办法强制Java始终使用我的类装入器(除非显式指定了另一个类装入器)?

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


答案 1

如果您的类装入器已正确实现,它将首先询问其父类装入器有关应装入的任何类的信息。

装入器的父类装入器可能是通常的应用程序类装入器。这意味着您的类装入器装入的每个类将首先在应用程序类装入器上搜索,并且只有在未找到的情况下,才会在您的类装入器上搜索。

类装入器定义的所有类也将在类装入器上搜索它们所需的类。如果他们不这样做,您的 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);
        }
    }
}

对于您的问题:

有没有办法告诉父类装入器查找并定义类,但将类的上下文类装入器设置为您的自定义类装入器?

哈哈类的 ClassLoader 始终是调用其方法来创建类的类。(上下文类加载器是另一回事 - 它是特定于线程的,并且仅由明确想要使用它的类使用,而不是由解析其自身直接依赖关系的类使用。defineClass


答案 2