0 สรุป
บทความนี้อธิบายสั้น ๆ เกี่ยวกับลิงก์การแมปโปรเซสเซอร์ของ SpringMVC จากระดับซอร์สโค้ดนั่นคือกระบวนการโดยละเอียดในการค้นหาคอนโทรลเลอร์
1 กระบวนการคำขอ SpringMVC
คอนโทรลเลอร์ค้นหาขั้นตอนที่ 1 ถึง 2 ที่สอดคล้องกับภาพด้านบน
แผนภูมิโฟลว์การดำเนินการโดยละเอียดของ SpringMVC
2 กระบวนการเริ่มต้น SpringMVC
2.1 คนแรกเข้าใจสองประเภท
1. requestmappinginfo
encapsulation requestmapping คำอธิบายประกอบ
มีข้อมูลที่เกี่ยวข้องเกี่ยวกับส่วนหัวคำขอ HTTP
อินสแตนซ์สอดคล้องกับคำอธิบายประกอบการร้องขอ
2. handlermethod
วิธีการจัดการคำขอสำหรับการห่อหุ้มคอนโทรลเลอร์
รวมวัตถุถั่วที่เป็นของวิธีการวัตถุวิธีที่สอดคล้องกับวิธีการพารามิเตอร์ของวิธีการ ฯลฯ
requestmappinghandlermapping ความสัมพันธ์การสืบทอด
ในระหว่างการเริ่มต้น SpringMVC
ก่อนอื่นให้ดำเนินการ fterpropertiesset ของ requestmappinghandlermapping
จากนั้นป้อน abstracthandlermethodmapping afterpropertiesset
วิธีนี้จะเข้าสู่ Inithandlermethods ของคลาสนี้
รับผิดชอบในการสแกนถั่วจาก ApplicationContext จากนั้นค้นหาและลงทะเบียนวิธีโปรเซสเซอร์จากถั่ว
// สแกนถั่วใน ApplicationContext, ตรวจจับและลงทะเบียนวิธีการจัดการโมฆะที่ป้องกันไม่ได้ Inithandlermethods () {... // รับถั่วทั้งหมดในสตริง applicationcontext [] beannames = (this.detecthandlermethodsinancestorContexts? getApplicationContext (). getBeanNamesFortype (Object.class)); // โอนอาร์เรย์ BeanName สำหรับ (สตริง BeanName: BeanNames) {// Ishandler จะตัดสินว่าคำจำกัดความของถั่วมีคำอธิบายประกอบของคอนโทรลเลอร์หรือคำอธิบายประกอบการร้องขอการใช้งานตามถั่วหรือไม่ if (iShandler (getApplicationContext (). getType (BeanName))) {detecthandLermethods (BeanName); }} handlermethodsinitialized (gethandlermethods ());} requestmappinghandlermapping#ishandler
วิธีการข้างต้นคือการพิจารณาว่าคำจำกัดความของถั่วปัจจุบันมีคำอธิบายประกอบหรือคำอธิบายประกอบการร้องขอการร้องขอ
หากการร้องขอการทำเช่นนั้นมีผลบังคับใช้เท่านั้น? เลขที่!
เพราะในกรณีนี้ชั้นเรียนจะไม่ถูกลงทะเบียนเป็นถั่วฤดูใบไม้ผลิเมื่อเริ่มต้นฤดูใบไม้ผลิและชั้นเรียนจะไม่ถูกสำรวจเมื่อข้ามชื่อ Beanname ดังนั้นจึงสามารถแทนที่คอนโทรลเลอร์ด้วย compoent ที่นี่ แต่นี่ไม่ได้ทำโดยทั่วไป
หลังจากยืนยันว่าถั่วเป็นตัวจัดการวิธีการจัดการเฉพาะจะพบได้จากถั่ว (นั่นคือวิธีการประมวลผลคำขอที่กำหนดไว้ในคลาสคอนโทรลเลอร์) รหัสค้นหามีดังนี้
/** * มองหาวิธีการจัดการในตัวจัดการ * @param Handler ชื่อถั่วของตัวจัดการหรืออินสแตนซ์ของตัวจัดการ */void detecthandlermethods (ตัวจัดการวัตถุสุดท้าย) {// รับวัตถุคลาสของคอนโทรลเลอร์ปัจจุบัน <?> handlerType = getApplicationContext (). getType ((สตริง) ตัวจัดการ): handler.getClass (); // หลีกเลี่ยงการโทรซ้ำไปยัง getMappingFormethod เพื่อสร้างอินสแตนซ์ requestmappingInfo แผนที่สุดท้าย <method, t> mappings = new IdentityHashMap <method, t> (); // เช่นเดียวกับข้างต้นมันเป็นวัตถุคลาสของคลาสคอนโทรลเลอร์ถั่วสุดท้าย <?> userType = classutils.getUserClass (HandlerType); // รับวิธีการจัดการทั้งหมดของถั่วปัจจุบัน // กำหนดว่ามีการร้องขอการทำแผนที่ตามเมธอด // ถ้ามีให้สร้างอินสแตนซ์อินสแตนซ์ requestmappinginfo <method> วิธีการ = handlermethodselector.selectMethods (UserType, methodFilter ใหม่ (การแมป! = null) {mappings.put (วิธีการแมป); // เดินทางและลงทะเบียนวิธีการจัดการทั้งหมดของถั่วปัจจุบันสำหรับ (วิธีการ: วิธีการ) {// เมธอดตัวจัดการลงทะเบียนและป้อนวิธีการต่อไปนี้ registerhandlermethod (handler, method, mappings.get (วิธีการ)); - มีสองสถานที่ในรหัสด้านบนที่เรียก getmappingformethod
สร้าง requestMappingInfo โดยใช้วิธีการและระดับการร้องขอคำอธิบาย
@Override Protected RequestMappingInfo getMappingFormethod (วิธีการ, คลาส <?> handlerType) {requestMappingInfo info = null; // รับ @requestmapping requestmapping methodannotation = annotationutils.findannotation (วิธีการ, requestmapping.class); if (methodAnnotation! = null) {requestCondition <?> methodCondition = getCustommetHodCondition (วิธีการ); info = createRequestMappingInfo (methodannotation, methodCondition); // รับคำอธิบายประกอบ @requtestmapping ของถั่วซึ่งวิธีการที่เป็นไปตามคำขอ mapping typeannotation = Annotationutils.findannotation (handlerType, requestmapping.class); if (typeanNotation! = null) {requestCondition <?> typecondition = getCustomTypecondition (handlerType); // รวมสอง @RequestMapping คำอธิบายประกอบข้อมูล = createRequestMappingInfo (typeannotation, typecondition) .combine (ข้อมูล); }} ข้อมูลส่งคืน; - วัตถุประสงค์ของวิธีนี้คือการสร้างวัตถุ RequestMappingInfo ตามวิธีการจัดการ ก่อนอื่นตรวจสอบว่า mehtod มีคำอธิบายประกอบการร้องขอหรือไม่ ถ้าเป็นเช่นนั้นให้สร้างวัตถุ RequestMappingInfo โดยตรงตามเนื้อหาของคำอธิบายประกอบ หลังจากการสร้างแล้วให้ตรวจสอบว่าถั่วที่วิธีการปัจจุบันมีคำอธิบายประกอบการร้องขอหรือไม่ หากรวมคำอธิบายประกอบนี้วัตถุ RequestMappingInfo จะถูกสร้างขึ้นตามคำอธิบายประกอบในชั้นเรียน จากนั้นวัตถุ RequestMappingInfo บนวิธีการผสานจะถูกส่งคืนและวัตถุที่ผสานจะถูกส่งคืนในที่สุด ตอนนี้เมื่อมองย้อนกลับไปที่วิธี detecthandlermethods มีการโทรสองครั้งเพื่อ getMappingformethod วิธี โดยส่วนตัวแล้วฉันคิดว่าสิ่งนี้สามารถปรับให้เหมาะสมได้ เมื่อตัดสินว่าวิธีการนั้นเป็นตัวจัดการตั้งแต่แรกวัตถุที่สร้างขึ้นมา MappingInfo สามารถบันทึกและใช้งานได้โดยตรงในภายหลังซึ่งหมายความว่ากระบวนการสร้างวัตถุ RequestMappingInfo หายไป จากนั้นป้อนเมธอด registerhandlermehtod ทันทีดังนี้
Void Void registerhandlermethod (Handler Object, วิธีการ, การแมป t) {// สร้าง handlermethod handlermethod newhandlermethod = createHandLermethod (Handler, Method); handlemthod oldhandlermethod = handlermethods.get (การทำแผนที่); // ตรวจสอบว่ามีความคลุมเครือในการกำหนดค่าหรือไม่ถ้า (oldhandlermethod! = null &&! oldhandlermethod.equals (newhandlermethod)) {โยน regenlstateException ใหม่ ("การทำแผนที่ที่ไม่ชัดเจนไม่สามารถทำแผนที่" newhandlermethod.getBean () oldhandlermethod.getBean () + "'bean method/n" + oldhandlermethod + "แมป"); } this.handlermethods.put (การแมป, newhandlermethod); if (logger.isinfoenabled ()) {logger.info ("แมป /" " + การแมป +" /"ลงบน" + newhandlermethod); } // รับค่า @RequestMapping คำอธิบายประกอบจากนั้นเพิ่มค่า-> requestmappingInfo บันทึกการแมปการแมปลงในชุด URLMAP <String> รูปแบบ = getMappingPathPattern (การแมป); สำหรับ (รูปแบบสตริง: รูปแบบ) {ถ้า (! getPathMatcher (). ispattern (รูปแบบ)) {this.urlmap.add (รูปแบบ, การแมป); - ที่นี่ประเภทของ t คือ requestmappinginfo วัตถุนี้เป็นข้อมูลที่เกี่ยวข้องของคำอธิบายประกอบการร้องขอของวิธีการภายใต้คอนโทรลเลอร์เฉพาะที่ถูกห่อหุ้ม คำอธิบายประกอบการร้องขอที่สอดคล้องกับวัตถุคำขอ mappinginfo Handlermethod นั้นคล้ายคลึงกับ RequestMappingInfo และเป็นการห่อหุ้มวิธีการประมวลผลเฉพาะภายใต้ ControlelR ก่อนอื่นที่บรรทัดแรกของวิธีการและสร้างวัตถุ handlermethod ตามตัวจัดการและ Mehthod บรรทัดที่สองใช้แผนที่ handlermethods เพื่อรับ handlermethod ที่สอดคล้องกับการแมปปัจจุบัน จากนั้นตรวจสอบว่ามีการกำหนดค่าการร้องขอแบบเดียวกันหรือไม่ การกำหนดค่าต่อไปนี้จะทำให้เกิดการโยนที่นี่
Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping found. Cannot map...
ผิดปกติ
@controller @requestmapping ("/acciguoustest") คลาสสาธารณะที่ไม่ชัดเจน testcontroller {@requestmapping (value = "/test1") @ResponseBody สตริงสาธารณะ test1 () {return "method test1"; } @RequestMapping (value = "/test1") @ResponseBody สตริงสาธารณะ test2 () {return "method test2"; - ตรวจสอบว่าการกำหนดค่าการร้องขอการทำแผนที่เป็นความคลุมเครือในช่วงการเริ่มต้น SpringMVC (การเริ่มต้น) ซึ่งเป็นหนึ่งในสิ่งที่จะตรวจสอบความคลุมเครือ (สถานที่ที่จะตรวจสอบความคลุมเครือในเวลารันไทม์จะถูกกล่าวถึงในภายหลัง) จากนั้นหลังจากยืนยันว่าการกำหนดค่าเป็นเรื่องปกติจะมีการเพิ่มวัตถุ requestmappingInfo และ handlermethod ลงใน handlermethods (linkedHashMap) จากนั้นจะเพิ่มค่าคำอธิบายประกอบที่มีคำอธิบายประกอบและ reuqestmappingInfo ลงใน URLMAP
สรุปวิธีการ registerhandlermethod อย่างง่าย
มีความรับผิดชอบหลักสามประการของวิธีนี้
1. ตรวจสอบว่าการกำหนดค่าคำอธิบายประกอบการร้องขอนั้นมีความคลุมเครือหรือไม่
2. สร้างแผนที่ของ requestmappinginfo เพื่อ handlermethod แผนที่นี้เป็นตัวแปรของสมาชิก Handlermethods ของ abstracthandlermethodmapping LinkedHashMap
3. สร้าง URLMAP ตัวแปรของสมาชิกของ AbstracthandLermethodmapping และ MultivalUemap โครงสร้างข้อมูลนี้สามารถเข้าใจได้ว่าเป็นแผนที่> คีย์ประเภทสตริงเก็บค่าของคำอธิบายประกอบการร้องขอการใช้งานบนวิธีการประมวลผล เป็น URI ที่เฉพาะเจาะจง
ก่อนอื่นมีคอนโทรลเลอร์ต่อไปนี้
@controller @requestmapping ("/urlmap") คลาสสาธารณะ urlmapController {@requestmapping (value = "/test1", method = requestMethod.get) @ResponseBody สตริงสาธารณะ test1 () {return "method test1"; } @RequestMapping (value = "/test1") @ResponseBody สตริงสาธารณะ test2 () {return "method test2"; } @RequestMapping (value = "/test3") @ResponseBody สตริงสาธารณะ test3 () {return "method test3"; - หลังจากการเริ่มต้นเสร็จสิ้นโครงสร้างของ urlmap ที่สอดคล้องกับ abstracthandlermethodmapping มีดังนี้
ข้างต้นเป็นกระบวนการหลักของการเริ่มต้น SpringMVC
ขั้นตอนการค้นหา
เพื่อที่จะเข้าใจกระบวนการค้นหาพร้อมคำถามตัวควบคุมต่อไปนี้มีอยู่
@controller @requestmapping ("/lookuptest") คลาสสาธารณะ lookuptestController {@requestmapping (value = "/test1", method = requestMethod.get) @ResponseBody สตริงสาธารณะ test1 () {return "method test1"; } @RequestMapping (value = "/test1", ส่วนหัว = "referer = https: //www.baidu.com") @ResponseBody สตริงสาธารณะ test2 () {return "method test2"; } @RequestMapping (value = "/test1", params = "id = 1") @ResponseBody สตริงสาธารณะ test3 () {return "method test3"; } @RequestMapping (value = "/*") @ResponseBody สตริงสาธารณะ test4 () {return "method test4"; - มีคำขอดังนี้
คำขอนี้จะป้อนวิธีใด
หลังจากได้รับคำขอเว็บคอนเทนเนอร์ (Tomcat, Jetty) ถูกส่งไปยัง Dispatcherservlet สำหรับการประมวลผล FrameworkServlet เรียกใช้วิธีการร้องขอที่สอดคล้องกัน (เช่น: รับการโทร DEGET) จากนั้นเรียกใช้วิธี ProcessRequest หลังจากป้อนวิธี ProcessRequest หลังจากการประมวลผลหลายชุดแล้วให้ป้อนวิธี Doservice ในบรรทัด: 936 จากนั้นป้อนวิธี Dodispatch ใน LINE856 รับตัวจัดการโปรเซสเซอร์ที่ร้องขอในปัจจุบัน: 896 จากนั้นป้อนวิธีการ lookuphandlermethod ของ abstracthandlermethodmapping รหัสมีดังนี้
HandleRmethod ที่ได้รับการป้องกัน lookuphandLermethod (String lookuppath, คำขอ httpservletRequest) โยนข้อยกเว้น {รายการ <match> matches = new ArrayList <ACTACH> (); // รับ DirectPathMatches ตามรายการ URI <t> DirectPathMatches = this.urlmap.get (lookuppath); if (directAthMatches! = null) {addMatchingMappings (DirectPathMatches, Matches, Request); } // ไม่มีการจับคู่โดยตรง requestmappingInfo, วนซ้ำผ่าน requestmappinginfo ทั้งหมดถ้า (matches.isempty ()) {// ไม่มีทางเลือกนอกจากต้องผ่านการแมปทั้งหมด addmatchingMappings (this.handlermethods.keyset () จับคู่ } // รับ handlermethod ที่สอดคล้องกับการจับคู่ที่ดีที่สุด requestmappinginfo ถ้า (! matches.isempty ()) {comparator <ages> comparator = ใหม่ matchcomparator (getMappingComparator (คำขอ)); collections.sort (การจับคู่, เปรียบเทียบ); if (logger.istraceenabled ()) {logger.trace ("พบ" + matches.size () + "การจับคู่การจับคู่ (s) สำหรับ [" + lookuppath + "]:" + matches); } // ตรวจสอบความคลุมเครือของการกำหนดค่าอีกครั้งตรงกับ bestmatch = matches.get (0); if (matches.size ()> 1) {จับคู่ secondBestMatch = matches.get (1); if (comparator.Compare (BestMatch, SecondBestMatch) == 0) {Method M1 = BestMatch.HandLermethod.getMethod (); วิธี m2 = secondBestMatch.handlermethod.getMethod (); โยนใหม่ unlilstateException ("วิธีการจัดการที่ไม่ชัดเจนที่แมปสำหรับเส้นทาง http '" + request.getRequesturl () + "': {" + m1 + "," + m2 + "}"); }} handleMatch (bestmatch.mapping, lookuppath, คำขอ); ส่งคืน bestmatch.handlermethod; } else {return handlenomatch (handlermethods.keyset (), lookuppath, คำขอ); - ป้อนวิธี LookuphandLermethod โดยที่ lookuppath = "/lookuptest/test1" ตาม lookuppath นั่นคือ URI ที่ร้องขอ ค้นหา URLMAP โดยตรงและรับรายการ requestMappingInfo ที่ตรงกับโดยตรง ที่นี่เราจะจับคู่ 3 RequestMappingInfos ดังนี้
จากนั้นป้อนวิธี addMatchingMappings
โมฆะส่วนตัว addMatchingMappings (คอลเลกชัน <t> การแมป, รายการ <ages> การจับคู่, คำขอ httpservletrequest) {สำหรับ (การแมป t: การแมป) {t แมทช์ = getMatchingMapping (การแมป, คำขอ); if (match! = null) {matches.add (การจับคู่ใหม่ (จับคู่, handlermethods.get (การแมป))); - ความรับผิดชอบของวิธีการนี้คือการสำรวจว่า URI ที่ร้องขอในปัจจุบันและคำขอ MappingInfo ในการแมปสามารถจับคู่ได้หรือไม่และหากเป็นเช่นนั้นให้สร้างวัตถุคำขอ mappingInfo เดียวกัน จากนั้นรับ handlermethod ที่สอดคล้องกับ requestmappinginfo จากนั้นสร้างวัตถุจับคู่และเพิ่มลงในรายการจับคู่ หลังจากดำเนินการวิธีการ addmatchingMappings ให้กลับไปที่ LookuphandLermethod ในเวลานี้การจับคู่ยังคงมี 3 วัตถุคำขอ mappingInfo ที่สามารถจับคู่ได้ กระบวนการถัดไปคือการเรียงลำดับรายการจับคู่แล้วรับองค์ประกอบแรกของรายการเป็นคู่ที่ดีที่สุด ส่งคืน handlermethod ของการแข่งขัน ที่นี่เราป้อนวิธีการเปรียบเทียบของการร้องขอ mappingInfo และดูที่ตรรกะการเรียงลำดับที่เฉพาะเจาะจง รหัสมีดังนี้
public int compereto (requestmappinginfo อื่น ๆ , คำขอ httpservletrequest) {int result = patternscondition.compareto (อื่น ๆ . getPatternScondition (), คำขอ); ถ้า (ผลลัพธ์! = 0) {ผลตอบแทน; } result = paramScondition.Compareto (อื่น ๆ . getParamScondition (), คำขอ); ถ้า (ผลลัพธ์! = 0) {ผลตอบแทน; } result = headersCondition.COMPARETO (อื่น ๆ getHeadersCondition (), คำขอ); ถ้า (ผลลัพธ์! = 0) {ผลตอบแทน; } result = consumerCondition.Compareto (อื่น ๆ . getConsumEscondition (), คำขอ); ถ้า (ผลลัพธ์! = 0) {ผลตอบแทน; } result = producescondition.Compareto (อื่น ๆ . getProducescondition (), คำขอ); ถ้า (ผลลัพธ์! = 0) {ผลตอบแทน; } result = methodScondition.Compareto (อื่น ๆ . getMethodScondition (), คำขอ); ถ้า (ผลลัพธ์! = 0) {ผลตอบแทน; } result = customConditionholder.Compareto (อื่น ๆ . customconditionholder, คำขอ); ถ้า (ผลลัพธ์! = 0) {ผลตอบแทน; } return 0;} อย่างที่คุณเห็นในรหัสลำดับของการจับคู่คือค่า> พารามิเตอร์> ส่วนหัว> บริโภค> ผลิต> วิธีการ> กำหนดเอง เมื่อเห็นสิ่งนี้คำถามก่อนหน้านี้สามารถตอบได้ง่าย ในกรณีที่มีค่าเดียวกันพารามิเตอร์สามารถจับคู่ได้ก่อน ดังนั้นคำขอนั้นจะป้อนวิธีการทดสอบ 3 () กลับไปที่ lookuphandlermethod และค้นหา handlermethod SpringMVC จะตรวจสอบความคลุมเครือของการกำหนดค่าอีกครั้งที่นี่ หลักการของการตรวจสอบที่นี่คือการเปรียบเทียบทั้งสอง requestmappinginfos กับปริญญาที่จับคู่สูงสุด อาจมีคำถามที่นี่ว่าเมื่อเริ่มต้น SpringMVC มีการตรวจสอบการกำหนดค่าที่คลุมเครือทำไมจึงตรวจสอบอีกครั้งที่นี่? หากมีสองวิธีในคอนโทรลเลอร์ในขณะนี้การกำหนดค่าต่อไปนี้สามารถตรวจสอบผ่านความคลุมเครือการเริ่มต้น
@RequestMapping (value = "/test5", method = {requestMethod.get, requestMethod.post})@repectionbodypublic string test5 () {return "method test5";}@requestmapping (value = "/test5", method = {requestmethod.get, requestmethod.delte ตอนนี้ดำเนินการ http: // localhost: 8080/springmvc-demo/lookuptest/test5 คำขอและมันจะถูกโยนลงในวิธีการ lookuphandlermethod
java.lang.IllegalStateException: Ambiguous handler methods mapped for HTTP path 'http://localhost:8080/SpringMVC-Demo/LookupTest/test5' ยกเว้น ข้อยกเว้นถูกโยนลงที่นี่เนื่องจากวิธีการเปรียบเทียบของ RequestMethodsRequestCondition เป็นหมายเลขวิธีการเปรียบเทียบ รหัสมีดังนี้
public int compereto (requestMethodsRequestCondition อื่น ๆ คำขอ httpservletrequest) {return other.methods.size () - this.methods.size ();};ไวลด์การ์ดตรงกับเมื่อไหร่? เมื่อไม่สามารถหาค่าได้โดยตรงผ่าน URLMAP การจับคู่ไวด์การ์ดจะถูกใช้เพื่อป้อนวิธี addMatchingMappings
สรุป
ข้างต้นเป็นเนื้อหาทั้งหมดของบทความนี้ ฉันหวังว่าเนื้อหาของบทความนี้จะมีค่าอ้างอิงบางอย่างสำหรับการศึกษาหรือที่ทำงานของทุกคน หากคุณมีคำถามใด ๆ คุณสามารถฝากข้อความไว้เพื่อสื่อสาร ขอบคุณสำหรับการสนับสนุน Wulin.com