1

下面的测试用例。输出:

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

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

我怎样才能让它从我的自定义类加载器加载一个类的依赖项,即使第一个类是从父类加载的?请注意,在我的真实案例中,我得到了一个找不到类的异常,因为父类加载器不知道 OtherClass 的等价物。

我知道一种解决方案是让自定义类加载器显式加载 TestRun。但是,如何加载 TestRun 已为父类加载器所知,我不想单独管理查找它,因为它已经完成,而且我可能很难以某种方式弄清楚它何时已经在没有我做的情况下进行管理任何事物。而且我尝试过类似 super.getResource (返回 null)或 findClass (已经将 parent 设置为它的类加载器)之类的东西,但都没有奏效。

那么,我可以让父类找到类,但自定义加载器定义它吗?或者,有没有办法让它总是使用我的自定义加载器来查找依赖项?

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

2 回答 2

0

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

试试这个修改后的代码:

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

}

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

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");
    }
}
于 2012-06-30T10:16:45.620 回答
0

一种解决方案是使用父类加载器(或已经知道它在哪里的类加载器)来读取类的字节,然后在自定义类加载器中定义它。

所以,像这样:

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)使用这些字节调用以在自定义类加载器中定义它。

于 2012-12-21T18:35:29.227 回答