前言
在華陰等地區(qū),都構(gòu)建了全面的區(qū)域性戰(zhàn)略布局,加強(qiáng)發(fā)展的系統(tǒng)性、市場(chǎng)前瞻性、產(chǎn)品創(chuàng)新能力,以專(zhuān)注、極致的服務(wù)理念,為客戶(hù)提供網(wǎng)站設(shè)計(jì)、成都網(wǎng)站制作 網(wǎng)站設(shè)計(jì)制作定制開(kāi)發(fā),公司網(wǎng)站建設(shè),企業(yè)網(wǎng)站建設(shè),品牌網(wǎng)站制作,全網(wǎng)整合營(yíng)銷(xiāo)推廣,成都外貿(mào)網(wǎng)站制作,華陰網(wǎng)站建設(shè)費(fèi)用合理。
對(duì)于RequestMappingHandlerMapping,使用Spring的同學(xué)基本都不會(huì)陌生,該類(lèi)的作用有兩個(gè):
本文主要講解RequestMappingHandlerMapping是如何獲取HandlerMethod和Interceptor,并且將其封裝為HandlerExecutionChain的。
下面話(huà)不多說(shuō)了,來(lái)一起看看詳細(xì)的介紹吧
1.整體封裝結(jié)構(gòu)
RequestMappingHandlerMapping實(shí)現(xiàn)了HandlerMapping接口,該接口的主要方法如下:
public interface HandlerMapping { // 通過(guò)request獲取HandlerExecutionChain對(duì)象 HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception; }
這里我們直接看RequestMappingHandlerMapping是如何實(shí)現(xiàn)該接口的:
@Override @Nullable public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { // 通過(guò)request獲取具體的處理bean,這里handler可能有兩種類(lèi)型:HandlerMethod和String。 // 如果是String類(lèi)型,那么就在BeanFactory中查找該String類(lèi)型的bean,需要注意的是,返回的 // bean如果是需要使用RequestMappingHandlerAdapter處理,那么也必須是HandlerMethod類(lèi)型的 Object handler = getHandlerInternal(request); if (handler == null) { // 如果找不到處理方法,則獲取自定義的默認(rèn)handler handler = getDefaultHandler(); } if (handler == null) { return null; } if (handler instanceof String) { // 如果獲取的handler是String類(lèi)型的,則在當(dāng)前BeanFactory中獲取該名稱(chēng)的bean, // 并將其作為handler返回 String handlerName = (String) handler; handler = obtainApplicationContext().getBean(handlerName); } // 獲取當(dāng)前系統(tǒng)中配置的Interceptor,將其與handler一起封裝為一個(gè)HandlerExecutionChain HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request); // 這里CorsUtils.isCorsRequest()方法判斷的是當(dāng)前請(qǐng)求是否為一個(gè)跨域的請(qǐng)求,如果是一個(gè)跨域的請(qǐng)求, // 則將跨域相關(guān)的配置也一并封裝到HandlerExecutionChain中 if (CorsUtils.isCorsRequest(request)) { CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request); CorsConfiguration handlerConfig = getCorsConfiguration(handler, request); CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig); executionChain = getCorsHandlerExecutionChain(request, executionChain, config); } return executionChain; }
從上面的代碼可以看出,對(duì)于HandlerExecutionChain的獲取,RequestMappingHandlerMapping首先會(huì)獲取當(dāng)前request對(duì)應(yīng)的handler,然后將其與Interceptor一起封裝為一個(gè)HandlerExecutionChain對(duì)象。這里在進(jìn)行封裝的時(shí)候,Spring會(huì)對(duì)當(dāng)前request是否為跨域請(qǐng)求進(jìn)行判斷,如果是跨域請(qǐng)求,則將相關(guān)的跨域配置封裝到HandlerExecutionChain中,關(guān)于跨域請(qǐng)求,讀者可以閱讀跨域資源共享 CORS 詳解。
2. 獲取HandlerMethod
關(guān)于RequestMappingHandlerMapping是如何獲取handler的,其主要在getHandlerInternal()方法中,如下是該方法的源碼:
@Override protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { // 獲取當(dāng)前request的URI String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); if (logger.isDebugEnabled()) { logger.debug("Looking up handler method for path " + lookupPath); } // 獲取注冊(cè)的Mapping的讀鎖 this.mappingRegistry.acquireReadLock(); try { // 通過(guò)path和request查找具體的HandlerMethod HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request); if (logger.isDebugEnabled()) { if (handlerMethod != null) { logger.debug("Returning handler method [" + handlerMethod + "]"); } else { logger.debug("Did not find handler method for [" + lookupPath + "]"); } } // 如果獲取到的bean是一個(gè)String類(lèi)型的,則在BeanFactory中查找該bean, // 并將其封裝為一個(gè)HandlerMethod對(duì)象 return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null); } finally { // 釋放當(dāng)前注冊(cè)的Mapping的讀鎖 this.mappingRegistry.releaseReadLock(); } }
上述方法中,其首先會(huì)獲取當(dāng)前request的uri,然后通過(guò)uri查找HandlerMethod,并且在最后,會(huì)判斷獲取到的HandlerMethod中的bean是否為String類(lèi)型的,如果是,則在當(dāng)前BeanFactory中查找該名稱(chēng)的bean,并且將其封裝為HandlerMethod對(duì)象。這里我們直接閱讀lookupHandlerMethod()方法:
@Nullable protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception { Listmatches = new ArrayList<>(); // 通過(guò)uri直接在注冊(cè)的RequestMapping中獲取對(duì)應(yīng)的RequestMappingInfo列表,需要注意的是, // 這里進(jìn)行查找的方式只是通過(guò)url進(jìn)行查找,但是具體哪些RequestMappingInfo是匹配的,還需要進(jìn)一步過(guò)濾 List directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath); if (directPathMatches != null) { // 對(duì)獲取到的RequestMappingInfo進(jìn)行進(jìn)一步過(guò)濾,并且將過(guò)濾結(jié)果封裝為一個(gè)Match列表 addMatchingMappings(directPathMatches, matches, request); } if (matches.isEmpty()) { // 如果無(wú)法通過(guò)uri進(jìn)行直接匹配,則對(duì)所有的注冊(cè)的RequestMapping進(jìn)行匹配,這里無(wú)法通過(guò)uri // 匹配的情況主要有三種: // ①在RequestMapping中定義的是PathVariable,如/user/detail/{id}; // ②在RequestMapping中定義了問(wèn)號(hào)表達(dá)式,如/user/?etail; // ③在RequestMapping中定義了*或**匹配,如/user/detail/** addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request); } if (!matches.isEmpty()) { // 對(duì)匹配的結(jié)果進(jìn)行排序,獲取相似度最高的一個(gè)作為結(jié)果返回,這里對(duì)相似度的判斷時(shí), // 會(huì)判斷前兩個(gè)是否相似度是一樣的,如果是一樣的,則直接拋出異常,如果不相同, // 則直接返回最高的一個(gè) Comparator comparator = new MatchComparator(getMappingComparator(request)); matches.sort(comparator); if (logger.isTraceEnabled()) { logger.trace("Found " + matches.size() + " matching mapping(s) for [" + lookupPath + "] : " + matches); } // 獲取匹配程度最高的一個(gè)匹配結(jié)果 Match bestMatch = matches.get(0); if (matches.size() > 1) { // 如果匹配結(jié)果不止一個(gè),首先會(huì)判斷是否是跨域請(qǐng)求,如果是, // 則返回PREFLIGHT_AMBIGUOUS_MATCH,如果不是,則會(huì)判斷前兩個(gè)匹配程度是否相同, // 如果相同則拋出異常 if (CorsUtils.isPreFlightRequest(request)) { return PREFLIGHT_AMBIGUOUS_MATCH; } Match secondBestMatch = matches.get(1); if (comparator.compare(bestMatch, secondBestMatch) == 0) { Method m1 = bestMatch.handlerMethod.getMethod(); Method m2 = secondBestMatch.handlerMethod.getMethod(); throw new IllegalStateException("Ambiguous handler methods mapped for" + " HTTP path '" + request.getRequestURL() + "': {" + m1 + ", " + m2 + "}"); } } // 這里主要是對(duì)匹配結(jié)果的一個(gè)處理,主要包含對(duì)傳入?yún)?shù)和返回的MediaType的處理 handleMatch(bestMatch.mapping, lookupPath, request); return bestMatch.handlerMethod; } else { // 如果匹配結(jié)果是空的,則對(duì)所有注冊(cè)的Mapping進(jìn)行遍歷,判斷當(dāng)前request具體是哪種情況導(dǎo)致 // 的無(wú)法匹配:①RequestMethod無(wú)法匹配;②Consumes無(wú)法匹配;③Produces無(wú)法匹配; // ④Params無(wú)法匹配 return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request); } }
這里對(duì)于結(jié)果的匹配,首先會(huì)通過(guò)uri進(jìn)行直接匹配,如果能匹配到,則在匹配結(jié)果中嘗試進(jìn)行RequestMethod,Consumes和Produces等配置的匹配;如果通過(guò)uri不能匹配到,則直接對(duì)所有定義的RequestMapping進(jìn)行匹配,這里主要是進(jìn)行正則匹配,如果能匹配到。如果能夠匹配到,則對(duì)匹配結(jié)果按照相似度進(jìn)行排序,并且對(duì)前兩個(gè)結(jié)果相似度進(jìn)行比較,如果相似度一樣,則拋出異常,如果不一樣,則返回相似度最高的一個(gè)匹配結(jié)果。如果無(wú)法獲取到匹配結(jié)果,則對(duì)所有的匹配結(jié)果進(jìn)行遍歷,判斷當(dāng)前request具體是哪一部分參數(shù)無(wú)法匹配到結(jié)果。對(duì)于匹配結(jié)果的獲取,主要在addMatchingMappings()方法中,這里我們繼續(xù)閱讀該方法的源碼:
private void addMatchingMappings(Collectionmappings, List matches, HttpServletRequest request) { for (T mapping : mappings) { T match = getMatchingMapping(mapping, request); if (match != null) { matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping))); } } }
對(duì)于RequestMapping的匹配,這里邏輯比較簡(jiǎn)單,就是對(duì)所有的RequestMappingInfo進(jìn)行遍歷,然后將request分別于每個(gè)RequestMappingInfo進(jìn)行匹配,如果匹配上了,其返回值就不為空,最后將所有的匹配結(jié)果返回。如下是getMatchingMapping()方法的源碼(其最終調(diào)用的是RequestMappingInfo.getMatchingCondition()方法):
@Override @Nullable public RequestMappingInfo getMatchingCondition(HttpServletRequest request) { // 判斷request請(qǐng)求的類(lèi)型是否與當(dāng)前RequestMethod匹配 RequestMethodsRequestCondition methods = this.methodsCondition.getMatchingCondition(request); // 判斷request請(qǐng)求的參數(shù)是否與RequestMapping中params參數(shù)配置的一致 ParamsRequestCondition params = this.paramsCondition.getMatchingCondition(request); // 判斷request請(qǐng)求的headers是否與RequestMapping中headers參數(shù)配置的一致 HeadersRequestCondition headers = this.headersCondition.getMatchingCondition(request); // 判斷request的請(qǐng)求體類(lèi)型是否與RequestMapping中配置的consumes參數(shù)配置的一致 ConsumesRequestCondition consumes = this.consumesCondition.getMatchingCondition(request); // 判斷當(dāng)前RequestMapping將要返回的請(qǐng)求體類(lèi)型是否與request中Accept的header指定的一致 ProducesRequestCondition produces = this.producesCondition.getMatchingCondition(request); // 對(duì)于上述幾個(gè)判斷,如果匹配上了,那么其返回值都不會(huì)為空,因而這里會(huì)對(duì)每個(gè)返回值都進(jìn)行判斷, // 如果有任意一個(gè)為空,則說(shuō)明沒(méi)匹配上,那么就返回null if (methods == null || params == null || headers == null || consumes == null || produces == null) { return null; } // 對(duì)于前面的匹配,都是一些靜態(tài)屬性的匹配,其中最重要的uri的匹配,主要是正則匹配, // 就是在下面這個(gè)方法中進(jìn)行的 PatternsRequestCondition patterns = this.patternsCondition.getMatchingCondition(request); // 如果URI沒(méi)匹配上,則返回null if (patterns == null) { return null; } // 這里主要是對(duì)用戶(hù)自定義的匹配條件進(jìn)行匹配 RequestConditionHolder custom = this.customConditionHolder.getMatchingCondition(request); if (custom == null) { return null; } // 如果上述所有條件都匹配上了,那么就將匹配結(jié)果封裝為一個(gè)RequestMappingInfo返回 return new RequestMappingInfo(this.name, patterns, methods, params, headers, consumes, produces, custom.getCondition()); }
可以看到,對(duì)于一個(gè)RequestMapping的匹配,主要包括:RequestMethod,Params,Headers,Consumes,Produces,Uri和自定義條件的匹配,如果這幾個(gè)條件都匹配上了,才能表明當(dāng)前RequestMapping與request匹配上了。
3. Interceptor的封裝
關(guān)于Inteceptor的封裝,由前述第一點(diǎn)可以看出,其主要在getHandlerExecutionChain()方法中,如下是該方法的源碼:
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) { // 將當(dāng)前handler封裝到HandlerExecutionChain對(duì)象中 HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ? (HandlerExecutionChain) handler : new HandlerExecutionChain(handler)); // 獲取當(dāng)前request的URI,用于MappedInterceptor的匹配 String lookupPath = this.urlPathHelper.getLookupPathForRequest(request); // 對(duì)當(dāng)前所有注冊(cè)的Interceptor進(jìn)行遍歷,如果其是MappedInterceptor類(lèi)型,則調(diào)用其matches() // 方法,判斷當(dāng)前Interceptor是否能夠應(yīng)用于該request,如果可以,則添加到HandlerExecutionChain中 for (HandlerInterceptor interceptor : this.adaptedInterceptors) { if (interceptor instanceof MappedInterceptor) { MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor; if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) { chain.addInterceptor(mappedInterceptor.getInterceptor()); } } else { // 如果當(dāng)前Interceptor不是MappedInterceptor類(lèi)型,則直接將其添加到 // HandlerExecutionChain中 chain.addInterceptor(interceptor); } } return chain; }
對(duì)于攔截器,理論上,Spring是會(huì)將所有的攔截器都進(jìn)行一次調(diào)用,對(duì)于是否需要進(jìn)行攔截,都是用戶(hù)自定義實(shí)現(xiàn)的。這里如果對(duì)于URI有特殊的匹配,可以使用MappedInterceptor,然后實(shí)現(xiàn)其matches()方法,用于判斷當(dāng)前MappedInterceptor是否能夠應(yīng)用于當(dāng)前request。
4. 小結(jié)
本文首先講解了Spring是如何通過(guò)request進(jìn)行匹配,從而找到具體處理當(dāng)前請(qǐng)求的RequestMapping的,然后講解了Spring是如何封裝Interceptor,將HandlerMethod和Interceptor封裝為一個(gè)HandlerExecutionChain的。
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)創(chuàng)新互聯(lián)的支持。