Изменение 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();
    }
}

и:

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. ClassLoder, который должен загрузить измененные версии классов прежде, чем попросить, чтобы это был родитель для них),

Я хотел бы сделать результат из "подхода 1", и "приближаются 2" точно то же, но похоже, что thread.setContextClassLoader (classLoader) ничего не делает, и "подход 2" всегда использование система classloader (может быть определен путем сравнения tcl и ccl переменные при отладке).

Действительно ли возможно сделать все классы загруженными данным classloader использования нового потока?

11
задан Chris 13 May 2010 в 20:14
поделиться

2 ответа

Анонимный класс, который вы создаете через new Thread("test") { ... } имеет неявную ссылку на вложенный экземпляр. Литералы классов в этом анонимном классе будут загружаться с помощью ClassLoader объемлющего класса.

Для того, чтобы этот тест сработал, вы должны найти подходящую реализацию Runnable и загрузить его рефлексивно, используя нужный ClassLoader; затем передать его явно в поток. Something like:

    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();
16
ответ дан 3 December 2019 в 07:11
поделиться

Я думаю, что InjectingClassLoader может быть важен здесь. Помните, как работает делегирование загрузки классов - если более одного загрузчика классов в вашей иерархии могут найти класс, то загружаться будет самый верхний загрузчик. (См. рисунок 21.2 здесь)

Поскольку InjectingClassLoader не указывает родителя в своем конструкторе, он по умолчанию обратится к конструктору абстрактного ClassLoader, который установит текущий контекстный загрузчик классов в качестве родителя InjectingClassLoader. Поэтому, поскольку родитель (старый контекстный загрузчик классов) может найти TestProxy, он всегда загрузит класс до того, как InjectingClassLoader получит шанс.

1
ответ дан 3 December 2019 в 07:11
поделиться
Другие вопросы по тегам:

Похожие вопросы: