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

(InjectingClassLoaderorg.apache.bcel.util를 확장하는 클래스입니다 .ClassLoader 부모에게 요청하기 전에 클래스의 수정 된 버전을로드해야합니다.

e는 "접근 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

Nit : "반사적으로". 컨텍스트 클래스 로더에 관한 질문에 혼란이 있다면 setContextClassLoader는 스레드가 설정해야하는 작업 (예 : SAXParser 만들기, JNDI 조회 수행 등)을 수행하지 않는 한 아무런 효과가 없다고 말할 수 있습니다. –

+0

수정 됨; 반사적으로 -> 반사적으로. 감사. –

1

여기서는 InjectingClassLoader가 중요하다고 생각합니다. 클래스 로딩 위임이 작동하는 방법 기억 - 계층의 둘 이상의 클래스 로더가 클래스를 찾을 수있는 경우 최상위 클래스 로더가로드됩니다. (그림 21.2 here 참조)

InjectingClassLoader는 생성자에서 부모를 지정하지 않으므로 추상 ClassLoader의 생성자가 기본값이되어 현재 컨텍스트 클래스 로더를 InjectingClassLoader의 부모로 설정합니다. 따라서 부모 (구 문맥 클래스 로더)는 TestProxy를 찾을 수 있기 때문에 InjectingClassLoader가 기회를 갖기 전에 항상 클래스를로드합니다.

+0

확인, 죄송합니다 ... 그것은으로 성가신 물건을하게되는 InjectingClassLoader이 (구현 modifyClass 방법 사용) org.apache.bcel.util.ClassLoader를 확장하는 클래스가하는 방식으로 관련이 클래스가로드되기 전에 AFAIK org.apache.bcel.util.ClassLoader는 부모 클래스 로더가 사용되기 전에 수정 된 클래스가로드되는 방식으로 클래스 로더 체인의 기본 동작을 재정의합니다. – Chris

+0

그냥 소스를 확인하고 org.apache.bcel.utilClassloader가 여전히 java.lang.ClassLoader를 확장합니다 ... – Greg

+1

기본 클래스 로더 생성자는 부모로 Thread.currentThread(). getSystemClassLoader()가 아닌 ClassLoader.getSystemClassLoader()를 사용합니다. 클래스 로더. –

관련 문제