스레드의 컨텍스트 클래스 로더와 일반 클래스 로더의 차이점


242

스레드의 컨텍스트 클래스 로더와 일반 클래스 로더의 차이점은 무엇입니까?

경우 즉, Thread.currentThread().getContextClassLoader()getClass().getClassLoader()하나가 사용되는 다른 클래스 로더 객체를 반환?

답변:


151

각 클래스는 자체 클래스 로더를 사용하여 다른 클래스를로드합니다. 경우에 따라서 ClassA.class참조는 ClassB.class다음 ClassB의 클래스 로더의 클래스 경로에 있어야합니다 ClassA, 또는 그 부모.

스레드 컨텍스트 클래스 로더는 현재 스레드의 현재 클래스 로더입니다. 클래스에서 객체를 만든 ClassLoaderC다음이 소유 한 스레드로 전달할 수 있습니다 ClassLoaderD. 이 경우 Thread.currentThread().getContextClassLoader()자체 클래스 로더에서 사용할 수없는 자원을로드하려는 경우 오브젝트를 직접 사용해야합니다.


1
왜 로더 ClassB의 클래스 경로 ClassA(또는 ClassA로더의 부모) 에 있어야 한다고 말 합니까? 그것은 가능하지 않다 ClassA재정에의 로더 loadClass()가 성공적으로로드 할 수 있도록, ClassB경우에도 ClassB그 클래스 경로에 있지?
Pacerier

8
실제로 모든 클래스 로더에 클래스 경로가있는 것은 아닙니다. "ClassB는 ClassA의 클래스 로더의 클래스 경로에 있어야합니다"라고 썼을 때, "ClassB는 ClassA의 클래스 로더에 의해로드 가능해야 함"을 의미했습니다. 시간의 90 %는 같은 의미입니다. 그러나 URL 기반 클래스 로더를 사용하지 않는 경우 두 번째 경우 만 적용됩니다.
David Roussel

" ClassA.class참조 ClassB.class" 란 무슨 뜻 입니까?
jameshfisher

1
ClassA에 ClassB에 대한 import 문이 있거나 ClassA에 ClassB 유형의 로컬 변수가있는 메소드가있는 경우 클래스 B가 아직로드되지 않은 경우로드됩니다.
David Roussel

내 문제는이 주제와 관련이 있다고 생각합니다. 내 용질에 대해 어떻게 생각하십니까? 나는 그것이 좋은 패턴이 아니라는 것을 알고 있지만 그것을 고치는 방법에 대한 다른 아이디어가 없습니다 : stackoverflow.com/questions/29238493/…
Marcin Erbel

109

이것은 원래 질문에 대답하지 않지만 질문의 순위가 높고 ContextClassLoader쿼리에 연결되어 있으므로 컨텍스트 클래스 로더를 사용해야하는 시점과 관련된 질문에 대답하는 것이 중요하다고 생각합니다. 짧은 대답 : 컨텍스트 클래스 로더를 사용하지 마십시오 ! 그러나 매개 변수 getClass().getClassLoader()가없는 메소드를 호출 해야 할 때 설정하십시오 ClassLoader.

한 클래스의 코드가 다른 클래스를로드하도록 요청하면 사용할 올바른 클래스 로더는 호출자 클래스와 동일한 클래스 로더입니다 (예 :) getClass().getClassLoader(). 99.9 %의 시간 작동하는 방식 입니다. 새 클래스의 인스턴스를 처음 구성하거나 정적 메소드를 호출하거나 정적 필드에 액세스 할 때 JVM 자체 가 수행하는 방식이기 때문 입니다.

리플렉션을 사용하여 클래스를 만들려면 (예 : 구성 가능한 명명 된 클래스를 역 직렬화 또는로드 할 때) 리플렉션을 수행하는 라이브러리는 항상 응용 프로그램ClassLoader 에서 매개 변수 로을 수신하여 사용할 클래스 로더를 응용 프로그램에 요청 해야 합니다. 응용 프로그램 (생성해야하는 모든 클래스를 알고 있음)이 전달해야합니다 getClass().getClassLoader().

클래스 로더를 얻는 다른 방법은 올바르지 않습니다. 라이브러리가 Thread.getContextClassLoader(), 등의 해킹을 사용 sun.misc.VM.latestUserDefinedLoader()하거나 sun.reflect.Reflection.getCallerClass()API의 결함으로 인한 버그 인 경우 기본적으로 API를 Thread.getContextClassLoader()디자인 한 사람 이 매개 변수로 사용하는 ObjectInputStream것을 잊어 버렸기 때문에 존재 하며이 ClassLoader실수는 오늘날까지 Java 커뮤니티를 괴롭 혔습니다.

즉, 많은 JDK 클래스는 몇 가지 해킹 중 하나를 사용하여 사용할 클래스 로더를 추측합니다. 일부는 ContextClassLoader(공유 스레드 풀에서 다른 앱을 실행할 때 실패하거나을 떠날 ContextClassLoader null때 실패) 일부는 스택을 걷습니다 (클래스의 직접 호출자가 라이브러리 인 경우 실패). 일부는 시스템 클래스 로더를 사용합니다 ( CLASSPATH또는 클래스의 부트 스트랩 클래스 로더 만 사용하도록 문서화되어있는 한 괜찮습니다. ) 일부는 예측할 수없는 위의 기술 조합을 사용하여 혼란을 야기합니다. 이로 인해 치아가 많이 울리고 찢어졌습니다.

이러한 API를 사용할 때는 먼저 클래스 로더를 매개 변수로 허용하는 메소드의 과부하를 찾으십시오 . 합리적인 방법이없는 경우 ContextClassLoaderAPI 호출 전을 설정 한 후 나중에 재설정하십시오.

ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
try {
    Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
    // call some API that uses reflection without taking ClassLoader param
} finally {
    Thread.currentThread().setContextClassLoader(originalClassLoader);
}

5
예, 이것이 제가 질문하는 사람에게 지적한 답변입니다.
Marko Topolnik

6
이 답변은 클래스 로더를 사용하여 클래스를로드하는 데 중점을 둡니다 (반사 또는 유사를 통해 클래스를 인스턴스화하기 위해). 다른 용도로 사용됩니다 (실제로 개인적으로 사용했던 유일한 목적)는 리소스를로드하는 것입니다. 동일한 원칙이 적용되거나 호출자 클래스 로더가 아닌 컨텍스트 클래스 로더를 통해 자원을 페치하려는 상황이 있습니까?
Egor Hans

90

javaworld.com에는 차이점 => 어떤 ClassLoader를 사용 해야하는지 설명하는 기사가 있습니다.

(1)

스레드 컨텍스트 클래스 로더는 클래스 로딩 위임 체계 주변의 백도어를 제공합니다.

예를 들어, JNDI를 보자 : 내장은 rt.jar의 부트 스트랩 클래스 (J2SE 1.3로 시작)로 구현되지만 이러한 핵심 JNDI 클래스는 독립 벤더가 구현하고 응용 프로그램의 -classpath에 배치 될 수있는 JNDI 제공자를로드 할 수 있습니다. 이 시나리오에서는 부모 클래스 로더 (이 경우 가장 중요한 클래스)를 호출하여 자식 클래스 로더 중 하나 (예 : 시스템 클래스)에 보이는 클래스를로드합니다. 일반적인 J2SE 위임은 작동하지 않으며, 해결 방법은 핵심 JNDI 클래스가 스레드 컨텍스트 로더를 사용하도록하여 적절한 위임과 반대 방향으로 클래스 로더 계층 구조를 통해 효과적으로 "터널링"하는 것입니다.

(2) 같은 출처에서 :

이 혼란은 아마도 한동안 자바와 함께있을 것이다. 어떤 종류의 동적 리소스 로딩이있는 J2SE API를 사용하고 어떤 로딩 전략을 사용하는지 추측하십시오. 샘플링은 다음과 같습니다.

  • JNDI는 컨텍스트 클래스 로더를 사용합니다.
  • Class.getResource () 및 Class.forName ()은 현재 클래스 로더를 사용합니다.
  • JAXP는 컨텍스트 클래스 로더를 사용합니다 (J2SE 1.4 기준).
  • java.util.ResourceBundle은 호출자의 현재 클래스 로더를 사용합니다.
  • java.protocol.handler.pkgs 시스템 특성을 통해 지정된 URL 프로토콜 핸들러는 부트 스트랩 및 시스템 클래스 로더에서만 검색됩니다.
  • Java Serialization API는 기본적으로 호출자의 현재 클래스 로더를 사용합니다.

해결 방법은 핵심 JNDI 클래스가 스레드 컨텍스트 로더를 사용하도록하는 것입니다. . 따라서이 부모 클래스 로더를 스레드의 컨텍스트 클래스 로더에 설정 한 경우에도 어떻게 부모를 사용하여로드 할 수 있습니까?
Sunny Gupta

6
@ SAM, 제안 된 해결 방법은 실제로 말한 것과 실제로 반대입니다. bootstrap컨텍스트 클래스 로더로 설정되는 상위 클래스 로더가 아니라로 설정되는 하위 system클래스 경로 클래스 로더입니다 Thread. JNDI클래스는 다음 사용해야하고있다 Thread.currentThread().getContextClassLoader()클래스 패스에서 사용할 수있는 JNDI 구현 클래스를로드 할 수 있습니다.
Ravi Thapliyal

"정상 J2SE 위임이 작동하지 않습니다."왜 작동하지 않는지 알 수 있습니까? 부트 스트랩 ClassLoader는 rt.jar에서만 클래스를로드 할 수 있고 응용 프로그램의 -classpath에서 클래스를로드 할 수 없기 때문에? 권리?
YuFeng Shen

37

@David Roussel 답변에 추가하여 여러 클래스 로더가 클래스를로드 할 수 있습니다.

클래스 로더 작동 방식을 이해할 수 있습니다.

javarevisited의 javin paul 블로그에서 :

여기에 이미지 설명을 입력하십시오

여기에 이미지 설명을 입력하십시오

ClassLoader 세 가지 원칙을 따릅니다.

위임 원칙

클래스는 필요할 때 Java로로드됩니다. Abc.class라는 응용 프로그램 특정 클래스가 있다고 가정하면이 클래스를로드하는 첫 번째 요청은 응용 프로그램 클래스 로더에 제공됩니다.

  • Bootstrap ClassLoader 는 rt.jar에서 표준 JDK 클래스 파일을로드하고 Java의 모든 클래스 로더의 상위입니다. 부트 스트랩 클래스 로더에는 부모가 없습니다.

  • 확장 클래스 로더는 클래스 로딩 요청을 부모, 부트 스트랩에 위임하고 실패하면 클래스 형식 jre / lib / ext 디렉토리 또는 java.ext.dirs 시스템 특성이 가리키는 기타 디렉토리를로드합니다.

  • 시스템 또는 애플리케이션 클래스 로더 이며 JAR 내의 Manifest 파일의 클래스 경로 속성, CLASSPATH 환경 변수, -classpath 또는 -cp 명령 행 옵션에서 애플리케이션 특정 클래스를로드합니다.

  • 응용 프로그램 클래스 로더Extension ClassLoader 의 자식이며 sun.misc.Launcher$AppClassLoader클래스 로 구현됩니다 .

참고 : 대부분 C로 네이티브 언어로 구현되는 Bootstrap 클래스 로더를 제외하고 모든 Java 클래스 로더는을 사용하여 구현됩니다 java.lang.ClassLoader.

가시성 원리

가시성 원칙에 따라 Child ClassLoaderParent ClassLoader에 의해로드 된 클래스를 볼 수 있지만 그 반대도 마찬가지입니다.

고유성 원리

이 원칙에 따라 Parent에 의해로드 된 클래스는 Child ClassLoader에 의해 다시로드되지 않아야합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.