คำนำ
สำหรับการวิเคราะห์หลักการเริ่มต้นของการบูตฤดูใบไม้ผลิเองโปรดดูที่: http://www.vevb.com/article/141478.htm
ความสัมพันธ์การสืบทอดการสืบทอดของ Classloader ใน Spring Boot สามารถเรียกใช้การสาธิตที่ให้ไว้ด้านล่างและเรียกใช้ในสถานการณ์ที่แตกต่างกัน คุณสามารถทราบความสัมพันธ์การสืบทอดของ Classloader ของแอปพลิเคชัน Spring Boot ในสถานการณ์ต่าง ๆ
https://github.com/hengyunabc/spring-boot-inside/tree/master/demo-classloader-context
มีสามสถานการณ์:
ใน IDE ฟังก์ชั่นหลักรันจะทำงานโดยตรงคลาสโหลดของสปริงคือ SystemClassLoader โดยตรง URL ของ classloader มีขวดทั้งหมดและเป้าหมาย/คลาสของตัวเอง
========= แอปพลิเคชันสปริงบูตคลาส URLs ================
URL classloader: sun.misc.launcher$appclassloader@2a139a55
ไฟล์:/ผู้ใช้/hengyunabc/code/java/spring-boot-inside/demo-classloader-context/target/classes/
ไฟล์: /users/hengyunabc/.m2/repository/org/springframework/cloud/spring-cloud-starter/1.1.9.release/spring-cloud-starter-1.1.9.release.jar
ไฟล์: /users/hengyunabc/.m2/repository/org/springframework/boot/spring-boot-starter/1.4.7.release/spring-boot-starter-1.4.7.release.jar
-
วิ่งเป็นโถไขมัน
MVN Clean PackageJava -jar เป้าหมาย/การสาธิต Classloader-Context-0.0.1-snapshot.jar
classloader ที่ดำเนินการฟังก์ชั่นหลักของแอปพลิเคชันคือการเปิดตัว UrlClassLoader และพาเรนต์คือ SystemClassLoader
========== ต้นไม้ classloader ================
org.springframework.boot.load.launchedurlclassload@1218025c
- sun.misc.launcher$appclassloader@6bc7c054
- sun.misc.launcher$extclassloader@85ede7b
และ LaunchedURLClassLoader的urls คือ BOOT-INF/classes!/ ไดเรกทอรีใน fat jar และขวดทั้งหมดใน boot-inf/lib
========= แอปพลิเคชันสปริงบูตคลาส URLs ================
URL classloader: org.springframework.boot.load.launchedurlclassload@1218025c
jar: ไฟล์: /users/hengyunabc/code/java/spring-boot-inside/demo-classloader-context/target/demo-classloader-context-0.0.1-snapshot.jar!/boot-inf/classes!
JAR: ไฟล์: /users/hengyunabc/code/java/spring-boot-inside/demo-classloader-context/target/demo-classloader-context-0.0.1-snapshot.jar!
jar: ไฟล์: /users/hengyunabc/code/java/spring-boot-inside/demo-classloader-context/target/demo-classloader-context-0.0.1-snapshot.jar!
-
URL ของ SystemClassLoader คือ demo-classloader-context-0.0.1-SNAPSHOT.jar เอง
========== URL classloader ระบบ =================
URL classloader: sun.misc.launcher$appclassloader@6bc7c054
ไฟล์: /users/hengyunabc/code/java/spring-boot-inside/demo-classloader-context/target/demo-classloader-context-0.0.1-snapshot.jar
เรียกใช้เป็นไดเรกทอรีที่บีบอัด
MVN CLEAN PACKAGECD DEMO-ClassLoader-Context-0.0.1-snapshot.jar -d Demojava org.springframework.boot.loader.propertieslauncher
classloader ที่ดำเนินการฟังก์ชั่นหลักของแอปพลิเคชันคือ LaunchedURLClassLoader และพาเรนต์คือ SystemClassLoader
========== ต้นไม้ classloader ================
org.springframework.boot.load.launchedurlclassload@4aa298b7
- sun.misc.launcher$appclassloader@2a139a55
- sun.misc.launcher$extclassloader@1b6d3586
URL ของ LaunchedURLClassLoader คือแพ็คเกจ JAR ด้านล่าง BOOT-INF/classes/ และ /BOOT-INF/lib/ ในไดเรกทอรีการบีบอัด
========= แอปพลิเคชันสปริงบูตคลาส URLs ================
URL classloader: org.springframework.boot.load.launchedurlclassload@4aa298b7
ไฟล์:/ผู้ใช้/hengyunabc/code/java/spring-boot-inside/demo-classloader-context/target/demo/boot-inf/classes/
jar: ไฟล์: /users/hengyunabc/code/java/spring-boot-inside/demo-classloader-context/target/demo/boot-inf/lib/bcpkix-jdk15on-1.55.jar!
jar: ไฟล์: /users/hengyunabc/code/java/spring-boot-inside/demo-classloader-context/target/demo/boot-inf/lib/bcprov-jdk15on-1.55.jar!
jar: ไฟล์: /users/hengyunabc/code/java/spring-boot-inside/demo-classloader-context/target/demo/boot-inf/lib/classmate-1.3.3.jar!
URL ของ SystemClassLoader มีไดเรกทอรีปัจจุบันเท่านั้น:
========== URL classloader ระบบ =================
URL classloader: sun.misc.launcher$appclassloader@2a139a55
ไฟล์:/ผู้ใช้/hengyunabc/code/java/spring-boot-inside/demo-classloader-context/เป้าหมาย/ตัวอย่าง/
ในความเป็นจริงมีอีกสองวิธีในการเรียกใช้: mvn spring-boot:run และ mvn spring-boot:run -Dfork=true แต่ไม่ค่อยได้ใช้และจะไม่ถูกกล่าวถึงแยกต่างหาก หากคุณรู้สึกสนใจคุณสามารถลงไปได้ด้วยตัวเอง
บทสรุปของความสัมพันธ์การสืบทอดของ classloader ในฤดูใบไม้ผลิ boot
เมื่อฟังก์ชั่นหลักถูกดำเนินการใน IDE มีเพียงหนึ่ง classloader นั่นคือ SystemClassLoader
เมื่อทำงานเป็นขวดไขมันจะมีการเปิดตัว LaunchlClassloader ซึ่งผู้ปกครองคือ SystemClassloader
URL ของการเปิดตัว URLEDURLCLASSLOADER คือขวดภายใต้ BOOT-INF/คลาสและ BOOT-INF/LIB ในขวดไขมัน URL ของ SystemClassloader เป็นขวดไขมันเอง
เมื่อเรียกใช้ไดเรกทอรีที่รันซิปมันคล้ายกับขวดไขมัน แต่ URL อยู่ในรูปแบบของไดเรกทอรี รูปแบบไดเรกทอรีจะมีความเข้ากันได้ดีขึ้น
สปริงบูตเวอร์ชัน 1.3. และ 1.4.
ใน Spring Boot 1.3.* เวอร์ชัน
คลาสสปริงบูตตัวโหลดอยู่ในขวดไขมัน
การเปลี่ยนแปลงโครงสร้างบรรจุภัณฑ์ของสปริงบูต 1.4 ได้รับการแนะนำโดยการกระทำนี้
https://github.com/spring-projects/spring-boot/commit/87fe0b2adeef85c842c009bfeebac1c84af8a5d7
ความตั้งใจดั้งเดิมของการกระทำนี้คือการทำให้ความสัมพันธ์ในการสืบทอดของคลาสโหลดง่ายขึ้นใช้ตัวเปิดใช้งาน LaNedurlclassloader ในวิธีแรกของผู้ปกครองที่ใช้งานง่ายและในขณะเดียวกันโครงสร้างบรรจุภัณฑ์ก็ใกล้เคียงกับแอปพลิเคชันแพ็คเกจสงครามแบบดั้งเดิม
อย่างไรก็ตามการเปลี่ยนแปลงนี้ทำให้เกิดปัญหาที่ซับซ้อนมากมาย จากข้างต้นเราวิเคราะห์ความสัมพันธ์การสืบทอดของ Classloader เป็นอาการวิงเวียนศีรษะเล็กน้อย
ผลกระทบบางอย่างของความสัมพันธ์การสืบทอดคลาสโหลดในปัจจุบัน
มีผู้ใช้จำนวนมากที่อาจพบว่ารหัสบางอย่างทำงานได้ดีใน IDE แต่ไม่ทำงานเมื่อปรับใช้จริง หลายครั้งที่เกิดจากโครงสร้างของ classloader นี่คือบางกรณี
demo.jar!/boot-inf/classes!/ด้วยวิธีนี้ URL ไม่ทำงาน
เนื่องจาก Spring Boot ขยายโปรโตคอล JAR มาตรฐานทำให้สามารถรองรับขวดหลายชั้นในขวดและไดเรกทอรีในขวด อ้างถึงการวิเคราะห์หลักการเริ่มต้นแอปพลิเคชัน Spring Boot Application
แม้ว่าจะมี jar ใน jar ใน Spring Boot 1.3 แต่บางรหัสที่ค่อนข้างแข็งแกร่งสามารถจัดการกับสถานการณ์นี้ได้เช่น Tomcat8 เองรองรับ Jar ใน Jar
อย่างไรก็ตามรหัสส่วนใหญ่จะไม่รองรับ URL หลายรายการเช่น demo.jar!/BOOT-INF/classes!/ ดังนั้นใน Spring Boot1.4 รหัสห้องสมุดจำนวนมากจะไม่ถูกต้อง
ปัญหาทรัพยากรภายใต้ demo.jar!/meta-inf/ทรัพยากร
ในข้อกำหนดของ Servlet 3.0 แอปพลิเคชันสามารถวางทรัพยากรแบบคงที่ภายใต้ Meta-INF/ทรัพยากรและคอนเทนเนอร์ Servlet จะสนับสนุนการอ่าน แต่จากผลลัพธ์การสืบทอดข้างต้นเราสามารถหาปัญหาได้:
สิ่งนี้สร้างปรากฏการณ์แปลก ๆ :
นอกจากนี้ตัวอย่าง JSSP อย่างเป็นทางการของ Spring Boot รองรับรูปแบบบรรจุภัณฑ์ของสงครามเท่านั้นและไม่รองรับขวดไขมันซึ่งเกิดจากสิ่งนี้
ปัญหาเกี่ยวกับมูลค่าคืนของ GetResource ("") และ GetResources ("")
ความหมายของ GetResource ("") คือการส่งคืน URL แรกของ URL classloader หลายครั้งที่ผู้ใช้คิดว่านี่เป็นไดเรกทอรีชั้นเรียนของตัวเองหรือ url jar
แต่ในความเป็นจริงเนื่องจาก classloader โหลดรายการ URL จึงเป็นแบบสุ่มซึ่งเกี่ยวข้องกับการใช้งานระดับต่ำของระบบปฏิบัติการและไม่สามารถรับประกันได้ว่าลำดับของ URL นั้นเหมือนกัน ดังนั้นผลลัพธ์ที่ส่งคืนโดย GetResource ("") มักจะแตกต่างกัน
อย่างไรก็ตามห้องสมุดจำนวนมากหรือแอปพลิเคชันพึ่งพารหัสนี้เพื่อค้นหาแหล่งข้อมูลการสแกนเพื่อให้พวกเขาไม่ทำงานภายใต้การบูตฤดูใบไม้ผลิ
นอกจากนี้ยังเป็นที่น่าสังเกตว่า Spring Boot ทำงานในสามรูปแบบที่แตกต่างกันและผลลัพธ์ที่ส่งคืนโดย GetResources ("") ก็แตกต่างกันเช่นกัน ผู้ใช้สามารถเปลี่ยนรหัสในการสาธิตด้วยตนเองและพิมพ์ผลลัพธ์
ในระยะสั้นอย่าพึ่งพา API ทั้งสองนี้เป็นการดีที่สุดที่จะวางทรัพยากรเพื่อค้นหาด้วยตัวเอง หรือใช้กลไกการสแกนทรัพยากรโดยตรงโดยสปริง
ปัญหาการจำแนกประเภทป่าคล้ายกับ classpath*: **-service.xml
ผู้ใช้มีโมดูลโค้ดหลายตัวและไฟล์การกำหนดค่าสปริงแบบสปริงหลาย *-service.xml จะถูกวางไว้ภายใต้โมดูลที่แตกต่างกัน
หากผู้ใช้ใช้ WildCards เช่น ClassPath*: **-Service.xml เพื่อโหลดทรัพยากรก็เป็นไปได้มากที่จะโหลดได้อย่างถูกต้องเมื่อทำงานใน IDE แต่ไม่สามารถโหลดใต้ขวดไขมันได้
คุณสามารถดูการวิเคราะห์ที่เกี่ยวข้องได้จากเอกสารของฤดูใบไม้ผลิ:
https://docs.spring.io/spring/docs/4.3.9.release/javadoc-api/org/springframework/core/io/support/pathmatchingresourcepatternresolver.html
คำเตือน: โปรดทราบว่า“ classpath:” เมื่อรวมกับรูปแบบสไตล์มดจะทำงานได้อย่างน่าเชื่อถือกับไดเรกทอรีรูทอย่างน้อยหนึ่งไดเรกทอรีก่อนที่รูปแบบจะเริ่มขึ้นเว้นแต่ไฟล์เป้าหมายจริงจะอยู่ในระบบไฟล์ ซึ่งหมายความว่ารูปแบบเช่น“ classpath:*. xml” จะไม่ดึงไฟล์จากรูทของไฟล์ JAR แต่เป็นเพียงรูทของไดเรกทอรีที่ขยายตัวเท่านั้น สิ่งนี้เกิดขึ้นจากข้อ จำกัด ในเมธอด classloader.getResources () ของ JDK ซึ่งจะส่งคืนตำแหน่งระบบไฟล์สำหรับสตริงที่ว่างเปล่าที่ผ่านมา (แสดงถึงรากที่อาจเกิดขึ้นเพื่อค้นหา) การใช้งาน ResourcePatternResolver นี้กำลังพยายามลดข้อ จำกัด การค้นหารูท JAR ผ่านการแนะนำ urlclassloader และ "java.class.path" การประเมินผลการวิจัย อย่างไรก็ตามไม่มีการรับประกันการพกพา
กล่าวคือเมื่อใช้ classpath* เพื่อจับคู่แพ็คเกจขวดอื่น ๆ จะต้องมีเลเยอร์ของไดเรกทอรีด้านหน้ามิฉะนั้นจะไม่ตรงกัน สิ่งนี้เกิดจากฟังก์ชั่น classloader.getResources ()
เพราะเมื่อทำงานใน IDE โมดูลอื่น ๆ ที่แอปพลิเคชันขึ้นอยู่กับมักจะเป็นไดเรกทอรีคลาสดังนั้นจึงไม่มีปัญหา
อย่างไรก็ตามเมื่อทำงานด้วยขวดไขมันโมดูลอื่น ๆ จะถูกบรรจุเป็นขวดและวางไว้ภายใต้ boot-inf/lib ดังนั้นการกระจายตัวป่าจะล้มเหลวในเวลานี้
สรุป
ข้างต้นเป็นเนื้อหาทั้งหมดของบทความนี้ ฉันหวังว่ามันจะเป็นประโยชน์ต่อการเรียนรู้ของทุกคนและฉันหวังว่าทุกคนจะสนับสนุน wulin.com มากขึ้น