12

実行時にクラスローダーを切り替えようとしています。

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を拡張するクラスであり、クラスの親に要求する前に、変更されたバージョンのクラスをロードする必要があります)

「アプローチ1」と「アプローチ2」の結果をまったく同じにしたいのですが、thread.setContextClassLoader(classLoader)は何も行わず、「アプローチ2」は常にシステムクラスローダーを使用しているようです(比較することで判断できます)。デバッグ中のtclおよびccl変数)。

新しいスレッドによってロードされたすべてのクラスに、指定されたクラスローダーを使用させることは可能ですか?

4

2 に答える 2

17

作成している匿名クラスには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();
于 2010-05-14T06:49:35.547 に答える
2

ここではInjectingClassLoaderが重要かもしれないと思います。クラスローディングの委任がどのように機能するかを覚えておいてください。階層内の複数のクラスローダーがクラスを見つけることができる場合、最上位のクラスローダーがロードされるクラスローダーになります。(ここで図21.2を参照してください)

InjectingClassLoaderはコンストラクターで親を指定しないため、デフォルトで抽象ClassLoaderのコンストラクターになり、現在のコンテキストクラスローダーがInjectingClassLoaderの親として設定されます。したがって、親(古いコンテキストクラスローダー)はTestProxyを見つけることができるため、InjectingClassLoaderが取得する前に、常にクラスをロードします。

于 2010-05-12T19:39:59.897 に答える