更改类加载器

Chr*_*ris 11 java reflection multithreading bcel classloader

我正在尝试在运行时切换类加载器:

public class Test {
    public static void main(String[] args) throws Exception {
        final InjectingClassLoader classLoader = new InjectingClassLoader();
        Thread.currentThread().setContextClassLoader(classLoader);
        Thread thread = new Thread("test") {
            public void run() {
                System.out.println("running...");
                // approach 1
                ClassLoader cl = TestProxy.class.getClassLoader();
                try {
                    Class c = classLoader.loadClass("classloader.TestProxy");
                    Object o = c.newInstance();
                    c.getMethod("test", new Class[] {}).invoke(o);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                // approach 2
                new TestProxy().test();
            };
        };
        thread.setContextClassLoader(classLoader);
        thread.start();
    }
}
Run Code Online (Sandbox Code Playgroud)

和:

public class TestProxy {
    public void test() {
        ClassLoader tcl = Thread.currentThread().getContextClassLoader();
        ClassLoader ccl = ClassToLoad.class.getClassLoader();
        ClassToLoad classToLoad = new ClassToLoad();
    }
}
Run Code Online (Sandbox Code Playgroud)

(InjectingClassLoader是一个扩展org.apache.bcel.util.ClassLoader的类,它应该在要求它们的父类之前加载类的修改版本)

我想使"方法1"和"方法2"的结果完全相同,但看起来像thread.setContextClassLoader(classLoader)什么都不做,"方法2"总是使用系统类加载器(可以通过比较确定)调试时的tcl和ccl变量).

是否可以通过给定类加载器使用新线程加载所有类?

小智 16

您正在创建的匿名类new Thread("test") { ... }具有对封闭实例的隐式引用.将使用封闭类的ClassLoader加载此匿名类中的类文字.

为了使这个测试工作,您应该提取一个正确的Runnable实现,并使用所需的ClassLoader反射加载它; 然后将其明确传递给线程.就像是:

    public final class MyRunnable implements Runnable {
        public void run() {
            System.out.println("running...");
            // etc...
        }
    }

    final Class runnableClass = classLoader.loadClass("classloader.MyRunnable");
    final Thread thread = new Thread((Runnable) runableClass.newInstance());

    thread.setContextClassLoader(classLoader); // this is unnecessary unless you you are using libraries that themselves call .getContextClassLoader()

    thread.start();
Run Code Online (Sandbox Code Playgroud)