몇일전 친구가 면접때 클래스 로딩에 대한 질문을 받았는데, 클래스 로딩에 관련된 질문에 평소대로 대답을 했고, 면접 질문을 조금만 외우면 대처가 가능하더군요. .
하지만 좀 더 깊게, 조금만 더 깊게 물어보면 많은 사람을 시원하게 할 수 있어요.
예: tomcat 클래스 로더가 부모 위임 모델을 위반하는 이유는 무엇입니까?
면접시 이런 상황에 직면하지 않도록 이 글에서는 이 문제를 정리하겠습니다.
4부분으로 나누어 논의하겠습니다.
Tomcat 클래스 로딩을 공부하기 전에 Java 기본 클래스 로더를 검토하거나 통합해야 한다고 생각합니다. 처음 JVM을 배우기 시작했을 때 클래스 로딩 메커니즘에 대해 헷갈렸는데, 이 기회를 빌어 여러분과 공유하고 싶습니다.
코드 컴파일 결과를 로컬 기계어 코드에서 바이트코드로 변환하는 것은 저장 형식에서는 작은 단계이지만 프로그래밍 언어 개발에서는 큰 단계입니다.
Java 가상머신은 클래스를 기술하는 데이터를 Class 파일에서 메모리로 로딩하고, 데이터를 검증하고, 변환, 파싱, 초기화를 거쳐 최종적으로 가상머신에서 직접 사용할 수 있는 자바 타입을 형성하는 것입니다. 가상 머신의 클래스 로딩 메커니즘.
가상 머신 설계 팀은 Java 가상 머신 외부의 클래스 로딩 단계에서 "클래스의 정규화된 이름을 통해 이 클래스를 설명하는 바이너리 바이트 스트림 얻기" 작업을 구현하여 애플리케이션이 이를 수행하는 방법을 결정할 수 있도록 했습니다. .필요한 수업을 받으세요. 이 작업을 구현하는 코드 모듈을 "클래스 로더"라고 합니다.
클래스 로더는 클래스 로딩 작업을 구현하는 데에만 사용되지만 Java 프로그램에서 클래스 로더의 역할은 클래스 로딩 단계에만 국한되지 않습니다. 모든 클래스에 대해 이를 로드하는 클래스 로더와 클래스 자체는 Java 가상 머신에서 고유성을 설정해야 합니다. 각 클래스 로더에는 독립적인 클래스 네임스페이스가 있습니다.
이 문장은 더 일반적으로 표현할 수 있습니다. 두 클래스가 "동일"인지 비교하는 것은 두 클래스가 동일한 클래스 로더에 의해 로드되는 경우에만 의미가 있습니다. 그렇지 않으면 두 클래스가 동일한 클래스 파일에 의해 로드됩니다. 동일한 가상 머신을 로드하는 클래스 로더가 다른 한 두 클래스는 동일할 수 없습니다.
1. Java 가상 머신의 관점에서 보면 클래스 로더는 두 가지뿐입니다. 하나는 C++ 언어를 사용하는 Bootstrap ClassLoader입니다. 구현(HotSpot만 해당)은 일부입니다. 다른 하나는 가상 머신과 독립적으로 Java 언어로 구현되고 모두 추상 클래스에서 상속되는 다른 모든 클래스 로더입니다.java.lang.ClassLoader
.
sun.misc.Launcher$ExtClassLoader
구현. JAVA_HOME/lib/ext 디렉토리 또는 java.ext.dirs 시스템 변수에 의해 지정된 경로에 있는 모든 클래스 라이브러리를 혼합하는 역할을 합니다. 개발자는 확장 클래스 로더를 직접 사용할 수 있습니다. sun.misc.Launcher$ExtClassLoader
实现,它负责夹杂JAVA_HOME/lib/ext 目录下的,或者被java.ext.dirs 系统变量所指定的路径中的所有类库。开发者可以直接使用扩展类加载器。sun.misc.Launcher$AppClassLoader
Application ClassLoader:
이 클래스 로더는sun.misc.Launcher$AppClassLoader
구현. 이 클래스 로더는 ClassLoader의 getSystemClassLoader 메소드의 반환 값이므로 시스템 클래스 로더가 됩니다. 사용자 클래스 경로(ClassPath)에 지정된 클래스 라이브러리를 로드하는 역할을 담당합니다. 개발자는 이 클래스 로더를 직접 사용할 수 있습니다. 애플리케이션이 자체 클래스 로더를 정의하지 않는 경우 이는 일반적으로 프로그램의 기본 클래스 로더입니다. picture
(부모 위임 모드)이라고 합니다. ). 상위 위임 모델에서는 최상위 시작 클래스 로더 외에도 다른 모든 클래스 로더가 자체 상위 클래스 로더에 의해 로드되어야 합니다. 여기에서 클래스 로더 간의 상위-하위 관계는 일반적으로 상속에 의해 실현되지 않습니다. . 이들은 모두 구성 관계를 사용하여 상위 로더의 코드를 재사용합니다. 🎜참고: 인터뷰 중에 면접관은 해당 클래스 로더가 어떤 디렉터리를 로드하는지 물을 수도 있습니다. 🎜🎜🎜클래스 로더의 상위 위임 모델은 JDK1.2에서 도입되었으며 이후의 모든 Java 프로그램에서 널리 사용됩니다. 그러나 필수 제약 모델은 아니지만 Java 디자이너가 클래스 로더 구현을 권장합니다. 🎜그림에서 각 클래스 로더 간의 관계를
클래스 로더의 부모 위임 모델
상위 위임 모델의 작동 프로세스는 다음과 같습니다. 클래스 로더가 클래스 로딩 요청을 받으면 먼저 클래스 자체를 로드하려고 시도하지 않고 요청을 상위 클래스 로더에 위임하여 완료합니다. 이는 클래스 로더의 모든 레벨에 적용됩니다. 따라서 모든 로딩 요청은 결국 상위 레벨 시작 클래스 로더로 전송되어야 합니다. 요청을 완료할 수 없다고 피드백하는 경우에만(필요한 요청이 해당 검색 범위에서 발견되지 않음) ) 클래스), 서브로더는 자체적으로 로드를 시도합니다.
상위 위임 모델을 사용하지 않고 각 클래스 로더가 이를 자체적으로 로드하는 경우, 사용자가 java.lang.Object라는 클래스를 작성하여 프로그램의 ClassPath에 넣으면 시스템은 여러 개의 서로 다른 Object 클래스를 가지게 됩니다. Java 유형 시스템의 가장 기본적인 동작은 보장될 수 없습니다. 응용 프로그램도 복잡해집니다.
는 매우 간단합니다. 모든 코드는 java.lang.ClassLoader
의 loadClass 메소드에 있으며 코드는 다음과 같습니다.
논리는 명확하고 이해하기 쉽습니다. 먼저 로드되었는지 확인하세요. 그렇지 않은 경우 로더의 상위 로더 LoadClass 메서드를 호출합니다. 상위 로더가 비어 있으면 기본적으로 시작 클래스 로더가 상위 로더로 사용됩니다. 상위 클래스가 로드되지 않으면 ClassNotFoundException 예외가 발생하고 자체 findClass 메서드를 호출하여 이를 로드합니다.
우리는 방금 부모 위임 모델이 필수 제약 모델이 아니라 제안된 클래스 로더 구현이라고 말했습니다. Java 세계의 대부분의 클래스 로더는 이 모델을 따르지만 예외가 있습니다. 지금까지 상위 위임 모델은 대규모로 세 번 "깨졌습니다".
처음: 부모 위임 모델이 등장하기 전------JDK1.2 출시 전.
두 번째 : 본 모델 자체의 결함으로 인한 현상입니다. 상위 위임 모델은 각 클래스 로더의 기본 클래스를 통합하는 문제를 잘 해결한다고 합니다(기본 클래스일수록 상위 로더가 로드함). 기본 클래스를 "기본"이라고 부르는 이유는 다음과 같습니다. 항상 사용자 코드에 의해 호출되는 API이지만 기본 클래스가 사용자 코드를 호출하면 어떻게 될까요?
불가능하지 않습니다. 일반적인 예는 JNDI 서비스입니다. JNDI는 이제 Java의 표준 서비스입니다. 해당 코드는 시작 클래스 로더(JDK1.3에 있는 rt.jar)에 의해 로드되지만 독립적인 서비스에 의해 호출되고 구현되어야 합니다. 그리고 애플리케이션의 ClassPath 아래에 JNDI 인터페이스 공급자(SPI, Service Provider Interface) 코드를 배포하지만 시작 클래스 로더가 이러한 코드를 "알는" 것은 불가능합니다. 이 클래스는 rt.jar
, 그러나 시작 클래스 로더를 다시 로드해야 합니다. 어떻게 하나요? rt.jar
中,但是启动类加载器又需要加载。怎么办呢?
为了解决这个问题,Java设计团队只好引入了一个不太优雅的设计:线程上下文类加载器(Thread Context ClassLoader)。这个类加载器可以通过java.lang.Thread
java.lang.Thread code> 클래스의 setContextClassLoader 메소드가 설정됩니다. 스레드가 생성될 때 설정되지 않은 경우 상위 스레드에서 하나를 상속합니다. 애플리케이션의 전역 범위에서 너무 많이 설정하지 않으면 이 클래스 로더는 기본적으로 애플리케이션 클래스 로더로 설정됩니다. <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;margin-top: 1px;margin-bottom: 1px;"></p>안녕하세요, 스레드 컨텍스트 로더를 사용하면 JNDI 서비스는 이 스레드 컨텍스트 로더를 사용하여 필요한 SPI 코드를 로드합니다. 즉, 상위 클래스 로더는 하위 클래스 로더에게 클래스 로딩 작업을 완료하도록 요청합니다. 이 동작은 실제로 열립니다. 클래스 로더를 역으로 사용하기 위해 부모 위임 모델의 계층 구조를 상향 조정했는데, 이는 실제로 부모 위임 모델의 일반 원칙을 위반하는 것입니다. 하지만 이에 대해 할 수 있는 일은 없습니다. Java에서 SPI와 관련된 모든 로딩 작업은 기본적으로 이 방법을 사용합니다. 예를 들어 JNDI, JDBC, JCE, JAXB, JBI 등이 있습니다. <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;margin-top: 1px;margin-bottom: 1px;"><strong style="font-weight: bold;color: black;"></strong>세 번째: 다시 시작하지 않고 기능을 추가하거나 빼는 것을 의미하는 핫 플러깅, 핫 배포 및 모듈화를 달성하려면 클래스 로더와 함께 모듈만 교체하면 핫 코드 교체가 가능합니다. </p>
<p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;margin-top: 1px;margin-bottom: 1px;"></p>책에는 다음과 같은 내용도 나와 있습니다. <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;margin-top: 1px;margin-bottom: 1px;"></p>Java 프로그램에는 기본적으로 공통된 의견이 있습니다. OSGI의 클래스 로더 사용은 배울 가치가 있습니다. OSGI 구현을 이해하는 것은 클래스 로더의 본질을 익히는 것으로 간주할 수 있습니다. <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;margin-top: 1px;margin-bottom: 1px;"></p>멋져요! ! ! <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;margin-top: 1px;margin-bottom: 1px;"></p>이제 우리는 기본적으로 Java 기본 클래스 로딩의 작동 원리를 이해했으며 상위 위임 모델도 알고 있습니다. 너무 많이 말하면서 나는 Tomcat에 대해 거의 잊어버렸습니다. 우리의 주제는 Tomcat 로더가 부모 위임 모델을 위반하는 이유입니다. <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;margin-top: 1px;margin-bottom: 1px;"></p>Tomcat 클래스 로더에 대해 이야기해 보겠습니다. <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 20px;"></h3>4. Tomcat의 클래스 로더는 어떻게 설계되었나요? <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;margin-top: 1px;margin-bottom: 1px;"></p>먼저 질문해 보겠습니다. <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;margin-top: 1px;margin-bottom: 1px;"><strong style="font-weight: bold;color: black;"></strong>Tomcat이 기본 클래스 로딩 메커니즘을 사용해도 괜찮나요? </p>🎜<p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;margin-top: 1px;margin-bottom: 1px;">생각해 봅시다. Tomcat은 웹 컨테이너이므로 해결해야 할 문제는 무엇입니까? </p>
<ul class="list-paddingleft-1" data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;list-style-type: disc;">
<li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;">웹 컨테이너는 두 개의 애플리케이션을 배포해야 할 수 있으며, 서로 다른 애플리케이션은 동일한 타사 클래스의 서로 다른 버전에 의존할 수 있습니다. 동일한 서버에는 동일한 클래스 라이브러리의 복사본이 하나만 있어야 하므로 각 애플리케이션의 클래스 라이브러리가 서로 독립적이고 격리되어 있는지 확인해야 합니다. </section></li>
<li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;">동일한 웹 컨테이너에 배포된 동일한 클래스 라이브러리와 동일한 버전을 공유할 수 있습니다. 그렇지 않고 서버에 10개의 애플리케이션이 있으면 동일한 클래스 라이브러리의 복사본 10개가 가상 머신에 로드되어야 하는데 이는 말도 안되는 일입니다. </section></li>
<li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;">웹 컨테이너에는 애플리케이션의 클래스 라이브러리와 혼동할 수 없는 자체 종속 클래스 라이브러리도 있습니다. 보안상의 이유로 컨테이너의 클래스 라이브러리는 프로그램의 클래스 라이브러리와 격리되어야 합니다. </section></li>
<li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;">웹 컨테이너는 jsp 수정을 지원해야 합니다. jsp 파일은 가상 머신에서 실행되기 전에 결국 클래스 파일로 컴파일되어야 한다는 것을 알고 있습니다. 그러나 프로그램이 완료된 후에 jsp를 수정하는 것이 일반적입니다. 그렇지 않으면 무슨 소용이 있겠습니까? 따라서 웹 컨테이너는 재시작 없이 jsp 수정을 지원해야 합니다. </section></li>
</ul>
<p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;margin-top: 1px;margin-bottom: 1px;"><strong style="font-weight: bold;color: black;"> 질문을 다시 살펴보겠습니다. Tomcat이 기본 클래스 로딩 메커니즘을 사용해도 괜찮을까요? </strong></p>
<p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;margin-top: 1px;margin-bottom: 1px;">답은 '아니오'입니다. 왜? 첫 번째 문제를 살펴보겠습니다. 기본 클래스 로더 메커니즘을 사용하면 동일한 클래스 라이브러리의 두 가지 다른 버전을 로드할 수 없습니다. 기본 누산기는 이름에 관계없이 정규화된 클래스에만 관심을 갖습니다. 복사. </p>
<p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;margin-top: 1px;margin-bottom: 1px;">두 번째 질문은 기본 클래스 로더의 책임이 고유성을 보장하는 것이기 때문에 구현할 수 있다는 것입니다. 세 번째 질문도 첫 번째 질문과 동일합니다. 네 번째 질문을 다시 살펴보겠습니다. jsp 파일(포스터에서 지정한 이름)의 핫 수정을 어떻게 구현하는지 생각해 보겠습니다. jsp 파일은 실제로 클래스 파일이므로 수정되었더라도 클래스 이름은 여전히 입니다. 동일하게, 클래스 로더는 메소드 영역에 이미 존재하는 jsp를 검색하는 경우 수정된 jsp를 다시 로드하지 않습니다. </p>
<p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;margin-top: 1px;margin-bottom: 1px;">그럼 우리는 어떻게 해야 할까요? 이 jsp 파일의 클래스 로더를 직접 제거할 수 있으므로 각 jsp 파일이 고유한 클래스 로더에 해당한다고 생각해야 합니다. jsp 파일이 수정되면 jsp 클래스 로더가 직접 언로드됩니다. 클래스 로더를 다시 작성하고 jsp 파일을 다시 로드하십시오. </p>
<h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 18px;">Tomcat은 고유한 클래스 로딩 메커니즘을 어떻게 구현합니까? </h4>
<p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;margin-top: 1px;margin-bottom: 1px;">그럼 Tomcat은 어떻게 구현하나요? 멋진 Tomcat 팀이 이미 디자인했습니다. 그들의 디자인 도면을 살펴보겠습니다: </p>
<figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img src="https://img.php.cn/upload/article/001/273/727/f346c0c0e7dec3ae08cda6afa5b8e6eb-2.png" alt="인터뷰어: Tomcat 클래스 로더가 부모 위임 모델을 위반하는 이유는 무엇입니까?" ><figcaption style="max-width:90%">Picture</figcaption></figure><p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;margin-top: 1px;margin-bottom: 1px;">처음 3개의 클래스 로딩이 기본 클래스 로딩과 일치한다는 것을 알 수 있습니다. CommonClassLoader, CatalinaClassLoader, SharedClassLoader 및 WebappClassLoader는 각각 Tomcat의 자체 클래스 로더입니다. 로딩 경로는 현재 웹앱에만 표시됩니다. <code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">/common/*
、/server/*
、/shared/*
(在tomcat 6之后已经合并到根目录下的lib目录下)和/WebApp/WEB-INF/*
中的Java类库。其中WebApp类加载器和Jsp类加载器通常会存在多个实例,每一个Web应用程序对应一个WebApp类加载器,每一个JSP文件对应一个Jsp类加载器。
commonLoader
:Tomcat最基本的类加载器,加载路径中的class可以被Tomcat容器本身以及各个Webapp访问;catalinaLoader
:Tomcat容器私有的类加载器,加载路径中的class对于Webapp不可见;sharedLoader
:各个Webapp共享的类加载器,加载路径中的class对于所有Webapp可见,但是对于Tomcat容器不可见;WebappClassLoader
WebAppClassLoader는 SharedClassLoader에 의해 로드된 클래스를 사용할 수 있지만 개별 WebAppClassLoader 인스턴스는 서로 격리되어 있습니다.
JasperLoader의 로딩 범위는 이 JSP 파일로 컴파일된 .Class 파일뿐입니다. 표시 목적은 삭제되는 것입니다. 웹 컨테이너가 JSP 파일이 수정되었음을 감지하면 현재 JasperLoader 인스턴스를 대체합니다. , 새로운 Jsp 클래스 로더를 생성하여 JSP 파일의 HotSwap 기능을 구현합니다.
좋아, 지금까지 우리는 Tomcat이 왜 이런 방식으로 설계되었는지, 어떻게 설계되었는지 이미 알고 있습니다. 그렇다면 Tomcat은 Java에서 권장하는 부모 위임 모델을 위반합니까? 대답은 위반입니다.
앞서 말했듯이:
상위 위임 모델에서는 최상위 시작 클래스 로더를 제외하고 다른 모든 클래스 로더가 자체 상위 클래스 로더에 의해 로드되어야 합니다.
분명히 tomcat은 격리를 달성하기 위해 이 방식으로 구현되지 않습니다. 각 webappClassLoader는 클래스 파일을 자체 디렉터리에 로드하고 상위 클래스 로더에 전달하지 않습니다.
질문을 확장해 보겠습니다. Tomcat의 Common ClassLoader가 WebApp ClassLoader에 클래스를 로드하려면 어떻게 해야 합니까? 부모 위임 모델을 파괴하는 것에 대한 이전 내용을 읽은 후에 우리는 스레드 컨텍스트 클래스 로더를 사용하여 이를 구현할 수 있다는 명확한 아이디어를 얻었습니다. 스레드 컨텍스트 로더를 사용하면 부모 클래스 로더가 클래스를 완료하도록 요청할 수 있습니다. 로딩 동작.
멋지지 않나요?
좋아, 마침내 우리는 Tomcat이 부모 위임 모델을 위반하는 이유를 이해하고 Tomcat의 클래스 로더가 어떻게 설계되었는지도 알았습니다. 그런데 Java의 기본 클래스 로더 메커니즘을 검토하고 Java의 클래스 로딩 메커니즘을 파괴하는 방법도 배웠습니다. 이번에는 수확량이 적지 않습니다! ! !
위 내용은 인터뷰어: Tomcat 클래스 로더가 부모 위임 모델을 위반하는 이유는 무엇입니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!