简体   繁体   English

如何使Java使用我的类加载器进行类解析

[英]How to make Java use my classloader for class resolution

Test case below. 下面的测试用例。 Output: 输出:

custom loading of: pkg.TestRun
ran.
wish this would run in my custom classloader

What I want to happen is to see "custom loading of: pkg.TestRun" show up between the second and third lines of the output. 我想要发生的是看到“自定义加载:pkg.TestRun”显示在输出的第二行和第三行之间。

How can I get it to load the dependencies of a class from my custom classloader, even when the first class is loaded from the parent? 即使从父级加载了第一个类,如何从自定义的类加载器中加载类的依赖关系呢? Note, that in my real case, I get a class not found exception because the equivalent of OtherClass is not known to the parent classloader. 请注意,在我的真实情况下,我得到一个未找到的类异常,因为父类加载器不知道OtherClass的等效项。

I know one solution is to have the custom class loader explicitly load TestRun. 我知道一种解决方案是让自定义类加载器显式加载TestRun。 However, how to load TestRun is already known to the parent classloader and I don't want to have to manage finding it separately since it's already done and it might be tricky for me to somehow figure that out when it's already being managed without me doing anything. 但是,父类加载器已经知道如何加载TestRun,并且我不想单独管理它,因为它已经完成了,因此我可能很难以某种方式弄清楚何时不用我就可以对其进行管理任何东西。 And I've tried to do something like super.getResource (returns null) or findClass (already sets parent as classloader for it) but neither worked. 而且我尝试做类似super.getResource(返回null)或findClass(已经将parent设置为classloader)的东西,但是都没有用。

So, can I let the parent find the class, but the custom loader define it? 因此,我可以让父级找到该类,但自定义加载程序对其进行定义吗? Or, is there just a way to make it so that it will always use my custom loader to look for dependencies? 或者,是否只有一种方法可以使它始终使用我的自定义加载程序来查找依赖项?

package pkg;

public class TestCL {

    static class MyCL extends ClassLoader {
        MyCL(ClassLoader parent) {
            super(parent);
        }

        @Override
        public Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
            System.out.println("custom loading of: " + name);
            return getParent().loadClass(name);
        }
    }

    public static void main(String[] args) throws Exception {
        MyCL cl = new MyCL(Thread.currentThread().getContextClassLoader());
        Thread.currentThread().setContextClassLoader(cl);
        cl.loadClass("pkg.TestRun").getMethod("run", new Class[] {}).invoke(null);
    }
}

class TestRun {
    public static void run() {
        System.out.println("ran.");
        OtherClass.runAlso();
    }
}

class OtherClass {
    public static void runAlso() {
        System.out.println("wish this would run in my custom classloader");
    }
}

The problem was you were not loading anything using your custom classloader 问题是您没有使用自定义类加载器加载任何内容
It asks it parent to load the class, so everything was being loaded by the main classloader. 它要求其父级加载该类,因此所有内容均由主类加载器加载。

Try this modified code: 尝试以下修改的代码:

package stackoverflow.june2012.classloader;

import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;

public class TestCL {

static class MyCL extends URLClassLoader {
    MyCL(URL[] urls) {
        // NOTE: No parent classloader!
        super(urls, null);
    }

    @Override
    public Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
        System.out.println("custom loading of: " + name);
        return super.loadClass(name, resolve);
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        System.out.println("findClass: " + name);
        System.out.println("NOTE: Only called if this classloader does NOT have a parent");
        return super.findClass(name);
    }
}

public static void main(String[] args) throws Exception {
    URL url = new File("./bin").toURI().toURL();
    System.out.println("url to search for classes: " + url);
    MyCL cl = new MyCL(new URL[] {url});
    Thread.currentThread().setContextClassLoader(cl);
    Class loadClass = cl.loadClass("stackoverflow.june2012.classloader.TestRun");
    System.out.println("Loaded TestRun using classloader: " + loadClass.getClassLoader());
    loadClass.getMethod("run", new Class[] {}).invoke(null);
}

} }

And I had to move your other 2 classes into a separate file, otherwise it cant be accessed as it was package-private in a different classloader: 而且我必须将其他两个类移到一个单独的文件中,否则无法访问它,因为它在另一个类加载器中是程序包私有的:

package stackoverflow.june2012.classloader;

public class TestRun {
    public static void run() {
    System.out.println("ran.");
    OtherClass.runAlso();
    }
}

class OtherClass {
    public static void runAlso() {
        System.out.println("wish this would run in my custom classloader");
    }
}

One solution is to use the parent classloader (or the one that already knows where it is) to read the bytes of the class, then define it in the custom class loader. 一种解决方案是使用父类加载器(或已经知道其位置的父类加载器)读取类的字节,然后在自定义类加载器中对其进行定义。

So, something like this: 因此,如下所示:

public static byte[] loadBytesForClass(ClassLoader loader, String fqn) throws IOException {
    InputStream input = loader.getResourceAsStream(fqn.replace(".", "/") + ".class");
    if (input == null) {
        System.out.println("Could not load bytes for class: " + fqn);
    }
    ByteArrayOutputStream output = new ByteArrayOutputStream();
    StringUtil.copy(input, output);
    return output.toByteArray();
}

Then you can call defineClass(name, bytes, 0, bytes.length) with those bytes to define it in the custom class loader. 然后,您可以使用这些字节调用defineClass(name, bytes, 0, bytes.length)以在自定义类加载器中对其进行定义。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM