Spring Boot: ClassLoader 의 계승 관계 와 영향 깊이

머리말
spring boot 자체 의 작 동 원리 에 대한 분석 은 다음 과 같 습 니 다.http://hengyunabc.github.io/s...
Spring boot 의 ClassLoader 계승 관계
아래 에 제 공 된 demo 를 실행 할 수 있 습 니 다. 각각 다른 장면 에서 실행 할 수 있 습 니 다. 서로 다른 장면 에서 Spring boot 응용 프로그램의 ClassLoader 계승 관 계 를 알 수 있 습 니 다.
https://github.com/hengyunabc...
세 가지 상황 으로 나 뉜 다.
IDE 에서 직접 run main 함수
Spring 의 ClassLoader 는 바로 System ClassLoader 입 니 다.ClassLoader 의 urls 는 모든 jar 와 자신의 것 을 포함 합 니 다 target/classes

========= Spring Boot Application ClassLoader Urls =============

ClassLoader urls: sun.misc.Launcher$AppClassLoader@2a139a55

file:/Users/hengyunabc/code/java/spring-boot-inside/demo-classloader-context/target/classes/

file:/Users/hengyunabc/.m2/repository/org/springframework/cloud/spring-cloud-starter/1.1.9.RELEASE/spring-cloud-starter-1.1.9.RELEASE.jar

file:/Users/hengyunabc/.m2/repository/org/springframework/boot/spring-boot-starter/1.4.7.RELEASE/spring-boot-starter-1.4.7.RELEASE.jar

...

fat jar 로 실행

mvn clean package

java -jar target/demo-classloader-context-0.0.1-SNAPSHOT.jar

응용 프로그램의 main 함 수 를 실행 하 는 ClassLoader 는 LaunchedURLClassLoader 이 고, parent 는 SystemClassLoader 입 니 다.

========= ClassLoader Tree=============

org.springframework.boot.loader.LaunchedURLClassLoader@1218025c

- sun.misc.Launcher$AppClassLoader@6bc7c054

-- sun.misc.Launcher$ExtClassLoader@85ede7b

또한 LaunchedURLClassLoader 의 urls 는 fat jar 의 BOOT-INF/classes!/ 디 렉 터 리 와 BOOT-INF/lib 의 모든 jar 입 니 다.

========= Spring Boot Application ClassLoader Urls =============

ClassLoader urls: org.springframework.boot.loader.LaunchedURLClassLoader@1218025c

jar:file:/Users/hengyunabc/code/java/spring-boot-inside/demo-classloader-context/target/demo-classloader-context-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/

jar:file:/Users/hengyunabc/code/java/spring-boot-inside/demo-classloader-context/target/demo-classloader-context-0.0.1-SNAPSHOT.jar!/BOOT-INF/lib/spring-boot-1.4.7.RELEASE.jar!/

jar:file:/Users/hengyunabc/code/java/spring-boot-inside/demo-classloader-context/target/demo-classloader-context-0.0.1-SNAPSHOT.jar!/BOOT-INF/lib/spring-web-4.3.9.RELEASE.jar!/

...
SystemClassLoader 의 urls 는 demo-classloader-context-0.0.1-SNAPSHOT.jar 자체 이다.

========= System ClassLoader Urls =============

ClassLoader urls: sun.misc.Launcher$AppClassLoader@6bc7c054

file:/Users/hengyunabc/code/java/spring-boot-inside/demo-classloader-context/target/demo-classloader-context-0.0.1-SNAPSHOT.jar

디 렉 터 리 압축 풀기 로 실행

mvn clean package

cd target

unzip demo-classloader-context-0.0.1-SNAPSHOT.jar -d demo

cd demo

java org.springframework.boot.loader.PropertiesLauncher

응용 프로그램의 main 함 수 를 실행 하 는 ClassLoader 는 LaunchedURLClassLoader 이 고, parent 는 SystemClassLoader 입 니 다.

========= ClassLoader Tree=============

org.springframework.boot.loader.LaunchedURLClassLoader@4aa298b7

- sun.misc.Launcher$AppClassLoader@2a139a55

-- sun.misc.Launcher$ExtClassLoader@1b6d3586
LaunchedURLClassLoader 의 urls 는 디 렉 터 리 에 있 는 BOOT-INF/classes//BOOT-INF/lib/ 아래 의 jar 가방 입 니 다.

========= Spring Boot Application ClassLoader Urls =============

ClassLoader urls: org.springframework.boot.loader.LaunchedURLClassLoader@4aa298b7

file:/Users/hengyunabc/code/java/spring-boot-inside/demo-classloader-context/target/demo/BOOT-INF/classes/

jar:file:/Users/hengyunabc/code/java/spring-boot-inside/demo-classloader-context/target/demo/BOOT-INF/lib/bcpkix-jdk15on-1.55.jar!/

jar:file:/Users/hengyunabc/code/java/spring-boot-inside/demo-classloader-context/target/demo/BOOT-INF/lib/bcprov-jdk15on-1.55.jar!/

jar:file:/Users/hengyunabc/code/java/spring-boot-inside/demo-classloader-context/target/demo/BOOT-INF/lib/classmate-1.3.3.jar!/
SystemClassLoader 의 urls 는 현재 디 렉 터 리 만 있 습 니 다:

========= System ClassLoader Urls =============

ClassLoader urls: sun.misc.Launcher$AppClassLoader@2a139a55

file:/Users/hengyunabc/code/java/spring-boot-inside/demo-classloader-context/target/demo/

사실 두 가지 운행 방식 이 있다.mvn spring-boot:runmvn spring-boot:run -Dfork=true 그러나 비교적 적 게 사용 하고 따로 토론 하지 않 습 니 다.흥미 가 있 으 면 스스로 뛰 어 내 릴 수 있다.
spring boot 에서 ClassLoader 의 계승 관 계 를 정리 합 니 다.
  • IDE 에서 main 함수 가 실 행 될 때 하나의 ClassLoader, 즉 SystemClassLoader
  • 만 실 행 됩 니 다.
  • fat jar 로 실 행 될 때 하나 LaunchedURLClassLoader 가 있 습 니 다. parent 는 System ClassLoader LaunchedURLClassLoader 의 urls 는 fat jar 의 BOOT-INF/classesBOOT-INF/lib 의 jar 입 니 다.SystemClassLoader 의 urls 는 fat jar 자체 입 니 다.
  • 디 렉 터 리 (exploded directory) 가 실 행 될 때 fat jar 와 유사 하지만 url 은 디 렉 터 리 형식 입 니 다.디 렉 터 리 형식 은 더욱 좋 은 호환성 을 가 질 것 이다.

  • spring boot 1. 3. 1. 4. 버 전의 차이 점
    spring boot 1, 3. * 버 전에 서
  • 응용 클래스 와 spring boot loader 의 클래스 는 모두 fat jar 에 포장 되 어 있 습 니 다
  • 응용 의존 jar 는 fat jar 의 /lib 아래 에 놓 습 니 다.

  • spring boot 1.4. * 버 전 후
  • spring boot loader 의 종 류 는 fat jar 에 넣 습 니 다
  • 응용 클래스 는 fat jar BOOT-INF/classes 디 렉 터 리 에 포장 합 니 다
  • 응용 의존 jar 는 fat jar 의 /lib 아래 에 놓 습 니 다.

  • spring boot 1.4 의 포장 구조 변경 은 이 commt 가 도입 한 것 입 니 다.
    https://github.com/spring-pro...
    이 commt 의 본 의 는 classloader 의 계승 관 계 를 간소화 하고 직관 적 인 parent 우선 방식 으로 LaunchedURLClassLoader 을 실현 하 는 동시에 포장 구조 와 전통 적 인 war 가방 응용 이 더욱 가깝다 는 것 이다.
    그러나 이 변경 은 많은 복잡 한 문 제 를 일 으 켰 다. 위 에서 우리 가 분석 한 ClassLoader 계승 관 계 는 좀 어 지 러 웠 다.
    현재 의 ClassLoader 계승 관계 가 가 져 온 영향
    많은 사용자 들 이 일부 코드 가 IDE 에서 잘 달리 지만 실제 배치 가 실 행 될 때 작 동 하지 않 는 다 는 것 을 발견 할 수 있 습 니 다.ClassLoader 의 구조 로 인해 발생 하 는 경우 가 많 습 니 다. 다음은 사례 를 분석 하 겠 습 니 다.demo.jar!/BOOT-INF/classes!/ 이렇게 url 은 작 동 하지 않 습 니 다.
    spring boot 는 표준 jar 프로 토 콜 을 확장 하여 다 층 jar in jar, 그리고 directory in jar 를 지원 합 니 다.spring boot 응용 시작 원리 분석 참조
    spring boot 1.3 에 서 는 jar in jar 가 있 지만 비교적 건장 한 코드 는 이러한 상황 을 처리 할 수 있 습 니 다. 예 를 들 어 tomcat 8 은 스스로 jar in jar 를 지원 합 니 다.
    그러나 대부분의 코드 는 demo.jar!/BOOT-INF/classes!/ 와 같은 directory in jar 의 다 중 url 을 지원 하지 않 기 때문에 spring boot 1.4 에 서 는 많은 라 이브 러 리 코드 가 효력 을 잃 습 니 다.demo.jar!/META-INF/resources 하의 자원 문제
    servlet 3.0 규범 에서 정적 자원 을 META-INF/resources 아래 에 두 면 servlet container 는 읽 기 를 지원 합 니 다.그러나 위의 상속 결과 에서 우 리 는 한 가지 문 제 를 발견 할 수 있다.
  • 응용 프로그램 은 fat jar 로 시작 합 니 다. embedded tomcat 를 시작 하 는 ClassLoader 는 LaunchedURLClassLoader
  • 입 니 다.
  • LaunchedURLClassLoader 의 urls 는 fat jar 자체
  • 가 없다.
  • 응 용 된 main 함수 가 있 는 모듈 의 src/main/resources/META-INF/resources 디 렉 터 리 가 fat jar 에 포장 되 었 습 니 다. 즉 demo.jar!/META-INF/resources
  • 응 용 된 fat jar 는 SystemClassLoader 의 url, 즉 LaunchedURLClassLoader 의 parent
  • 입 니 다.
    이렇게 해서 이상 한 현상 을 일 으 켰 다.
  • 자신의 ClassLoader. getResources () 를 직접 사용 하면 META-INF/resources 자원 을 얻 을 수 있 습 니 다
  • 그러나 embedded tomcat 는 fat jar 자 체 를 리 소스 세트 에 넣 지 않 았 습 니 다. 시작 할 때 ClassLoader 는 LaunchedURLClassLoader 이 고 자신의 ClassLoader 만 스 캔 하기 때 문 입 니 다 urls
  • 자원 을 다른 jar 패키지 META-INF/resources 에 두 면 접근 할 수 있 습 니 다. 자원 을 자신의 main 함수 src/main/resources/META-INF/resources 에 두 었 을 때 접근 할 수 없습니다
  • 또한, spring boot 의 공식 jsp 의 예 는 war 의 포장 형식 만 지원 하고 fat jar 는 지원 되 지 않 는 것 도 이 로 인해 발생 한 것 입 니 다.
    질문getResource("") 의 미 는 ClassLoader 의 url 을 되 돌려 주 는 첫 번 째 url 입 니 다. 사용자 들 은 이것 이 자신의 classes 디 렉 터 리 나 jar 의 url 이 라 고 생각 할 때 가 많 습 니 다.
    그러나 실제로 ClassLoader 가 url 목록 을 불 러 올 때 임 의 성 이 있 기 때문에 OS 저층 실현 과 관련 이 있 기 때문에 url 의 순서 가 모두 같다 고 보장 할 수 없습니다.그래서 getResources("") 돌아 오 는 결과 가 다 를 때 가 많다.
    그러나 많은 라 이브 러 리 나 이 코드 에 의존 하여 스 캔 자원 을 찾 으 면 spring boot 에서 작 동 하지 않 습 니 다.
    또 주의해 야 할 것 은 스프링 부 트 가 세 가지 다른 형식 으로 실행 되 고 getResource("") 되 돌아 오 는 결과 도 다르다 는 점 이다.사용 자 는 데모 의 코드 를 스스로 바 꾸 어 결 과 를 인쇄 할 수 있 습 니 다.
    한 마디 로 하면 이 두 API 에 의존 하지 말고 자원 을 넣 어 포 지 셔 닝 하 는 것 이 좋다.또는 spring 자체 가 제공 하 는 자원 검색 체 제 를 직접 이용 합 니 다.
    질문
    사용 자 는 여러 개의 코드 모듈 을 가지 고 있 으 며, 서로 다른 모듈 아래 에 여러 개의 getResource("") spring 프로필 을 놓 았 습 니 다.
    사용자 가 유사 한 getResources("") 어댑터 를 사용 하여 자원 을 불 러 오 면 IDE 에서 뛸 때 올 바 르 게 불 러 올 수 있 지만 fat jar 에 서 는 불 러 올 수 없 는 문제 가 발생 할 수 있 습 니 다.
    spring 자신의 문서 에서 관련 해석 을 볼 수 있 습 니 다.
    https://docs.spring.io/spring...
    WARNING: Note that "classpath
    :" when combined with Ant-style patterns will only work reliably with at least one root directory before the pattern starts, unless the actual target files reside in the file system. This means that a pattern like "classpath:*.xml" will not retrieve files from the root of jar files but rather only from the root of expanded directories. This originates from a limitation in the JDK's ClassLoader.getResources() method which only returns file system locations for a passed-in empty String (indicating potential roots to search). This ResourcePatternResolver implementation is trying to mitigate the jar root lookup limitation through URLClassLoader introspection and "java.class.path" manifest evaluation; however, without portability guarantees.
    즉, classpath*:**-service.xml 을 사용 하여 다른 jar 가방 과 일치 할 때 디 렉 터 리 가 앞 에 있어 야 합 니 다. 그렇지 않 으 면 일치 하지 않 습 니 다. 이것 은 ClassLoader. getResources () 함수 로 인 한 것 입 니 다.
    IDE 에서 뛸 때 응용 에 의존 하 는 다른 모듈 은 보통 하나의 *-service.xml 디 렉 터 리 이기 때문에 문제 가 없다.
    그러나 fat jar 로 달 릴 때 다른 모듈 은 하나의 jar 로 포장 되 어 classpath*:**-service.xml 아래 에 놓 여 있 기 때문에 이 때 는 연결 이 실패 합 니 다.
    총결산
  • 이 새로운 classpath* 포장 형식 은 뚜렷 한 장점 이 있다. 더욱 뚜렷 하고 war 가방 의 형식 에 가깝다.
  • spring boot 의 ClassLoader 의 구조 수정 에 따 른 복잡 한 문 제 는 당초 수 정 된 사람 이 예견 할 수 있 는 것 이 아니다
  • 많은 문 제 는 spring boot 의 ClassLoader 구 조 를 이해 해 야 합 니 다. 그렇지 않 으 면 근본 적 인 원인 을 찾 을 수 없습니다
  • 위 챗 공식 번호
    횡 운 단 령 칼럼

    좋은 웹페이지 즐겨찾기