麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁(yè) > 開(kāi)發(fā) > Java > 正文

Spring MVC學(xué)習(xí)教程之RequestMappingHandlerMapping匹配

2024-07-14 08:42:53
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

前言

對(duì)于RequestMappingHandlerMapping,使用Spring的同學(xué)基本都不會(huì)陌生,該類的作用有兩個(gè):

  • 通過(guò)request查找對(duì)應(yīng)的HandlerMethod,即當(dāng)前request具體是由Controller中的哪個(gè)方法進(jìn)行處理;
  • 查找當(dāng)前系統(tǒng)中的Interceptor,將其與HandlerMethod封裝為一個(gè)HandlerExecutionChain。

本文主要講解RequestMappingHandlerMapping是如何獲取HandlerMethod和Interceptor,并且將其封裝為HandlerExecutionChain的。

下面話不多說(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@Nullablepublic final HandlerExecutionChain getHandler(HttpServletRequest request)  throws Exception { // 通過(guò)request獲取具體的處理bean,這里handler可能有兩種類型:HandlerMethod和String。 // 如果是String類型,那么就在BeanFactory中查找該String類型的bean,需要注意的是,返回的 // bean如果是需要使用RequestMappingHandlerAdapter處理,那么也必須是HandlerMethod類型的 Object handler = getHandlerInternal(request); if (handler == null) {  // 如果找不到處理方法,則獲取自定義的默認(rèn)handler  handler = getDefaultHandler(); } if (handler == null) {  return null; } if (handler instanceof String) {  // 如果獲取的handler是String類型的,則在當(dāng)前BeanFactory中獲取該名稱的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()方法中,如下是該方法的源碼:

@Overrideprotected 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類型的,則在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類型的,如果是,則在當(dāng)前BeanFactory中查找該名稱的bean,并且將其封裝為HandlerMethod對(duì)象。這里我們直接閱讀lookupHandlerMethod()方法:

@Nullableprotected HandlerMethod lookupHandlerMethod(String lookupPath,   HttpServletRequest request) throws Exception { List<Match> matches = new ArrayList<>(); // 通過(guò)uri直接在注冊(cè)的RequestMapping中獲取對(duì)應(yīng)的RequestMappingInfo列表,需要注意的是, // 這里進(jìn)行查找的方式只是通過(guò)url進(jìn)行查找,但是具體哪些RequestMappingInfo是匹配的,還需要進(jìn)一步過(guò)濾 List<T> 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<Match> 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(Collection<T> mappings, List<Match> 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@Nullablepublic RequestMappingInfo getMatchingCondition(HttpServletRequest request) { // 判斷request請(qǐng)求的類型是否與當(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)求體類型是否與RequestMapping中配置的consumes參數(shù)配置的一致 ConsumesRequestCondition consumes =   this.consumesCondition.getMatchingCondition(request); // 判斷當(dāng)前RequestMapping將要返回的請(qǐng)求體類型是否與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ì)用戶自定義的匹配條件進(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類型,則調(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類型,則直接將其添加到   // HandlerExecutionChain中   chain.addInterceptor(interceptor);  } } return chain;}

對(duì)于攔截器,理論上,Spring是會(huì)將所有的攔截器都進(jìn)行一次調(diào)用,對(duì)于是否需要進(jìn)行攔截,都是用戶自定義實(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ì)VeVb武林網(wǎng)的支持。


注:相關(guān)教程知識(shí)閱讀請(qǐng)移步到JAVA教程頻道。
發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: av电影在线观看免费 | 在线观看免费污视频 | 怦然心动50免费完整版 | 亚洲人成在线播放网站 | 在线小视频国产 | 亚洲视频网 | 亚洲天堂ww| 久久久久国产成人精品亚洲午夜 | 国产精品久久久久久影院8一贰佰 | lutube成人福利在线观看污 | 免费永久看羞羞片网站入口 | 亚洲人成在线播放 | 一级毛片电影院 | 欧美福利视频一区二区三区 | 国毛片 | 亚洲日本欧美 | 国产精品一区二区手机在线观看 | 美女视频黄a视频免费全过程 | 国产99免费 | 奶子吧naiziba.cc免费午夜片在线观看 | 国产亚洲精品久久777777 | 精品一区二区三区欧美 | 国产91九色在线播放 | 黄色毛片视频在线观看 | 法国性hdfreexxxx人妖 | 免费看性xxx高清视频自由 | 九九视频精品在线观看 | 亚洲午夜国产 | 污黄视频在线观看 | 日韩欧美高清片 | av成人免费看 | 日韩毛片免费观看 | 91久久精品一区二区 | 欧美一级淫片免费播放口 | 91精品久久久久久久久久久 | 最新中文在线视频 | 亚洲精品久久久久www | 成人综合免费视频 | 日韩av在线网 | 欧日韩在线 | 91精品久久久久久久久网影视 |