2010-05-12 12 views
7

próbuję przełączyć ładującego klasy w czasie wykonywania:Zmień 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(); 
    } 
} 

oraz:

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

(InjectingClassLoader jest klasą rozszerzenie org.apache.bcel.util .ClassLoader, który powinien załadować zmodyfikowane wersje klas przed zapytaniem, czy jest dla nich rodzicem)

Chciałbym e, aby wynik "podejścia 1" i "podejścia 2" był dokładnie taki sam, ale wygląda na to, że thread.setContextClassLoader (classLoader) nic nie robi, a "podejście 2" zawsze używa systemowego programu ładującego klasy (można to ustalić porównując tcl i zmienne ccl podczas debugowania).

Czy to możliwe, aby wszystkie klas załadowane przez nowe zastosowanie gwintu danego classloader?

Odpowiedz

12

Anonimowa klasa tworzona przez new Thread("test") { ... } ma niejawne odwołanie do otaczającej instancji. Literały klas w tej anonimowej klasie będą ładowane przy użyciu klasy ClassLoader klasy otaczającej.

Aby ten test zadziałał, należy wyciągnąć poprawną implementację Runnable i wczytać ją za pomocą pożądanej klasy ClassLoader; następnie przekaż to jawnie do wątku. Coś jak:

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

Nit: "refleksyjnie". Biorąc pod uwagę, że w pytaniu dotyczącym modułu ładującego klasy kontekst jest zamieszanie, można wspomnieć, że setContextClassLoader nie ma żadnego efektu, chyba że wątek wykonuje pewną operację, która wymaga jej ustawienia (np. Tworzenie SAXParser, wykonywanie wyszukiwania JNDI itd.). –

+0

Naprawiono; refleksyjnie -> refleksyjnie. Dzięki. –

1

Myślę, że InjectingClassLoader może być tutaj ważna. Zapamiętaj, jak działa delegowanie klasowe - jeśli więcej niż jeden program ładujący klasy w hierarchii może znaleźć klasę, najpotężniejszy będzie ładujący klasy. (Patrz rysunek 21.2 here)

Od InjectingClassLoader nie określa rodzica w jego konstruktora, będzie domyślnie konstruktora w abstrakcyjnej ClassLoader, która określi aktualną classloader kontekście jako rodzica InjectingClassLoader użytkownika. Dlatego, ponieważ rodzic (stary moduł ładujący klasy) może znaleźć TestProxy, zawsze ładuje klasę przed InjectingClassLoader dostaje szansę.

+0

Ok, przepraszam ... Jest to istotne w sposób, że InjectingClassLoader to klasa rozszerzenie org.apache.bcel.util.ClassLoader (z realizowanego metodą modifyClass), który sprawia, że ​​niektóre z nieprzyjemnych rzeczy klasa przed załadowaniem. AFAIK org.apache.bcel.util.ClassLoader zastępuje domyślne zachowanie łańcucha modułów ładujących klasy w sposób, w jaki zmodyfikowana klasa jest ładowana przed użyciem nadrzędnego programu ładującego klasy. – Chris

+0

Tylko sprawdzone źródła i org.apache.bcel.utilClassloader nadal rozszerza java.lang.Classloader ... – Greg

+1

Domyślny konstruktor klasy ładowarka korzysta ClassLoader.getSystemClassLoader(), nie Thread.currentThread(). GetContextClassLoader(), jako rodzica program ładujący klasy. –

Powiązane problemy