혼란시키다
과거에는 소스 코드를 살펴보면 Thread.CurrentThread.getContextClassLoader ()를 사용하여 프레임 워크의 코드가 항상 발생하여 현재 스레드의 컨텍스트 클래스 로더를 가져 와서이 컨텍스트 클래스 로더를 사용하여 클래스를로드했습니다.
일반적으로 프로그램에 코드를 작성할 때 클래스를 동적으로로드하려면 일반적으로 클래스를 사용하여 필요한 클래스를로드합니다. 예를 들어, 가장 일반적인 것은 JDBC를 프로그래밍 할 때 class.forname ()을 사용하여 JDBC 드라이버를로드한다는 것입니다.
{return class.forname ( "oracle.jdbc.driver.oracledriver");} catch (classNotFoundException e) {// skip}그렇다면 클래스를로드하여 클래스를로드하는데 클래스를 찾을 수없는 경우 thread.currentthread.getContextLoader ()에서 얻은 클래스 로더를 사용하여 클래스를로드하려고하는 이유는 무엇입니까? 예를 들어 다음 코드가 발생할 수 있습니다.
{return class.forname (className);} catch (classNotFoundException e) {// skip} classLoader ctxClassLoader = thread.currentThread (). getContextClassLoader (); if (ctxclassloader! = null) {try {clazz = ctxclassloader.loadclass (classname); } catch (classNotFoundException e) {// skip}}여기서 대담한 부분은 Thread.CurrentThread.getContextLoader ()가 얻은 로더를 사용하여 클래스를로드하는 것입니다. 분명히 Class.forname ()로드시 사용되는 클래스 로더는 클래스가 thread.currentthread.getContextLoader ()에서 얻은 클래스 로더와 다를 수 있습니다. 그렇다면 차이가 발생하는 이유는 무엇입니까?
자바 클래스 로더
Thread.CurrentThread.getContextLoader ()가 사용하는이 클래스 로더를 사용하는 이유를 이해하기 전에 먼저 JVM에 사용되는 클래스 로더 (클래스 로더)를 이해해 봅시다.
기본적으로 JVM에는 세 가지 유형의 클래스 로더가 있습니다.
부트 스트랩 클래스 로더
부트 스트랩 클래스 로더 클래스 로더는 JDK에 내장 된 클래스 로더로 JDK 내부에 클래스를로드하는 데 사용됩니다. 부트 스트랩 클래스 로더는 rt.jar 패키지의 클래스와 같은 JDK에서 $ java_home/jre/lib 이하의 클래스를로드하는 데 사용됩니다. 부트 스트랩 클래스 로더는 JVM의 일부이며 일반적으로 기본 코드로 작성됩니다.
확장 클래스 로더
확장 클래스 로더 클래스 로더는 주로 JDK 확장 패키지로 클래스를로드하는 데 사용됩니다. 일반적으로 $ java_home/lib/ext의 패키지는이 클래스 로더를 통해로드 되며이 패키지의 클래스는 기본적으로 Javax로 시작합니다.
시스템 클래스 로더
시스템 클래스 로더 클래스 로더를 Application Class 로더 (AppClassLoader)라고도합니다. 이름에서 알 수 있듯이이 클래스 로더는 개발자가 일반적으로 작성하는 응용 프로그램 코드를로드하는 데 사용됩니다. 시스템 클래스 로더는 클래스 경로에 저장된 응용 프로그램 수준 클래스를로드하는 데 사용됩니다.
다음 코드는이 세 가지 클래스 로더를 나열합니다.
public class mainclass {public static void main (String [] args) {System.out.println (integer.class.getClassLoader ()); System.out.println (logging.class.getClassLoader ()); System.out.println (mainclass.class.getClassLoader ()); }}부트 스트랩 클래스 로더를 얻을 수있는 곳 영원히 널 값을 반환합니다.
null # bootstrap 클래스 로더 Sun.misc.launcher$extclassloader@5e2de80c # 확장 클래스 로더 sun.misc.launcher$ aappclasloader@18b4aac2 # 시스템 클래스 로더
부모 대표 모델
위에서 소개 된 세 가지 유형 로더는 분리되지 않았으며 계층 적 관계가 있습니다.
3 개의 클래스 로더는이 계층 적 관계를 통해 함께 작동하며 클래스로드를 담당합니다. 위의 계층 적 모델을 클래스 로더의 "부모 대표"모델이라고합니다. 상위 대의원 모델은 모든 클래스 로더에 최상위 부트 스트랩 클래스 로더를 제외한 부모 로더가 있어야합니다. 클래스 로더가 클래스를로드하면 먼저로드 된 캐시에 클래스가 있는지 확인하십시오. 그렇지 않은 경우, 부모 로더는 클래스를 먼저로드하도록 위임됩니다. 상위 로더는 요청이 최상위 부트 스트랩 클래스 로더에 도달 할 때까지 이전 하위 로더와 동일한 작업을 수행합니다. 상위 로더가 필요한 클래스를로드 할 수 없으면 하위 로더는 클래스를 자체적으로로드하려고합니다. 다음과 유사하게 작동합니다.
JDK의 클래스 로더에서 코드를 사용하여 부모 대의원 메커니즘의 구현을 볼 수 있습니다. 코드는 ClassLoader.loadClass ()에서 구현됩니다.
회전 클래스 <?> loadClass (문자열 이름, 부울 resolve)는 classNotFoundException 동기화 (getClassLoadingLock (name)) {// 클래스가 이미로드되었는지 확인 <?> c = findloadedClass (name); if (c == null) {long t0 = system.nanoTime (); try {if (parent! = null) {c = parent.loadclass (이름, false); } else {c = findBootStrapClassorNull (이름); }} catch (classNotFoundException e) {// 클래스를 찾을 수없는 경우 null 부모 클래스 로더에서 //를 찾을 수없는 경우 classNotFoundException} if (c == null) {// 아직 찾을 수없는 경우 findclass를 주문하여 클래스를 찾으십시오. long t1 = system.nanoTime (); c = findClass (이름); // 이것은 정의 클래스 로더입니다. 통계를 기록하십시오 Sun.misc.perfcounter.getParentDelegationTime (). AddTime (T1 -T0); sun.misc.perfcounter.getfindclasstime (). addelapsedtimefrom (t1); sun.misc.perfcounter.getfindclasses (). increment (); }} if (resolve) {resolveClass (c); } 반환 c; }학부모 대표단을 사용하여 클래스 로더를 구성하는 한 가지 장점은 안전합니다. 문자열 클래스를 스스로 정의하면이 문자열 클래스를 기본 Java에서 java.lang.string의 구현으로 바꾸기를 희망합니다.
우리는 클래스 경로 경로에 구현 한 문자열 클래스의 클래스 파일을 넣었습니다. 클래스 로더를 사용하여 구현 한 문자열 클래스를로드 할 때, 먼저 클래스 로더는 부모 로더에 요청을 위임하고 레이어를 통해 Bootstrap 클래스 로더는 마침내 Rt.jar 패키지에 문자열 유형을로드 한 다음 우리에게 항상 우리에게 반환합니다. 이 과정에서 클래스 로더는 클래스 경로에 배치 한 문자열 클래스를 무시합니다.
상위 대의원 메커니즘이 채택되지 않으면 시스템 클래스 로더는 ClassPath 경로에서 문자열 클래스 파일을 찾아 프로그램에로드하여 JDK의 문자열 구현이 덮어 쓰게됩니다. 따라서이 클래스 로더 의이 작업 방법은 Java 프로그램이 어느 정도 안전하고 안정적으로 실행될 수 있도록합니다.
스레드 컨텍스트 클래스 로더
위의 내용은 많은 클래스 로더 관련 콘텐츠에 대해 이야기하지만 오늘날의 주제, 스레드 컨텍스트 클래스 로더에 대해서는 여전히 이야기하지 않습니다.
이 시점에서 우리는 Java가 엄격한 상위 대의원 메커니즘에 따라 3 개의 유형 로더를 제공하고 콘서트에서 작동한다는 것을 이미 알고 있습니다. 표면적으로는 완벽 해 보이지만 클래스를로드 할 때 제한을 일으키는이 엄격한 부모 대표 메커니즘입니다.
보다 기본적인 프레임 워크가 응용 프로그램 수준 클래스를 사용해야하는 경우, 현재 프레임 워크에서 사용하는 클래스 로더를로드 할 수있을 때이 클래스가로드 된 경우에만 이러한 클래스를 사용할 수 있습니다. 다시 말해, 현재 클래스 로더의 클래스 로더를 사용할 수 없습니다. 이 제한은 클래스 로딩 요청의 대표가 일방 통행이기 때문에 부모 대의원 메커니즘에 의해 발생합니다.
사례는 많지 않지만 여전히 그러한 요구가 있습니다. 전형적인 JNDI 서비스. JNDI는 쿼리 리소스에 대한 인터페이스를 제공하지만 특정 구현은 다른 제조업체가 구현합니다. 현재 JNDI의 코드는 JVM의 부트 스트랩 클래스 로더에 의해로드되지만 특정 구현은 사용자가 제공 한 JDK 이외의 코드이므로 시스템 클래스 로더 또는 기타 사용자 정의 클래스 로더에서만로드 할 수 있습니다. 상위 대의원 메커니즘에 따라 JNDI는 JNDI의 SPI 구현을 얻을 수 없습니다.
이 문제를 해결하기 위해 스레드 컨텍스트 클래스 로더가 소개됩니다. java.lang.thread의 setcontextClassLoader ()는 setContextClassLoader ()의 setContextClassLoader ()의 setContextClassLoader () (설정하지 않으면 기본적으로 상위 스레드에서 상속됩니다. 프로그램이 설정되지 않으면 시스템 클래스 로더로 기본값이 표시됩니다). 스레드 컨텍스트 클래스 로더를 사용하면 애플리케이션이 응용 프로그램에서 사용하는 클래스 로더를 java.lang.thread.setContxtClassLoader ()를 통해 최상위 클래스 로더를 사용하는 코드로 전달할 수 있습니다. 예를 들어, 위의 JNDI 서비스는이 방법을 사용하여 SPI 구현을로드하고 필요한 SPI 구현 클래스를 얻을 수있는 클래스 로더를 얻을 수 있습니다.
스레드 클래스 로더를 도입하는 것은 실제로 상위 대의원 메커니즘의 파괴라는 것을 알 수 있지만 클래스 로딩의 유연성을 제공합니다.
의심을 해결하십시오
처음으로, 프레임 워크 외부의 사용자가 구현 한 클래스를로드하기 위해, 이러한 클래스는 프레임 워크에서 사용하는 클래스 로더를 통해로드되지 않을 수 있습니다. 클래스 로더의 상위 대의원 모델을 우회하기 위해 Thread.getContextClassLoader ()는 이러한 클래스를로드하는 데 사용됩니다.
위는이 기사의 모든 내용입니다. 모든 사람의 학습에 도움이되기를 바랍니다. 모든 사람이 wulin.com을 더 지원하기를 바랍니다.