เพื่อให้เข้าใจอย่างลึกซึ้งยิ่งขึ้นเกี่ยวกับ ClassLoader คุณต้องทราบก่อนว่าจะใช้ classloader อะไร ตามชื่อหมายถึงมันจะใช้ในการโหลดไฟล์คลาสลงใน JVM เพื่อใช้งานโดยโปรแกรม เรารู้ว่า โปรแกรม Java สามารถโหลดคำจำกัดความของคลาสแบบไดนามิกและกลไกการโหลดแบบไดนามิกนี้ถูกนำไปใช้ผ่าน classloader ดังนั้นคุณสามารถจินตนาการได้ว่า classloader สำคัญแค่ไหน
หลังจากเห็นสิ่งนี้เพื่อนบางคนอาจนึกถึงคำถามนั่นคือเนื่องจาก classloader ถูกใช้เพื่อโหลดคลาสลงใน JVM classloader โหลดอย่างไร ไม่ใช่คลาส Java หรือไม่?
ถูกต้องมีคลาสโหลดที่นี่ที่ไม่ได้เขียนด้วยภาษา Java แต่เป็นส่วนหนึ่งของการใช้งาน JVM classloader นี้เป็น bootstrap classloader (เริ่มคลาส Loader), classloader นี้โหลด Java Core API เมื่อ JVM กำลังทำงานเพื่อตอบสนองความต้องการขั้นพื้นฐานที่สุดของโปรแกรม Java รวมถึง classloader ที่ผู้ใช้กำหนด classloader ที่ผู้ใช้กำหนดที่เรียกว่าที่นี่หมายถึง classloader ที่ใช้งานผ่านโปรแกรม Java หนึ่งคือ extclassloader Classloader นี้ใช้ในการโหลด API ส่วนขยาย Java นั่นคือคลาส In /lib /ext และอีกอันคือ AppClassloader classloader นี้ใช้สำหรับโหลดคลาสในไดเรกทอรีการตั้งค่า classpath บนเครื่องของผู้ใช้ โดยปกติแล้วโดยไม่ระบุคลาส Loadinger คลาสที่กำหนดเองของโปรแกรมเมอร์จะถูกโหลดโดย classloader
เมื่อเรียกใช้โปรแกรม JVM จะเริ่มต้นและเรียกใช้ bootstrap classloader classloader โหลด Java Core API (ExtClassLoader และ AppClassLoader ยังโหลดในเวลานี้) จากนั้นเรียก ExtClassLoader เพื่อโหลด API ส่วนขยายและในที่สุด AppClassLoader จะโหลดคลาสที่กำหนดไว้ในไดเรกทอรี ClassPath นี่เป็นกระบวนการโหลดพื้นฐานที่สุดของโปรแกรม
ข้างต้นอธิบายถึงบทบาทของ classloader และกระบวนการโหลดขั้นพื้นฐานที่สุดโดยย่อ ต่อไปเราจะอธิบายวิธีการโหลด classloader ที่นี่เราต้องพูดคุยเกี่ยวกับการใช้โหมดผู้แทนหลักสำหรับการโหลดคลาส
แต่ละคลาสที่กำหนดเองจะต้องสืบทอดคลาส Abstract class loader และแต่ละ classloader จะมีคลาส parentloader เราจะเห็นได้ว่ามีวิธี getParent () ในคลาสนามธรรมคลาสโหลดซึ่งใช้เพื่อส่งคืนพาเรนต์ของคลาสโหลดปัจจุบัน โปรดทราบว่าพาเรนต์นี้ไม่ได้อ้างถึงคลาสที่สืบทอดมา แต่ classloader ที่ระบุเมื่อสร้างอินสแตนซ์ classloader หากพาเรนต์นี้เป็นโมฆะแล้วพาเรนต์เริ่มต้นของ classloader คือ bootstrap classloader การใช้งานของผู้ปกครองนี้คืออะไร?
เราสามารถพิจารณาสถานการณ์นี้ สมมติว่าเราได้ปรับแต่ง clientDefClassLoader และเราใช้คลาสที่กำหนดเองนี้เพื่อโหลด java.lang.string จากนั้นสตริงจะถูกโหลดโดย classloader นี้หรือไม่ ในความเป็นจริงคลาส java.lang.string ไม่ได้ถูกโหลดโดย clientDefClassLoader แต่โหลดโดย bootstrap classloader ทำไมสิ่งนี้ถึงเกิดขึ้น? ในความเป็นจริงนี่คือเหตุผลสำหรับโหมดผู้แทนหลักเนื่องจากก่อนที่คลาสที่กำหนดเองจะโหลดคลาสใด ๆ มันจะมอบหมายให้พ่อของคลาสโหลดให้โหลด มันจะถูกโหลดด้วยตัวเองหลังจากที่พ่อของคลาสโหลดไม่สามารถโหลดได้สำเร็จ ในตัวอย่างข้างต้นเนื่องจาก java.lang.string เป็นคลาสที่เป็นของ Java Core API ดังนั้นเมื่อใช้ clientDefClassloader เพื่อโหลดคลาส classloader จะมอบหมายให้พ่อของคุณโหลด ดังที่ได้กล่าวไว้ข้างต้นเมื่อพาเรนต์ของ classloader เป็นโมฆะพาเรนต์ของ classloader คือ bootstrap classloader ดังนั้นที่ระดับบนสุดของ classloader คือ bootstrap classloader ดังนั้นในที่สุดก็มอบหมายให้ bootstrap เมื่อมี classloader อยู่
ลองดูที่ซอร์สโค้ดชิ้นหนึ่งใน classloader:
LoadClass คลาสซิงโครไนซ์ที่ได้รับการป้องกัน (ชื่อสตริง, การแก้ไขบูลีน) พ่น classnotFoundException {// ก่อนตรวจสอบว่าคลาสที่ระบุโดยชื่อนั้นโหลดคลาส C = findLoadEdClass (ชื่อ); if (c == null) {ลอง {ถ้า (parent! = null) {// ถ้า parent ไม่ใช่ null ให้โทร loadclass ของพาเรนต์เพื่อโหลด = parent.loadclass (ชื่อ, เท็จ); } else {// parent เป็น null, call bootstrapclassloader เพื่อโหลด c = findbootstrapclass0 (ชื่อ); }} catch (classnotFoundException e) {// ถ้าการโหลดยังไม่สำเร็จให้เรียก findclass ของคุณเองเพื่อโหลด c = findclass (ชื่อ); }} ถ้า (แก้ไข) {ResolveClass (c); } return c; - จากรหัสข้างต้นเราจะเห็นว่ากระบวนการทั่วไปของการโหลดคลาสนั้นเหมือนกับตัวอย่างที่ฉันให้ไว้ก่อนหน้านี้ เมื่อเราต้องการใช้คลาสที่กำหนดเองเราจะต้องใช้วิธี FindClass เท่านั้น
ทำไมต้องใช้รูปแบบการมอบหมายหลักนี้?
เหตุผลแรกคือสิ่งนี้สามารถหลีกเลี่ยงการโหลดซ้ำ ๆ เมื่อพ่อโหลดชั้นเรียนไม่จำเป็นต้องให้คลาสเด็ก ๆ โหลดโหลดอีกครั้ง
เหตุผลที่สองคือการพิจารณาปัจจัยความปลอดภัย ลองจินตนาการว่าถ้าเราไม่ใช้โหมดตัวแทนนี้เราสามารถแทนที่ประเภทที่กำหนดใน Java Core API ได้ตลอดเวลาซึ่งจะมีความเสี่ยงด้านความปลอดภัยที่ยิ่งใหญ่มาก วิธีการมอบหมายหลักสามารถหลีกเลี่ยงสถานการณ์นี้ได้เนื่องจากสตริงถูกโหลดแล้วเมื่อเริ่มต้นดังนั้นคลาสที่ผู้ใช้กำหนดไม่สามารถโหลดคลาสที่กำหนดเองได้
ข้างต้นเป็นการแนะนำสั้น ๆ เกี่ยวกับกลไกการโหลดของ classloader ต่อไปฉันต้องอธิบายคลาสอื่นที่เกี่ยวข้องกับ classloader นั่นคือคลาสคลาส แต่ละไฟล์คลาสที่โหลดโดย classloader จะถูกอ้างอิงในที่สุดโดยโปรแกรมเมอร์เป็นอินสแตนซ์ของคลาสคลาส เราสามารถถือว่าคลาสคลาสเป็นเทมเพลตของคลาสสามัญ JVM สร้างอินสแตนซ์ที่สอดคล้องกันตามเทมเพลตนี้และในที่สุดก็ใช้โดยโปรแกรมเมอร์
เราเห็นว่ามีวิธีการที่ไม่ได้รับการตั้งชื่อในชั้นเรียน วิธีนี้เหมือนกับวิธี loadclass ใน classloader มันถูกใช้ในการโหลดคลาส แต่ทั้งสองมีฟังก์ชั่นที่แตกต่างกัน
คลาส <?> loadclass (ชื่อสตริง)
คลาส <s?> loadclass (ชื่อสตริง, Boolean Resolve)
เราเห็นการประกาศสองวิธีข้างต้น พารามิเตอร์ที่สองของวิธีที่สองใช้เพื่อตั้งค่าว่าจะเชื่อมต่อคลาสเมื่อโหลดคลาส หากเป็นจริงมันจะเชื่อมต่อมิฉะนั้นจะไม่เชื่อมต่อ
เมื่อพูดถึงการเชื่อมต่อฉันต้องอธิบายที่นี่ เมื่อโหลดคลาสโดย JVM จำเป็นต้องผ่านสามขั้นตอน: การโหลดการเชื่อมต่อและการเริ่มต้น การโหลดหมายถึงการค้นหาไฟล์คลาสที่สอดคล้องกันการอ่านลงใน JVM และการเริ่มต้นมันจะต้องมีการพูดคุยกันสิ่งที่สำคัญที่สุดคือการพูดคุยเกี่ยวกับการเชื่อมต่อ
การเชื่อมต่อแบ่งออกเป็นสามขั้นตอน ขั้นตอนแรกคือการตรวจสอบว่าชั้นเรียนตรงตามข้อกำหนดหรือไม่ ขั้นตอนที่สองคือการเตรียมการ มันคือการจัดสรรหน่วยความจำสำหรับตัวแปรคลาสและตั้งค่าเริ่มต้นเริ่มต้น ขั้นตอนที่สามคือคำอธิบาย ขั้นตอนนี้เป็นทางเลือก ตามพารามิเตอร์ที่สองของวิธี loadclass ด้านบนจะมีการพิจารณาว่าจำเป็นต้องมีคำอธิบายหรือไม่ คำอธิบายที่เรียกว่าจะขึ้นอยู่กับคำจำกัดความของหนังสือ "in-depth JVM" ซึ่งคือการค้นหาเอนทิตีที่เกี่ยวข้องตามการอ้างอิงสัญลักษณ์ในชั้นเรียนจากนั้นแทนที่การอ้างอิงสัญลักษณ์ด้วยการอ้างอิงโดยตรง มันลึกซึ้งนิดหน่อยฮ่าฮ่าฉันจะไม่อธิบายที่นี่ หากคุณต้องการทราบข้อมูลเพิ่มเติมโปรดอ่าน "JVM เชิงลึก" ฮ่าฮ่าถ้าคุณอธิบายต่อไปทีละขั้นตอนคุณจะไม่ทราบว่าจะเสร็จสิ้นเมื่อใด
ลองดูที่วิธี loadclass ด้วยพารามิเตอร์สองตัว ในเอกสาร Java API คำจำกัดความของวิธีนี้ได้รับการปกป้องซึ่งหมายความว่าวิธีการได้รับการปกป้องและวิธีการที่ผู้ใช้ควรใช้จริง ๆ คือหนึ่งเดียวที่มีพารามิเตอร์เดียว วิธี loadclass ของพารามิเตอร์หนึ่งพารามิเตอร์จริง ๆ เรียกเมธอดด้วยพารามิเตอร์สองตัวและพารามิเตอร์ที่สองเริ่มต้นเป็นเท็จ ดังนั้นจึงสามารถเห็นได้ที่นี่ว่าคลาสการโหลดผ่าน LoadClass นั้นไม่ได้อธิบายเมื่อโหลดดังนั้นคลาสจะไม่เริ่มต้น วิธีการ forname ของคลาสคลาสนั้นตรงกันข้าม เมื่อโหลดด้วยชื่อก่อนชั้นเรียนจะได้รับการอธิบายและเริ่มต้น ชื่อ forname ยังมีวิธีอื่นของวิธีการซึ่งสามารถตั้งค่าว่าจะเริ่มต้นและตั้งค่า classloader ฉันจะไม่พูดถึงเรื่องนี้มากขึ้นที่นี่
ฉันสงสัยว่าคำอธิบายของวิธีการโหลดทั้งสองนี้ชัดเจนเพียงพอหรือไม่ มายกตัวอย่างที่นี่ ตัวอย่างเช่นเมื่อโหลด ไดรเวอร์ JDBC เราจะใช้ชื่อ forname แทนวิธี loadclass ของ classloader เมื่อโหลดไดรเวอร์ JDBC? เรารู้ว่าไดรเวอร์ JDBC ผ่าน DriverManager และต้องลงทะเบียนใน DriverManager หากคลาสไดรเวอร์ไม่ได้เริ่มต้นจะไม่สามารถลงทะเบียนใน DriverManager ดังนั้นต้องใช้ชื่อ forname แทน loadclass
ผ่าน classloader เราสามารถปรับแต่งคลาสโหลดเดอร์และปรับแต่งวิธีการโหลดที่เราต้องการเช่นการโหลดจากเครือข่ายการโหลดจากรูปแบบอื่น ๆ ของไฟล์ ฯลฯ ในความเป็นจริงยังมีอีกหลายสิ่งที่ไม่ได้กล่าวถึงในชั้นเรียน
ผ่านบทความนี้บรรณาธิการหวังว่าทุกคนจะมีความเข้าใจเกี่ยวกับกลไก Classloader ขอบคุณสำหรับการสนับสนุนของคุณ!