繁体   English   中英

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

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

下面的测试用例。 输出:

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

我想要发生的是看到“自定义加载:pkg.TestRun”显示在输出的第二行和第三行之间。

即使从父级加载了第一个类,如何从自定义的类加载器中加载类的依赖关系呢? 请注意,在我的真实情况下,我得到一个未找到的类异常,因为父类加载器不知道OtherClass的等效项。

我知道一种解决方案是让自定义类加载器显式加载TestRun。 但是,父类加载器已经知道如何加载TestRun,并且我不想单独管理它,因为它已经完成了,因此我可能很难以某种方式弄清楚何时不用我就可以对其进行管理任何东西。 而且我尝试做类似super.getResource(返回null)或findClass(已经将parent设置为classloader)的东西,但是都没有用。

因此,我可以让父级找到该类,但自定义加载程序对其进行定义吗? 或者,是否只有一种方法可以使它始终使用我的自定义加载程序来查找依赖项?

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

问题是您没有使用自定义类加载器加载任何内容
它要求其父级加载该类,因此所有内容均由主类加载器加载。

尝试以下修改的代码:

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

}

而且我必须将其他两个类移到一个单独的文件中,否则无法访问它,因为它在另一个类加载器中是程序包私有的:

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

一种解决方案是使用父类加载器(或已经知道其位置的父类加载器)读取类的字节,然后在自定义类加载器中对其进行定义。

因此,如下所示:

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

然后,您可以使用这些字节调用defineClass(name, bytes, 0, bytes.length)以在自定义类加载器中对其进行定义。

暂无
暂无

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

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