2010-05-12 5 views
7

Я пытаюсь переключить загрузчик класса во время выполнения:Изменение загрузчика классов

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

I 'd хотел бы сделать результат «подхода 1» и «приблизить 2» точно таким же, но он выглядит как thread.setContextClassLoader (classLoader) ничего не делает, и «подход 2» всегда использует системный загрузчик классов (может быть определен сравнение переменных tcl и ccl во время отладки).

Можно ли создать все классы, загруженные новой веткой, используемой данным загрузчиком классов?

ответ

12

Анонимный класс, который вы создаете через 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(); 
+1

Нит: «Отражающе». Учитывая, что в вопросе о загрузчике класса контекста есть путаница, вы можете упомянуть, что setContextClassLoader не имеет эффекта, если поток не выполняет некоторую операцию, которая требует ее установки (например, создание SAXParser, выполнение JNDI-поиска и т. Д.). –

+0

Исправлено; рефлексивно -> рефлексивно. Благодарю. –

1

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

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

+0

ОК, извините ... Это актуально в пути, что InjectingClassLoader является классом расширения org.apache.bcel.util.ClassLoader (с реализованным методом modifyClass), что делает некоторые неприятные вещи с класс перед загрузкой. AFAIK org.apache.bcel.util.ClassLoader переопределяет поведение цепочек загрузчиков по умолчанию в том, как модифицированный класс загружается до использования родительского загрузчика классов. – Chris

+0

Просто проверил источник и org.apache.bcel.utilClassloader все еще расширяет java.lang.ClassLoader ... – Greg

+1

Конструктор загрузчика класса по умолчанию использует ClassLoader.getSystemClassLoader(), а не Thread.currentThread(). GetContextClassLoader(), как родительский класс загрузчика. –