ทำไมเริ่มดูซอร์สโคจสปริง
ฉันเขียนรหัสครึ่งทางผ่านอาชีพมาเกือบปีครึ่ง ฉันใช้กรอบฤดูใบไม้ผลิตั้งแต่ฉันเริ่มทำงาน แม้ว่าฉันสามารถใช้มันและสร้างมันได้ แต่ฉันมักจะไม่เข้าใจหลักการที่อยู่เบื้องหลังเช่น: วิธีการควบคุมการทำธุรกรรมฤดูใบไม้ผลิวิธีการที่ SpringMVC จัดการกับการร้องขอและวิธีการดำเนินการของ AOP ... สิ่งนี้ทำให้ผู้คนรู้สึกไม่สบายใจมาก
วิธีดูซอร์สโค้ดอย่างมีประสิทธิภาพ
คำตอบของฉันคือดูซอร์สโค้ดที่มีคำถามเฉพาะมิฉะนั้นจะติดอยู่ในรายละเอียดซอร์สโค้ดได้ง่ายมากแล้วคุณจะเป็นลม ในที่สุดคุณจะพบสิ่งที่คุณอ่านมานานแล้ว
การแนะนำ
ฤดูใบไม้ผลิเป็นเฟรมเวิร์กระดับการออกแบบโอเพ่นซอร์สที่แก้ปัญหาการมีเพศสัมพันธ์แบบหลวมระหว่างเลเยอร์ตรรกะทางธุรกิจและเลเยอร์อื่น ๆ และรวมแนวคิดการเขียนโปรแกรมที่เน้นอินเทอร์เฟซตลอดทั้งแอปพลิเคชันระบบทั้งหมด นอกจากนี้ยังเป็นหนึ่งในทักษะที่สำคัญในงาน Java ...
เนื่องจากบันทึกกระบวนการวิเคราะห์ซอร์สโค้ดสปริงฉันจะไม่อธิบายรายละเอียดเกี่ยวกับการใช้อย่างละเอียด
รหัสหลัก
<Effercing> <roupID> org.springframework </groupId> <ratifactId> Spring-Context </artifactid> <version> 5.0.2.release </version>
การใช้งาน
แอปพลิเคชันระดับสาธารณะ {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {beandefinitionRegistry beanfactory = ใหม่ defaultListableBeanFactory (); XMLBEANDEFINITIONREADER Reader = ใหม่ XMLBEANDEFINITIONREADER (BeanFactory); classPathResource Resource = ใหม่ classPathResource ("bean.xml"); // จุดเริ่มต้นสำหรับการโหลดทรัพยากรทั้งหมด Reader.LoadBeanDefinitions (ทรัพยากร); -การถอดรหัส
DefaultListableBeanFactory เป็นการใช้งานเริ่มต้นของการลงทะเบียนสปริงและการโหลดถั่ว มันสามารถเรียกได้ว่าบรรพบุรุษในเทมเพลต IOC ฤดูใบไม้ผลิทั้งหมด
การติดตาม DefaultListableBeanFactory คุณสามารถค้นหาบล็อกรหัสต่อไปนี้ จุดประสงค์ของการออกแบบนี้คืออะไร?
Public AbstractautowirecapableBeanFactory () {super (); เพิกเฉยต่อการพึ่งพา INTERFACE (BeannameAwer.class); เพิกเฉยต่อการพึ่งพา Interface (beanfactoryaware.class); เพิกเฉยต่อการพึ่งพาตัวอย่างเช่นเมื่อมีแอตทริบิวต์ B ใน A สปริงจะยกตัวอย่างแอตทริบิวต์โดยอัตโนมัติหากพบว่าแอตทริบิวต์ B ไม่ได้สร้างอินสแตนซ์เมื่อได้รับแอตทริบิวต์ A นี่เป็นคุณสมบัติที่สำคัญในฤดูใบไม้ผลิ ในบางกรณี B จะไม่เริ่มต้นเช่นการใช้อินเทอร์เฟซ BeannameAwer
สปริงแนะนำสิ่งนี้: เมื่อการประกอบอัตโนมัติไม่สนใจอินเทอร์เฟซการพึ่งพาที่กำหนดเช่นการแยกวิเคราะห์การลงทะเบียนบริบทของแอปพลิเคชันผ่านวิธีการอื่น ๆ คล้ายกับการฉีด beanfactory ผ่าน beanfactoryaware หรือการฉีดแอปพลิเคชันผ่านแอปพลิเคชัน
การจัดการทรัพยากร
อินเทอร์เฟซทรัพยากรใช้ในการจัดการไฟล์ URL, classpath และทรัพยากรอื่น ๆ ทรัพยากรมีหน้าที่รับผิดชอบในการอ่านไฟล์การกำหนดค่านั่นคือการห่อหุ้มไฟล์การกำหนดค่าลงในทรัพยากรแล้วส่งไปยัง XMLBeanDefinitionReader สำหรับการประมวลผล
การแยกวิเคราะห์ XML
XMLBEANDEFINITIONREADER เป็นการใช้งานการอ่านไฟล์สปริงการแยกวิเคราะห์และการลงทะเบียนและเราควรมุ่งเน้นไปที่คลาสนี้
ติดตาม reader.loadBeanDefinitions(resource); เราสามารถดูรหัสหลักต่อไปนี้ (ยกเว้นความคิดเห็นและโยนข้อยกเว้น)
public int loadBeanDefinitions (encodedResource encodedResource) พ่น beandefinitionstoreexception {ลอง {inputstream inputstream = encodedResource.getResource () getInputStream (); ลอง {inputSource inputSource = new InputSource (InputStream); if (encodedResource.getEncoding ()! = null) {inputsource.setencoding (encodedResource.getEncoding ()); } return doLoadBeanDefinitions (inputSource, encodedResource.getResource ()); } ในที่สุด {inputStream.close (); -รหัสข้างต้นดำเนินการการเข้ารหัสบนทรัพยากรครั้งแรกโดยมีวัตถุประสงค์เพื่อกังวลเกี่ยวกับปัญหาการเข้ารหัสใน XML
หากคุณสังเกต InputSource inputSource = new InputSource(inputStream); ชื่อแพ็คเกจของมันคือ org.xml.sax จริง ๆ ดังนั้นเราจึงสามารถสรุปได้ว่า Spring ใช้การแยกวิเคราะห์ SAX และใช้ InputSource เพื่อตัดสินใจว่าจะอ่านไฟล์ XML อย่างไร
ในที่สุดข้อมูลที่เตรียมไว้จะถูกส่งผ่านไปยังส่วนการประมวลผลหลักจริงผ่านพารามิเตอร์ doLoadBeanDefinitions(inputSource, encodedResource.getResource())
รับเอกสาร
1. doLoadBeanDefinitions(inputSource, encodedResource.getResource()); ละเว้นการจับและความคิดเห็นหลายครั้ง
ได้รับการป้องกัน int doLoadBeanDefinitions (inputSource inputSource, ทรัพยากรทรัพยากร) พ่น beandefinitionstoreexception {ลอง {document doc = doLoadDocument (InputSource, Resource); ส่งคืน RegisterBeanDefinitions (DOC, Resource); - 2. doLoadDocument(inputSource, resource);
เอกสารที่ได้รับการป้องกัน doLoadDocument (InputSource InputSource, Resource Resource) โยนข้อยกเว้น {ส่งคืน this.documentloader.loadDocument (inputSource, getEntityResolver (), this.errorhandler, getValidationModeForResource (ทรัพยากร), isnamespaceaware ();ก่อนอื่นคุณสามารถรับโหมดการตรวจสอบ (DTD หรือ XSD) ของไฟล์ XML ผ่าน GetValidationModeForResource คุณสามารถตั้งค่าวิธีการตรวจสอบด้วยตัวเอง โดยค่าเริ่มต้นการตรวจสอบความถูกต้อง _auto ถูกเปิดใช้งานนั่นคือโหมดการตรวจสอบจะได้รับโดยอัตโนมัติ อ่านไฟล์ XML ผ่าน InputStream และตรวจสอบว่ามีคำ Doctype หรือไม่ ถ้ามันมีมันเป็น DTD มิฉะนั้นจะส่งคืน XSD
รูปแบบการตรวจสอบไฟล์ XML ทั่วไปคือ:
คลาสสาธารณะ XMLVALIdingModeDetector { /*** ระบุว่าควรใช้การตรวจสอบความถูกต้องของ DTD (เราพบการประกาศ "doctype") */ สาธารณะคงที่ int validation_dtd = 2; /*** ระบุว่าควรใช้การตรวจสอบความถูกต้องของ XSD (พบว่าไม่มีการประกาศ "doctype") */ สาธารณะคงที่ int validation_xsd = 3; public int detectValidationMode (inputStream inputStream) พ่น IOException {}} พารามิเตอร์ EntityResolver มีส่วนร่วมในวิธี this.documentLoader.loadDocument Method
เอกสารสาธารณะ loadDocument (InputSource InputSource, EntityResolver EntityResolver, ErrorHandler ErrorHandler, Int ValidationMode, Boolean namespaceaware) โยนข้อยกเว้น {}EntityResolver คืออะไร? คำอธิบายอย่างเป็นทางการ: หากแอปพลิเคชัน SAX จำเป็นต้องใช้การประมวลผลที่กำหนดเองของเอนทิตีภายนอกจะต้องใช้อินเทอร์เฟซนี้และลงทะเบียนอินสแตนซ์ด้วยไดรฟ์ SAX โดยใช้วิธี SetEntityResolver กล่าวคือสำหรับการแยกวิเคราะห์ XML แซ็กโซโฟนจะอ่านคำประกาศในเอกสาร XML ก่อนและค้นหาคำจำกัดความ DTD ที่สอดคล้องกันตามการประกาศเพื่อตรวจสอบเอกสารกฎการค้นหาเริ่มต้น (เช่นการดาวน์โหลดเครือข่ายดาวน์โหลดคำจำกัดความ DTD ผ่านที่อยู่ DTD URI ที่ประกาศโดย XML) กระบวนการดาวน์โหลดเป็นกระบวนการที่ยาวนานและเมื่อเครือข่ายไม่พร้อมใช้งานจะมีการรายงานข้อผิดพลาดที่นี่เนื่องจากไม่พบ DTD ที่เกี่ยวข้อง
ฟังก์ชั่นของ EntityResolver คือโครงการเองสามารถให้วิธีการหาคำประกาศ DTD นั่นคือโปรแกรมดำเนินการตามกระบวนการค้นหา DTD ซึ่งหลีกเลี่ยงการค้นหาการประกาศที่สอดคล้องกันผ่านเครือข่าย
3.EntityResolver ยอมรับสองพารามิเตอร์:
Public Abstract InputSource ResolveEntity (String PublicId, String SystemID) พ่น SaxException, IOException;
3.1 กำหนดไฟล์ bean.xml พร้อมเนื้อหาดังนี้ (โหมด XSD)
<? xml version = "1.0" การเข้ารหัส = "utf-8"?> <beans xmlns = "http://www.springframework.org/schema/beans" xmlns: xsi = "http://ww.w3.org/2001/xml XSI: schemalocation = "http://www.springframework.org/schema/beans.xsd"> </epeal>
แยกวิเคราะห์พารามิเตอร์สองตัวต่อไปนี้:
3.2 กำหนดไฟล์ bean.xml พร้อมเนื้อหาดังนี้ (โหมด DTD)
<? xml เวอร์ชัน = "1.0" การเข้ารหัส = "utf-8"?> <! doctype beans สาธารณะ "-// spring // dtd bean 2.0 // en" "http://www.springframework.org/dtd/spring-beans.dtd"
แยกวิเคราะห์พารามิเตอร์สองตัวต่อไปนี้:
3.3 สปริงใช้การมอบหมายงานที่เป็นตัวรวมการแยกวิเคราะห์ entityResolver
ระดับสาธารณะ delegatingEntityResolver {@Override @Nullable Public Public InputSource Resolveentity (String Publicid, @Nullable String SystemId) พ่น SaxException, iOexception {ถ้า (SystemId! = null) {ถ้า (systemid.endswith } อื่นถ้า (systemId.endswith (xsd_suffix)) {return this.schemaresolver.resolveentity (publicid, systemid); }} return null; -เราจะเห็นว่าใช้ตัวแยกวิเคราะห์ที่แตกต่างกันสำหรับโหมดที่แตกต่างกัน
ลงทะเบียนถั่ว
หลังจากอ่านการวิเคราะห์การแยกวิเคราะห์ XML ให้ติดตามรหัสต่อไปและดูว่า Spring Registers Bean ข้อมูลตามเอกสารได้อย่างไร
คลาสสาธารณะ XMLBEANDEFINITIONREADER {Public Int RegisterBeanDefinitions (เอกสารเอกสารทรัพยากรทรัพยากร) พ่น beanDefinitionStorEException {// สร้าง documentReader BEANDEFINITIONDOCUMENTREADER DOCICANTREADER = CREATEBEANDEFINITIONDOCUMENTREADER (); // บันทึกจำนวน beandefinitions ก่อนสถิติ int countbefore = getRegistry (). getBeanDefinitionCount (); // ลงทะเบียน beandefinition documentreader.registerBeandEfinitions (DOC, CreateDerContext (ทรัพยากร)); // บันทึกจำนวน beandefinitions ที่โหลดในครั้งนี้ส่งคืน getRegistry (). getBeanDefinitionCount () - countbefore; -เมื่อลงทะเบียนถั่วก่อนอื่นให้ใช้คลาส BeandefinitionParserDelegate เพื่อพิจารณาว่าเป็นเนมสเปซเริ่มต้นหรือไม่ การใช้งานคือการพิจารณาว่า URI เนมสเปซเท่ากับ URI เริ่มต้นหรือไม่:
คลาสสาธารณะ beandefinitionParserDelegate {สตริงคงสุดท้ายสาธารณะ beans_namespace_uri = "http://www.springframework.org/schema/beans"; บูลีนสาธารณะ isdefaultnamespace (@nullable สตริง namespaceuri) {return (! stringutils.haslength (namespaceuri) || beans_namespace_uri.equals (namespaceuri)); - ติดตาม documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); โดยที่เอกสารถูกแปลงผ่าน loadDocument ในบล็อกรหัสก่อนหน้า วัตถุประสงค์หลักของวิธีนี้คือการแยกโหนดรูท (ถั่ว)
คลาสสาธารณะ defaultBeanDefInitionDocumentReader {@Override โมฆะสาธารณะ registerBeanDefinitions (เอกสารเอกสาร, XMLReaderContext ReaderContext) {this.readerContext = readerContext; logger.debug ("การโหลดคำจำกัดความถั่ว"); Element root = doc.getDocumentElement (); Doregisterbeandefinitions (รูท); - ติดตาม doRegisterBeanDefinitions(root) และเราจะเห็นโฟลว์การประมวลผลต่อไปนี้
เป็นโมฆะที่ได้รับการป้องกัน DoregisterBeanDefinitions (Element Root) {// ... String profileSpec = root.getAttribute (profile_attribute); // ... // การใช้งานที่ว่างเปล่า preprocessXML (รูท); ParsebeanDefinitions (root, this.delegate); // การใช้งานที่ว่างเปล่า PostprocessXML (รูท); this.delegate = parent;}ขั้นแรกแยกวิเคราะห์โปรไฟล์ (วิธีการเล่นทั่วไปคือวัตถุถั่วที่เริ่มต้นด้วยโปรไฟล์ที่แตกต่างกันนั้นแตกต่างกันดังนั้นพวกเขาจึงใช้หลายสภาพแวดล้อม)
การแยกวิเคราะห์ต่อไปนี้ใช้โหมดวิธีการเทมเพลตโดยที่ preprocessXML และ postprocessXML เป็นทั้งวิธีที่ว่างเปล่าดังนั้น subclasses ที่ตามมาสามารถทำการประมวลผลบางอย่างก่อนและหลังการแยกวิเคราะห์ แทนที่ทั้งสองวิธีนี้
วิเคราะห์และลงทะเบียน beandefinition ส่วนนี้ของรหัสนี้ค่อนข้างง่าย
คลาสสาธารณะ defaultBeanDefinitionDocumentReader { / *** แก้ไขโหนดอื่น ๆ ภายใต้รูทโหนดนำเข้า "," นามแฝง "," ถั่ว ".* @param รูทชื่อโหนดชื่อ* / void void parsebeandefinitions root.getchildnodes (); Delegate.parsecustomelement (Ele);}}}} else {Delegate.parsecustomelement (รูท); importBeanDefinitionResource (ELE); (Delegate.nodenameeQuals (ele, nested_beans_element)) {// recurse doregisterbeandefinitions (Ele); Bdholder = dexter.parsebeandefinitionelement (ELE); getReaderContext (). getRegistry ()); BeanComponentDefinition (Bdholder));}}} มอบหมายวิธี ParsebeanDefinitionElement ของคลาส BeanDefinitionParserDelegate สำหรับการแยกวิเคราะห์องค์ประกอบและส่งคืนอินสแตนซ์ของ BDHOLDER BDENTICITY BDELDER (รวมถึงคุณลักษณะต่างๆของคลาสไฟล์การกำหนดค่าชื่อชื่อนามแฝง ฯลฯ )
เมื่อผู้ถือ BDHolder ที่ส่งคืนไม่ว่างเปล่าหากมีแอตทริบิวต์ที่กำหนดเองในโหนดลูกของป้ายกำกับเริ่มต้นฉลากที่กำหนดเองจะถูกแยกวิเคราะห์และแยกวิเคราะห์อีกครั้งจากนั้น BeanDefinitionReaderUtils.registerBeanDefinition(); ลงทะเบียนผู้ถือ BDHolder และส่งเหตุการณ์การลงทะเบียนแจ้งถั่วฟังที่เกี่ยวข้องว่าการลงทะเบียนประสบความสำเร็จ
สรุป
หลังจากฤดูใบไม้ร่วงฤดูใบไม้ร่วงฤดูใบไม้ผลิและฤดูร้อนทุกอย่างจะเป็นไปตามทิศทางที่คุณต้องการ ...
โอเคข้างต้นเป็นเนื้อหาทั้งหมดของบทความนี้ ฉันหวังว่าเนื้อหาของบทความนี้จะมีค่าอ้างอิงบางอย่างสำหรับการศึกษาหรือที่ทำงานของทุกคน หากคุณมีคำถามใด ๆ คุณสามารถฝากข้อความไว้เพื่อสื่อสาร ขอบคุณสำหรับการสนับสนุน Wulin.com
พูดอะไรบางอย่าง
รหัสข้อความแบบเต็ม: https://gitee.com/battcn/battcn-spring-source/tree/master/chapter1 (ดาวน์โหลดท้องถิ่น)