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

时间:2023-02-05 17:56:40

Let's say I have three classes, example.ClassA, example.ClassB, and example.ClassLoader. ClassA prints out HelloWorld and ClassB imports example.ClassA and invokes its main() method. If I do this:

假设我有三个类,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.ClassA

It works and uses my class loader. However, if I do this:

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

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

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

ClassB uses my class loader, but ClassA (which was imported by ClassB) is loaded using a default class loader.

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

Is there any way to force Java to always use my class loader (unless another class loader is specified explicitly)?

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

EDIT: Thanks to Paŭlo Ebermann's answer below, I figured the problem is that I'm calling the parent class loader (URLClassLoader) to load the classes that I don't need to touch, and those loaded classes set that as it's context class loader, so classes imported from it uses the parent class loader of my custom loader. (confusing, sorry) Now I can get it to work by manually reading in EVERY class, however it seems redundant as I've copied URLClassLoader's code directly. Is there a way to tell the parent class loader to find and define the class, BUT set the Class's context class loader to your custom one?

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

1 个解决方案

#1


7  

If your class loader is implemented right, it will first ask its parent class loader about any classes that should be loaded.

如果你的类加载器是正确实现的,它将首先向其父类加载器询问应该加载的任何类。

The parent class loader of your loader will likely be the usual application class loader. This means that every class your class loader loads will first searched on the application class loader, and only if not found, on your one.

加载器的父类加载器可能是通常的应用程序类加载器。这意味着您的类加载器加载的每个类将首先在应用程序类加载器上进行搜索,并且只有在未找到时才会在您的类加载器上进行搜

All classes which are defined by your class loader will also search their needed classes on your classloader. If they don't do, your ClassA is not really loaded by your loader.

由类加载器定义的所有类也将在类加载器上搜索所需的类。如果他们不这样做,你的ClassA并没有真正装载你的装载机。

If this does not help, you will need to show some code on how you got to your results.

如果这没有帮助,您需要显示一些关于如何获得结果的代码。


An idea on what to do:

关于该做什么的想法:

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);
        }
    }
}

To your question:

对你的问题:

Is there a way to tell the parent class loader to find and define the class, BUT set the Class's context class loader to your custom one?

有没有办法告诉父类加载器查找和定义类,但是将Class的上下文类加载器设置为自定义类?

No. The ClassLoader of a class is always the one whose defineClass method was called to create the class. (The context class loader is something else - it is thread specific, and only used by classes which explicitly want to use it, not by classes resolving their own direct dependencies.)

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

#1


7  

If your class loader is implemented right, it will first ask its parent class loader about any classes that should be loaded.

如果你的类加载器是正确实现的,它将首先向其父类加载器询问应该加载的任何类。

The parent class loader of your loader will likely be the usual application class loader. This means that every class your class loader loads will first searched on the application class loader, and only if not found, on your one.

加载器的父类加载器可能是通常的应用程序类加载器。这意味着您的类加载器加载的每个类将首先在应用程序类加载器上进行搜索,并且只有在未找到时才会在您的类加载器上进行搜

All classes which are defined by your class loader will also search their needed classes on your classloader. If they don't do, your ClassA is not really loaded by your loader.

由类加载器定义的所有类也将在类加载器上搜索所需的类。如果他们不这样做,你的ClassA并没有真正装载你的装载机。

If this does not help, you will need to show some code on how you got to your results.

如果这没有帮助,您需要显示一些关于如何获得结果的代码。


An idea on what to do:

关于该做什么的想法:

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);
        }
    }
}

To your question:

对你的问题:

Is there a way to tell the parent class loader to find and define the class, BUT set the Class's context class loader to your custom one?

有没有办法告诉父类加载器查找和定义类,但是将Class的上下文类加载器设置为自定义类?

No. The ClassLoader of a class is always the one whose defineClass method was called to create the class. (The context class loader is something else - it is thread specific, and only used by classes which explicitly want to use it, not by classes resolving their own direct dependencies.)

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