如何更改Java中的默认类加载器? [英] How to change default class loader in Java?

查看:385
本文介绍了如何更改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ŭloEbermann的答案下面,我想我的问题是,我调用父类加载器(URLClassLoader)加载类,我不需要触摸,那些加载类设置它作为上下文类加载器,所以从它导入的类使用我的自定义加载器的父类加载器。 (混淆,对不起)现在我可以通过手动阅读在每个类中工作,但它似乎多余,因为我直接复制URLClassLoader的代码。有没有办法告诉父类加载器来查找和定义类,但是把类的上下文类加载器设置为你自己的类加载器?

解决方案

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



加载器的父类加载器可能是通常的应用程序类加载器。这意味着你的类加载器将加载的每个类将首先在应用程序类加载器上搜索,并且只有在没有找到,在你的一个。



所有类,类加载器也将在您的类加载器上搜索他们需要的类。如果他们不这样做,你的ClassA不是真的由你的加载器加载。



如果这不起作用,你将需要显示一些代码,



$ b $ b


p $ p> 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 find find);
}
byte [] array = modifyClass(classData);
return defineClass(name,array,0,array.length);
}
catch(IOException io){
throw new ClassNotFoundException(io);
}
}
else {
return super.findClass(name);
}
}
}

p>


有一种方法告诉父类加载器来查找和定义类
将类的上下文类加载器设置为自定义一个?


否。类的ClassLoader总是调用 defineClass 方法来创建类。 (上下文类加载器 a>是别的东西 - 它是线程特定的,并且仅由明确想要使用它的类使用,而不是由类解析自己的直接依赖关系。)


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:

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

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

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

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?

解决方案

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.

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?

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.)

这篇关于如何更改Java中的默认类加载器?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆