Java 更改类加载器

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2822004/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 13:16:15  来源:igfitidea点击:

Change classloader

javareflectionmultithreadingclassloaderbcel

提问by Chris

I'm trying to switch the class loader at runtime:

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

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

and:

和:

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

(InjectingClassLoaderis a class extending the org.apache.bcel.util.ClassLoaderwhich should load the modified versions of classes before asking it's parent for them)

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

I'd like to make the result of "approach 1" and "approach 2" exactly same, but it looks like thread.setContextClassLoader(classLoader)does nothing and the "approach 2" always uses the system classloader (can be determined by comparing tcl and ccl variables while debugging).

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

Is it possible to make allclasses loaded by new thread use given classloader?

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

采纳答案by Patrick Schneider

The anonymous class you are creating via new Thread("test") { ... }has an implicit reference to the enclosing instance. Class literals within this anonymous class will be loaded using the enclosing class's ClassLoader.

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

In order to make this test work, you should pull out a proper Runnable implementation, and load it reflectively using the desired ClassLoader; then pass that explicitly to the thread. Something like:

为了使这个测试工作,你应该拉出一个合适的 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();

回答by G__

I think InjectingClassLoader may be important here. Remember how classloading delegation works - if more than one classloader in your hierarchy can find the class, the top-most classloader will be the one that loads. (See Figure 21.2 here)

我认为 InjectingClassLoader 在这里可能很重要。记住类加载委托是如何工作的——如果你的层次结构中有不止一个类加载器可以找到这个类,那么最顶层的类加载器将是加载的那个。(请参阅此处的图 21.2 )

Since InjectingClassLoader doesn't specify a parent in its constructor, it will default to the constructor in the abstract ClassLoader, which will set the current context classloader as InjectingClassLoader's parent. Therefore, since the parent (old context classloader) can find TestProxy, it always loads the class before InjectingClassLoader gets a chance to.

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