คอนโทรลเลอร์ส่วนหน้าเป็นส่วนสำคัญที่สุดของกรอบ MVC ทั้งหมด ส่วนใหญ่จะใช้เพื่อสกัดกั้นคำขอภายนอกที่ตรงตามข้อกำหนดและแจกจ่ายคำขอไปยังตัวควบคุมที่แตกต่างกันสำหรับการประมวลผล ตามผลลัพธ์ของการประมวลผลของคอนโทรลเลอร์มันสร้างคำตอบที่สอดคล้องกันและส่งไปยังลูกค้า คอนโทรลเลอร์ส่วนหน้าสามารถใช้งานได้โดยใช้ตัวกรอง (struts2 ใช้วิธีนี้) หรือ servlet (เฟรมเวิร์กสปริง MVC)
ในฐานะที่เป็นตัวควบคุมล่วงหน้า DispatchERServlet เป็นทางเข้าเว็บเซิร์ฟเวอร์และเป็นคลาสที่สำคัญที่สุดของสปริง MVC ผ่านวงจรชีวิตความเข้าใจของเว็บเซิร์ฟเวอร์สามารถทำให้ลึกลงไปได้
วงจรชีวิตของ Servlet
ก่อนอื่นให้เรียกคืนวงจรชีวิตของ servlet:
วงจรชีวิตของ Servlet แบ่งออกเป็นสามขั้นตอน: [คำอธิบายโดยละเอียดเกี่ยวกับวัฏจักรชีวิต Servlet และหลักการทำงาน]
1. วิธีการ init () เรียกว่า init () ในขั้นตอนการเริ่มต้น หลังจากโหลด servlet คอนเทนเนอร์ servlet จะสร้างอินสแตนซ์ servlet และเรียกใช้เมธอด init () ของ servlet สำหรับการเริ่มต้น วิธีการ init () เรียกเพียงหนึ่งครั้งตลอดชีวิตของ servlet
2. โทรเมธอดบริการ () เพื่อตอบสนองต่อขั้นตอนการร้องขอลูกค้า
3. วิธีการโทรทำลาย () ในขั้นตอนการเลิกจ้าง
ขั้นตอนการเริ่มต้น Servlet
ในช่วงเวลาต่อไปนี้คอนเทนเนอร์ servlet จะโหลด servlet:
1. เมื่อคอนเทนเนอร์ Servlet เริ่มต้น Servlets บางตัวจะถูกโหลดโดยอัตโนมัติ ในการใช้งานคุณจะต้องเพิ่มรหัสต่อไปนี้ระหว่าง <servlet> </servlet> ในไฟล์ web.xml:
<loadon-startup> 1 </loadon-startup>
2. หลังจากเริ่มคอนเทนเนอร์ servlet ไคลเอ็นต์จะส่งคำขอไปยัง servlet เป็นครั้งแรก
3. หลังจากอัปเดตไฟล์คลาส servlet แล้วโหลด servlet ใหม่
โครงสร้างของ dispatcherservlet
หลังจากตรวจสอบความรู้ข้างต้นลองมาดูโครงสร้างของ Dispatcherservlet:
Dispatcherservlet สืบทอดมาจากคลาสนามธรรม: frameworkservlet, ทางอ้อมสืบทอด httpservlet (frameworkservlet สืบทอดมาจาก httpservletbean และ httpservletbean สืบทอดมาจาก httpservlet)
การเริ่มต้นเซิร์ฟเล็ต
Void IntStrategies (บริบท ApplicationContext) {InitMultipartResolver (บริบท); // ไฟล์อัปโหลดและแยกวิเคราะห์ หากประเภทคำขอเป็นหลายส่วนให้อัปโหลดไฟล์และแยกวิเคราะห์ผ่าน MultipartResolver; initlocaleresolver (บริบท); // localization แยกวิเคราะห์ initthemeresolver (บริบท); // ธีมการแยกวิเคราะห์ Inithandlermappings (บริบท); // การทำแผนที่คำขอไปยังโปรเซสเซอร์ InithandlerAdapters (บริบท); // การแมปโปรเซสเซอร์หลายประเภท inithandlerexceptionResolvers (บริบท); // สนับสนุนโปรเซสเซอร์หลายประเภทผ่าน HandlerAdapter; // หากพบข้อยกเว้นในระหว่างการดำเนินการมันจะถูกส่งไปยัง handlerexceptionResolver เพื่อแยกวิเคราะห์ initrequesttoviewiewnametranslator (บริบท); // แยกวิเคราะห์การร้องขอโดยตรงไปยังชื่อมุมมอง initViewResolvers (บริบท); // แก้ไขชื่อมุมมองเชิงตรรกะไปยังมุมมองเฉพาะผ่าน ViewResolver เพื่อใช้งาน InitFlashMapManager (บริบท); // Flash Map Manager}วิธีจัดการคำขอ:
วิธีการบริการของ Servlet จัดการคำขอ HTTP
FrameworkServlet.java กำหนดบริการและทำลายวิธีการของ servlet ดังที่แสดงด้านล่าง:
/** * แทนที่การใช้งานคลาสแม่เพื่อสกัดกั้นการร้องขอ * */ @Override Void Service (คำขอ httpservletRequest, การตอบสนอง httpservletResponse) พ่น ServleTexception, iOexception {String Method = request.getMethod (); if (method.equalsignorecase (requestmethod.patch.name ())) {processRequest (คำขอ, การตอบกลับ); } else {super.service (คำขอ, การตอบกลับ); -เรารู้ว่ามีประเภทคำขอ HTTP เจ็ดประเภท (รวมถึงตัวเลือก) ซึ่งกำหนดไว้ดังนี้:
public enum requestmethod {get, head, post, put, patch, ลบ, ตัวเลือก, การติดตาม} บริการ () ของ FrameworkServlet จัดการคำขอที่แตกต่างกัน เราใช้โพสต์ทั่วไปเพื่ออธิบาย:
/*** ประมวลผลคำขอนี้เผยแพร่เหตุการณ์โดยไม่คำนึงถึงผลลัพธ์ * <p> การจัดการเหตุการณ์จริงดำเนินการโดย Abstract * {@link #DoService} วิธีเทมเพลต */ การป้องกันโมฆะขั้นสุดท้าย ProcessRequest (คำขอ httpservletRequest, การตอบสนอง httpservletResponse) พ่น ServleTexception, iOexception {Long StartTime = System.currentTimeMillis (); ความล้มเหลวแบบโยนได้ = null; localecontext previouslocalecontext = localecontextholder.getLocalecontext (); localEcontext localEcontext = buildLocalecontext (คำขอ); requestattributes previoutattributes = requestcontextholder.getRequestatTributes (); servletrequestattributes requestattributes = buildRequestatTributes (คำขอ, การตอบสนอง, ก่อนหน้า); webasyncManager asyncManager = webasyncutils.getasyncManager (คำขอ); asyncManager.RegisterCallableablePtor (frameworkservlet.class.getName (), requestBindingInterceptor ใหม่ ()); InitContextholders (คำขอ, localecontext, requestattributes); ลอง {doservice (คำขอ, การตอบกลับ); } catch (servletexception ex) {failurecause = ex; โยนอดีต; } catch (ioexception ex) {failurecause = ex; โยนอดีต; } catch (throwable ex) {failurecause = ex; โยน nestedservletexception ใหม่ ("การประมวลผลคำขอล้มเหลว", ex); } ในที่สุด {resetContextholders (คำขอ, preventLocalecontext, previoutattributes); if (requestattributes! = null) {requestattributes.requestcompleted (); } if (logger.isdebugenabled ()) {ถ้า (failurecause! = null) {this.logger.debug ("ไม่สามารถดำเนินการตามคำขอได้", failurecause); } else {if (asyncManager.isconCurrentHandlingStarted ()) {logger.debug ("ปล่อยการตอบกลับเปิดสำหรับการประมวลผลพร้อมกัน"); } else {this.logger.debug ("คำขอเสร็จสมบูรณ์สำเร็จ"); }}} PublishRequestHandleDevent (คำขอ, เริ่มต้น, failurecause); - FrameworkServlet จะกำหนดกระแสการประมวลผลอย่างเป็นนามธรรมและปล่อยให้มันเป็น subclasses เพื่อใช้วิธีการและเสร็จสิ้นการประมวลผลคำขอที่เฉพาะเจาะจง
/** * คลาสย่อยจะต้องใช้วิธีนี้เพื่อทำงานจัดการคำขอ * ได้รับการโทรกลับส่วนกลางเพื่อรับโพสต์ใส่และลบ * <p> สัญญาเป็นหลักเช่นเดียวกับที่ใช้งานทั่วไป * {@code doget} หรือ {@code dopost} วิธีของ httpservlet * <p> คลาสนี้สกัดกั้นการโทรเพื่อให้แน่ใจว่าการจัดการข้อยกเว้นและ * สิ่งพิมพ์เหตุการณ์เกิดขึ้น * @param คำขอปัจจุบันคำขอ http * @param การตอบสนองการตอบกลับ http ปัจจุบัน * @throws ยกเว้นในกรณีของความล้มเหลวในการประมวลผลใด ๆ * @see javax.servlet.http.httpservlet#doget * @see javax.servlet.http.httpsert การตอบสนอง httpservletResponse) โยนข้อยกเว้น;การใช้งานเฉพาะมีดังนี้:
/** * เปิดเผยแอตทริบิวต์การร้องขอเฉพาะ dispatcherservlet และมอบหมายให้ {@link #dodispatch} * สำหรับการส่งจริง */ @Override Void Doservice (คำขอ httpservletRequest, การตอบกลับ httpservletResponse) โยนข้อยกเว้น {ถ้า (logger.isdebugenabled ()) {สตริงกลับมา = webasyncutils.getasyncManager (คำขอ) "ดำเนินการต่อ": ""; logger.debug ("dispatcherservlet พร้อมชื่อ '" + getServletName () + "'" + กลับมาทำงาน + "การประมวลผล" + request.getMethod () + "คำขอ [" + getRequesturi (คำขอ) + "]"); } // เก็บสแนปชอตของแอตทริบิวต์คำขอในกรณีที่มีการรวม // เพื่อให้สามารถกู้คืนแอตทริบิวต์ดั้งเดิมหลังจากรวม แผนที่ <สตริงวัตถุ> attributeSnapShot = null; if (webUtils.isincluderequest (คำขอ)) {attributeSnapShot = new hashmap <string, object> (); การแจงนับ <?> attrNames = request.getAttributenames (); ในขณะที่ (attrNames.hasmoreElements ()) {string attrName = (string) attrNames.nextElement (); if (this.cleanupafterinclude || attrName.startswith ("org.springframework.web.servlet")) {attributeSnapshot.put (attrName, request.getAttribute (attrName)); }}}} // ทำให้วัตถุเฟรมเวิร์กพร้อมใช้งานสำหรับตัวจัดการและดูวัตถุ request.setAttribute (web_application_context_attribute, getWebapplicationContext ()); request.setAttribute (locale_resolver_attribute, this.localeresolver); request.setAttribute (Theme_Resolver_attribute, this.themeresolver); request.setAttribute (Theme_source_attribute, getthemesource ()); flashmap inputflashmap = this.flashmapManager.RetrieveandUpdate (คำขอ, การตอบสนอง); if (inputFlashMap! = null) {request.setAttribute (input_flash_map_attribute, collections.unmodifiablemap (inputflashmap)); } request.setAttribute (output_flash_map_attribute, flashmap ใหม่ ()); request.setAttribute (flash_map_manager_attribute, this.flashmapmanager); ลอง {dodispatch (คำขอ, การตอบกลับ); } ในที่สุด {ถ้า (webasyncutils.getasyncManager (คำขอ) .isconcurrenthandlingstarted ()) {return; } // กู้คืนสแนปชอตแอตทริบิวต์ดั้งเดิมในกรณีที่มีการรวม if (attributeSnapShot! = null) {restoreattributesafterinclude (คำขอ, attributeSnapShot); -ไฮไลต์ในฐานะการดำเนินการของผู้จัดจำหน่ายคำขอ:
ฟังก์ชั่น: 1. แจกจ่ายคำขอไปยังตัวจัดการ (รับความสัมพันธ์การแมปเซเล็ตตามลำดับของการกำหนดค่า); 2. คำถามตัวจัดการคนแรกที่สามารถประมวลผลได้ตามตัวจัดการที่ติดตั้งโดย servlet; 3. ตัวจัดการทริกเกอร์การประมวลผลคำขอ
/*** ประมวลผลการส่งจริงไปยังตัวจัดการ * <p> ตัวจัดการจะได้รับโดยการใช้ handlermappings ของ servlet ตามลำดับ * HandlerAdapter จะได้รับโดยการสอบถามตัวจัดการ HandlerAdapters ที่ติดตั้งของ Servlet * เพื่อค้นหาครั้งแรกที่รองรับคลาส Handler * <p> วิธีการ HTTP ทั้งหมดได้รับการจัดการโดยวิธีนี้ มันขึ้นอยู่กับ HandlerAdapters หรือ Handlers * ตัวเองในการตัดสินใจว่าวิธีการใดที่ยอมรับได้ * @param Request Request HTTP ปัจจุบัน * @Param Response การตอบกลับ HTTP ปัจจุบัน * @THROWS EXCAMENT ในกรณีของความล้มเหลวในการประมวลผลใด ๆ */ Void Dodispatch ที่ได้รับการป้องกัน (คำขอ httpservletRequest, httpservletResponse) การตอบกลับ handlerexecutionchain mappedhandler = null; บูลีน MultipartRequestParsed = false; webasyncManager asyncManager = webasyncutils.getasyncManager (คำขอ); ลอง {modelandView mv = null; ข้อยกเว้น dispatchexception = null; ลอง {processedRequest = checkMultipart (คำขอ); MultipartRequestParsed = (ProcessEdRequest! = คำขอ); // กำหนดตัวจัดการสำหรับคำขอปัจจุบัน mappedHandler = gethandler (processedRequest); if (mappedhandler == null || mappedhandler.gethandler () == null) {nohandlerfound (processedRequest, การตอบสนอง); กลับ; } // กำหนดอะแดปเตอร์ Handler สำหรับคำขอปัจจุบัน HandlerAdapter HA = GethandlerAdapter (mappedhandler.gethandler ()); // กระบวนการส่วนหัวที่ดัดแปลงล่าสุดหากรองรับโดยตัวจัดการ String method = request.getMethod (); บูลีน isget = "get" .equals (วิธีการ); if (isget || "head" .equals (วิธีการ)) {Long LastModified = ha.getLastModified (คำขอ, mappedhandler.gethandler ()); if (logger.isdebugenabled ()) {logger.debug ("ค่าที่แก้ไขล่าสุดสำหรับ [" + getRequesturi (คำขอ) + "] คือ:" + lastModified); } ถ้า (ใหม่ servletwebrequest (คำขอ, การตอบกลับ) .CheckNotModified (lastModified) && isget) {return; }} ถ้า (ใหม่ servletwebrequest (คำขอ, การตอบสนอง) .CheckNotModified (lastModified) && isget) {return; }} if (! mappedhandler.applyprehandle (processedRequest, การตอบสนอง)) {return; } ลอง {// เรียกใช้ตัวจัดการจริง ๆ mv = ha.handle (processedrequest, การตอบสนอง, mappedhandler.gethandler ()); } ในที่สุด {ถ้า (asyncManager.isconCurrentHandlingStarted ()) {return; }} appledefaultViewName (คำขอ, MV); mappedhandler.applyposthandle (ProcessedRequest, Response, MV); } catch (exception ex) {dispatchexception = ex; } processDispatchResult (ProcessEdRequest, การตอบสนอง, MappedHandler, MV, Dispatchexception); } catch (exception ex) {triggeraftercompletion (processedRequest, การตอบสนอง, mappedhandler, ex); } catch (ข้อผิดพลาดผิดพลาด) {triggeraftercompletionwitherror (processedRequest, การตอบสนอง, mappedhandler, err); } ในที่สุด {ถ้า (asyncManager.isconCurrentHandlingStarted ()) {// แทนที่จะเป็น postthandle และ aftercompletion mappedhandler.applyafterconcurrenthandlingstarted (processedrequest, การตอบสนอง); กลับ; } // ทำความสะอาดทรัพยากรใด ๆ ที่ใช้โดยคำขอหลายส่วน if (MultipartRequestParsed) {cleanupMultipart (processedRequest); -การทำลายเซิร์ฟเล็ต
/*** ปิด webApplicationContext ของ servlet นี้ * @See org.springframework.context.configurableapplicationContext#close () */ @Override โมฆะสาธารณะทำลายล้าง () {getServletContext (). log ("ทำลาย FrameworkServlet '" + GetServletName () + ""); // เฉพาะการโทรปิด () บน webApplicationContext หากมีการจัดการในเครื่อง ... ถ้า (this.webapplicationContext อินสแตนซ์ของ configurableapplicationContext &&! this.webapplicationContextInjected) {(configurableApplicationContext) this.webapplicationContext) .close (); -สรุป:
เนื่องจากข้อ จำกัด ของบทบทความนี้แนะนำกระบวนการประมวลผลคำขอเท่านั้นและไม่ได้ทำการวิเคราะห์เชิงลึกของรหัส บทความถัดไปจะเริ่มต้นจากรายละเอียดและวิเคราะห์ความงามของรหัสของฤดูใบไม้ผลิ
ข้างต้นเป็นเนื้อหาทั้งหมดของบทความนี้ ฉันหวังว่ามันจะเป็นประโยชน์ต่อการเรียนรู้ของทุกคนและฉันหวังว่าทุกคนจะสนับสนุน wulin.com มากขึ้น