【问题标题】:Change classloader更改类加载器
【发布时间】:2014-08-26 05:17:10
【问题描述】:

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

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

和:

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

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

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

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

【问题讨论】:

    标签: java reflection multithreading classloader bcel


    【解决方案1】:

    您通过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();
    

    【讨论】:

      【解决方案2】:

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

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

      【讨论】:

      • 好的,抱歉... 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(),作为父类加载器。
      • 图的链接已失效。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多