Java 更改类加载器

Java 更改类加载器,java,reflection,multithreading,classloader,bcel,Java,Reflection,Multithreading,Classloader,Bcel,我正在尝试在运行时切换类加载器: public class Test { public static void main(String[] args) throws Exception { final InjectingClassLoader classLoader = new InjectingClassLoader(); Thread.currentThread().setContextClassLoader(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();
    }
}
以及:

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

我想让“方法1”和“方法2”的结果完全相同,但它看起来像线程。setContextClassLoader(classLoader)什么都不做,“方法2”总是使用系统classLoader(可以在调试时通过比较tcl和ccl变量来确定)


有可能让新线程加载的所有类都使用给定的类加载器吗?

我认为InjectingClassLoader在这里可能很重要。记住类加载委托是如何工作的——如果层次结构中有多个类加载器可以找到该类,则最顶层的类加载器将是加载的类加载器。(见图21.2)


由于InjectingClassLoader没有在其构造函数中指定父类,因此它将默认为抽象类加载器中的构造函数,抽象类加载器将当前上下文类加载器设置为InjectingClassLoader的父类。因此,由于父级(旧上下文类加载器)可以找到TestProxy,因此它总是在InjectingClassLoader有机会访问之前加载该类。

通过
new Thread(“test”){…}
创建的匿名类隐式引用了封闭实例。将使用封闭类的类加载器加载此匿名类中的类文本

为了使这个测试能够工作,您应该拿出一个适当的可运行实现,并使用所需的类加载器反射地加载它;然后将其显式地传递给线程。比如:

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

好的,对不起。。。这与InjectingClassLoader是一个扩展org.apache.bcel.util.ClassLoader(使用实现的modifyClass方法)的类的方式有关,该类在加载之前会对类产生一些讨厌的东西。AFAIK org.apache.bcel.util.ClassLoader正在覆盖类加载器链的默认行为,其方式是在使用父类加载器之前加载修改过的类。只需检查源代码,org.apache.bcel.utilClassloader仍然扩展java.lang.ClassLoader…默认类加载器构造函数使用作为父类加载器的ClassLoader.getSystemClassLoader(),而不是Thread.currentThread().getContextClassLoader()。指向图的链接已失效。
    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();