มีคลาสโหลดสองประเภทใน Java หนึ่งคือผู้ใช้ที่กำหนดไว้และอื่น ๆ คือตัวโหลดคลาส bootstrap ที่สร้างขึ้นใน JVM
มีตัวโหลดคลาสสามประเภทที่สร้างขึ้นใน JVM คือ bootstrap classloader, ส่วนขยายคลาสโหลด (เช่น extclassloader) และ system classloader (เช่น appclassloader)
ฉันจะไม่พูดถึงการมอบหมายผู้ปกครองเมื่อมีการโหลด JVM มีบทความมากมายเกี่ยวกับ Javaeye ที่แนะนำ ...
คุณสามารถดูที่ตัวสร้างของพวกเขาแยกกันโดยที่ bootstrap classloader เขียนเป็น c
java.lang.classloader
Protection classloader (classloader parent) {SecurityManager Security = System.getSecurityManager (); จริง; เริ่มต้น = true;
ตัวสร้างนี้มีพารามิเตอร์สองตัวและไม่มีตัวสร้าง ตัวสร้างที่มีพารามิเตอร์ส่งผ่านในตัวโหลดหลักของตัวโหลดคลาสนี้ในขณะที่ตัวสร้างที่ไม่มีพารามิเตอร์จะรักษาตัวโหลดคลาสที่ส่งคืนโดย getSystemClassLoader () เป็นตัวโหลดหลักของตัวเอง
public classloader getSystemClassLoader () {// ตัวโหลดคลาสที่ส่งคืนถูกกำหนดให้ InitsystemClassLoader (); ccl = getCallerClassLoader (); ถ้า (ccl! = null && ccl! = scl &&! scl.isancestor (CCL)) {sm.checkpermission ถ้า (! OOPS = null; // ค่าถูกกำหนด scl = l.getClassLoader (); ............................... .... ..................................... }} sclset = true;}} ตัวโหลดคลาสแม่ที่นี่คือ SCL ซึ่งได้มาจาก L.GetClassLoader (), getClassLoader () จากนั้นดูที่ซอร์สโค้ดของตัวเรียกใช้งาน:
ตัวเรียกใช้งานแบบคงที่ส่วนตัว = Launcher ใหม่ ();
Public Launcher GetLauncher () {return Launcher; );} catch (ioexception e) {โยน InternalError ใหม่ ("ไม่สามารถสร้างตัวโหลดคลาสส่วนขยาย"); AppClassLoader กล่าวคือ GetSystemClassLoader ส่งคืน AppClassLoader Loader = AppClassLoader.getAppClassLoader (extcl); เธรดเพื่อป้องกันความสับสนที่เกิดจาก classloads ใน multithreads (ฉันเข้าใจสิ่งนี้เองฮ่าฮ่า) // ยังตั้งค่าตัวโหลดคลาสบริบทสำหรับเธรดหลัก .................................................. ................................................ .. .............. } / * * ส่งคืนตัวโหลดคลาสที่ใช้ในการเปิดแอปพลิเคชันหลัก จากนี้เราจะเห็นว่าตัวโหลดหลักของ AppClassLoader เป็น extclassloader และตัวโหลดพาเรนต์ของ extclassloader คืออะไร? ลองดูที่คอนสตรัคเตอร์ extclassLoader:
public extclassloader (ไฟล์ [] dirs) โยน ioexception {super (getexturls (dirs), null, โรงงาน);รถบรรทุกหลักของเขาว่างเปล่าในขณะที่คลาสแม่ระดับบนสุดของเขาคือ java.lang.classloader
คลาสซิงโครไนซ์ที่ได้รับการป้องกัน <?> loadclass (ชื่อสตริง, การแก้ไขบูลีน) พ่น classnotfoundexception {// ก่อนตรวจสอบว่าคลาสได้รับการโหลดคลาส C = findloadDclass (ชื่อ); ถ้า (c == null) {ลอง {ถ้า ( ! = null) {// เรียกตัวโหลดหลักก่อนที่จะโหลด ) {// ถ้ายังไม่พบดังนั้นจึงเรียกใช้ FindClass ในการสั่งซื้อ // เพื่อค้นหาคลาสที่นี่ FindBootStrapClass0 คือการเรียก Bootstrap classloader ซึ่งเป็นตัวโหลดคลาสหลักที่สุดในการโหลดคลาส
ในที่สุดเราจะเห็นได้ว่าตัวโหลดคลาสที่ส่งคืนโดย getSystemClassLoader () คือ AppClassLoader
การวิเคราะห์กลไก Java Classloader
JDK เริ่มต้น classloader
JDK จัดเตรียม classloaders ต่อไปนี้ตามค่าเริ่มต้น
bootstrp loader
bootstrp loader ถูกเขียนในภาษา C ++ JRE/คลาส
extclassloader
bootstrp loader โหลด extclassloader และตั้งค่าตัวโหลดหลักของ extclassloader ไปยัง bootstrp loader ไดเรกทอรีคลาสทั้งหมดนี้ภายใต้ไลบรารีพา ธ และคลาสในพา ธ ที่ระบุโดยตัวแปรระบบ java.ext.dirs
AppClassLoader
หลังจากตัวโหลด bootSTRP โหลด extclassloader แล้ว AppClassLoader จะถูกโหลดและตัวโหลดหลักของ AppClassLoader จะถูกระบุเป็น extClassLoader AppClassLoader ยังเขียนใน Java มันเป็นเอกสาร JAR ซึ่งเป็นตัวโหลดคลาสเริ่มต้นสำหรับโปรแกรม Java
เพื่อสรุปความสัมพันธ์ระหว่างพวกเขาสามารถอธิบายได้ในรูปต่อไปนี้:
รูปแบบการมอบหมายผู้ปกครอง
การโหลด classloader ใน Java ใช้กลไกผู้แทนหลัก
ปัจจุบัน ClassLoader ตรวจสอบครั้งแรกว่าคลาสนี้ได้รับการโหลดจากคลาสที่โหลดไปแล้วหรือไม่
ตัวโหลดแต่ละชั้นมีแคชโหลดของตัวเอง
เมื่อไม่พบแคช Classloader ตัวโหลดคลาสแม่จะถูกมอบหมายให้โหลด วิธี Bootstrp classloader
เมื่อตัวโหลดคลาสแม่ทั้งหมดไม่ได้โหลดพวกเขาจะถูกโหลดโดยตัวโหลดคลาสปัจจุบันและใส่ไว้ในแคชของตัวเองเพื่อให้สามารถส่งคืนโดยตรงในครั้งต่อไปที่มีคำขอโหลด
เมื่อพูดถึงสิ่งนี้คุณอาจสงสัยว่าทำไม Java จึงใช้กลไกการมอบหมายเช่นนี้? เพื่อให้เข้าใจถึงปัญหานี้เราได้แนะนำแนวคิดอื่น "เนมสเปซ" เกี่ยวกับ classloader ซึ่งหมายความว่าในการกำหนดคลาสที่แน่นอนคุณต้องมีชื่อที่มีคุณสมบัติครบถ้วนของคลาสและโหลดคลาสคลาสนี้เพื่อกำหนดร่วมกัน กล่าวคือแม้ว่าชื่อที่ผ่านการรับรองอย่างสมบูรณ์ของสองคลาสจะเหมือนกันเพราะคลาสโหลดที่แตกต่างกันโหลดคลาสนี้ แต่ก็เป็นคลาสที่แตกต่างกันใน JVM หลังจากทำความเข้าใจเนมสเปซให้ดูที่โมเดลผู้แทนกันเถอะ หลังจากใช้โมเดลตัวแทนความสามารถเชิงโต้ตอบของคลาสโหลดที่แตกต่างกันจะเพิ่มขึ้น ไม่ว่าโปรแกรมของคุณจะมีรถตักคลาสมากมายอยู่ในนั้นดังนั้นคลาสเหล่านี้สามารถแชร์ได้จริงซึ่งหลีกเลี่ยงความสับสนที่เกิดจากรถตักคลาสที่แตกต่างกันหลังจากโหลดคลาสที่แตกต่างกันของชื่อเดียวกัน
วิธีปรับแต่ง classloader
นอกเหนือจากคลาสโหลดเริ่มต้นที่กล่าวถึงข้างต้น Java ยังอนุญาตให้แอปพลิเคชันปรับแต่ง classloader เราต้องให้ความสนใจกับวิธีการสำคัญหลายประการ:
1. วิธีการโหลดคลาส
วิธี loadclass ประกาศ
คลาสสาธารณะ <?> loadclass (ชื่อสตริง) พ่น classnotFoundException
ข้างต้นคือการประกาศต้นแบบของวิธีการโหลด LOADCLASS ลองมาดูรหัสของวิธีนี้เพื่อดูว่ามันใช้การมอบหมายผู้ปกครองอย่างไร
ใช้วิธีการโหลดคลาส
คลาสสาธารณะ <?> loadclass (ชื่อสตริง) พ่น classnotfoundexception {return loadclass (ชื่อ, false);}จากด้านบนเราจะเห็นว่าวิธีการโหลด LoadClass เรียกใช้วิธี loadcclass (ชื่อ, เท็จ) ดังนั้นลองมาดูการใช้วิธี loadclass อื่น
คลาส loadclass (ชื่อสตริง, บูลีนแก้ไข)
คลาสซิงโครไนซ์ที่ได้รับการป้องกัน <?> loadclass (ชื่อสตริง, การแก้ไขบูลีน) พ่น classnotfoundexception {// ก่อนตรวจสอบว่าคลาสได้รับการโหลดคลาส C = findloadedClass (ชื่อ); // ตรวจสอบว่าคลาสได้รับการโหลดหรือไม่ (c = c = = null) {ลอง {ถ้า (parent! = null) {c = parent.loadclass (ชื่อ, เท็จ); } else {c = findBootStrapClass0 (ชื่อ); // หากไม่มีตัวโหลดคลาสแม่ให้มอบตัวโหลด bootstrap เพื่อโหลด}} catch (classnotFoundException e) {// ถ้ายังไม่พบ ค้นหาคลาส }} if (Resolve) {Resolveclass (C);} return c;}ในรหัสข้างต้นฉันเพิ่มความคิดเห็นเพื่อดูว่ากลไกการมอบหมายผู้ปกครองของ LoadClass ทำงานอย่างไร สิ่งหนึ่งที่เราต้องทราบที่นี่คือคลาสสาธารณะ <?> loadclass (ชื่อสตริง) พ่น classnotfoundexception ไม่ได้ทำเครื่องหมายเป็นขั้นสุดท้ายซึ่งหมายความว่าเราสามารถแทนที่วิธีนี้ซึ่งหมายความว่ากลไกการมอบหมายผู้ปกครองอาจแตก นอกจากนี้เราสังเกตเห็นว่ามีวิธีการค้นหาด้านบน
2.FindClass
เราตรวจสอบซอร์สโค้ดของ java.lang.classloader และเราพบว่าการใช้งาน FindClass มีดังนี้:
คลาสที่ได้รับการป้องกัน <?> findClass (ชื่อสตริง) พ่น classnotFoundException {โยน classnotFoundException ใหม่ (ชื่อ);}เราจะเห็นได้ว่าการใช้งานเริ่มต้นของวิธีนี้คือการโยนข้อยกเว้นโดยตรง แต่อันที่จริงแล้ววิธีนี้จะถูกทิ้งไว้ในแอปพลิเคชันของเราเพื่อแทนที่ การใช้งานที่เฉพาะเจาะจงขึ้นอยู่กับตรรกะการใช้งานของคุณ เรามาอธิบาย defineclass ในภายหลัง ตกลงผ่านการวิเคราะห์ข้างต้นเราสามารถวาดข้อสรุปต่อไปนี้:
เมื่อเราเขียน classloader ของเราเองหากเราต้องการติดตามกลไกการมอบหมายผู้ปกครองเราจะต้องแทนที่ FindClass เท่านั้น
3. แน่นอน
ก่อนอื่นให้ดูที่ซอร์สโค้ดของ defineclass:
กำหนด
คลาสสุดท้ายที่ได้รับการป้องกัน <?> defineclass (ชื่อสตริง, ไบต์ [] b, int ปิด, int len) พ่น classformaterror {return defineclass (ชื่อ, b, ปิด, len, null);}จากรหัสข้างต้นเราจะเห็นว่าวิธีนี้ถูกกำหนดให้เป็นขั้นสุดท้ายซึ่งหมายความว่าวิธีนี้ไม่สามารถแทนที่ได้ ไฟล์จะต้องปฏิบัติตามคำจำกัดความของคลาสที่ระบุโดยข้อกำหนดของเครื่องเสมือน Java ในที่สุดวิธีนี้จะเรียกวิธีการดั้งเดิมเพื่อใช้การโหลดของคลาสจริง
ตกลงผ่านคำอธิบายข้างต้นลองนึกถึงคำถามต่อไปนี้:
หากเราเขียนคลาส java.lang.string ด้วยตัวเองเราสามารถแทนที่คลาสที่เรียก JDK ได้หรือไม่?
คำตอบคือไม่ เราไม่สามารถบรรลุเป้าหมายได้ ทำไม ฉันเห็นคำอธิบายออนไลน์มากมายว่ากลไกการมอบหมายผู้ปกครองช่วยแก้ปัญหานี้ได้ แต่จริงๆแล้วมันไม่ถูกต้องมากนัก เนื่องจากกลไกการมอบหมายผู้ปกครองอาจแตกได้คุณสามารถเขียน classloader เพื่อโหลดคลาส java.lang.string ที่คุณเขียน แต่คุณจะพบว่ามันจะไม่โหลดสำเร็จโดยเฉพาะเพราะมันเป็นคลาสที่เริ่มต้นด้วย Java*, JVM การใช้งานทำให้มั่นใจได้ว่าจะต้องโหลดโดย bootstrp
สถานการณ์ที่ไม่ปฏิบัติตาม "กลไกการมอบหมายผู้ปกครอง"
ดังกล่าวข้างต้นว่ากลไกการมอบหมายผู้ปกครองส่วนใหญ่จะตระหนักถึงปัญหาการโต้ตอบของคลาสที่โหลดระหว่างคลาสที่แตกต่างกัน ตัวโหลดคลาสแม่ใน Java มาพูดคุยเกี่ยวกับการเกิดขึ้นของสถานการณ์นี้
มีมาตรฐาน SPI (อินเทอร์เฟซผู้ให้บริการ) ใน Java ซึ่งใช้ไลบรารี SPI เช่น JDBC, JNDI ฯลฯ เราทุกคนรู้ว่า JDBC ต้องการไดรเวอร์ที่จัดทำโดยบุคคลที่สาม API ของ ClassPath และ JDBC เป็นส่วนหนึ่งของบทบัญญัติของ JDK และได้รับการโหลดโดย Bootstrp Java แนะนำแนวคิดของการโหลดคลาสเธรด ไดรเวอร์มันก็โอเคโหลดผ่านตัวโหลดคลาสบริบทของเธรด
นอกจากนี้เพื่อที่จะใช้ OSGI ตัวโหลดคลาสที่ยืดหยุ่นมากขึ้นและเซิร์ฟเวอร์แอพ Java บางตัวมันยังทำลายกลไกการมอบหมายหลัก