Spring中HandlerMapping接口源碼詳解
前言
實現(xiàn)HandlerMapping接口的類用來定義request請求和handler object之間的映射關系;request請求可以理解為路由url、RequestMappingInfo,handler object理解為控制器類;
RequestMappingHandlerMapping類就是實現(xiàn)此接口并將容器中所有的控制器的RequestMappingInfo請求和HandlerMethod注冊到內(nèi)存之中,方便真實的請求發(fā)送過來調(diào)用具體的控制器方法;
本文將以RequestMappingHandlerMapping為主線來講解。
接口源碼如下:
public interface HandlerMapping { /** * 返回一個包含handler Object和所有攔截器的HandlerExecutionChain */ @Nullable HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception; }
1.HandlerMapping的初始化
容器加載時DispatcherServlet中的初始化方法initStrategies會被調(diào)用,里面的initHandlerMappings(context)方法會被執(zhí)行,源碼如下:
//容器中HandlerMapping接口實現(xiàn)類集合 @Nullable private List<HandlerMapping> handlerMappings; private void initHandlerMappings(ApplicationContext context) { this.handlerMappings = null; //判定是否檢測所有的HandlerMapping if (this.detectAllHandlerMappings) { //查看容器中所有的實現(xiàn)了HandlerMapping的bean Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); if (!matchingBeans.isEmpty()) { this.handlerMappings = new ArrayList<>(matchingBeans.values()); // 使用實現(xiàn)類的order進行優(yōu)先級排序,升序 AnnotationAwareOrderComparator.sort(this.handlerMappings); } } else { try { //加載缺省的HandlerMapping HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class); this.handlerMappings = Collections.singletonList(hm); } catch (NoSuchBeanDefinitionException ex) { // Ignore, we'll add a default HandlerMapping later. } } // Ensure we have at least one HandlerMapping, by registering // a default HandlerMapping if no other mappings are found. if (this.handlerMappings == null) { this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class); if (logger.isTraceEnabled()) { logger.trace("No HandlerMappings declared for servlet '" + getServletName() + "': using default strategies from DispatcherServlet.properties"); } } }
上面的源碼是從容器中獲取HandlerMapping接口的實現(xiàn)類,其中包括RequestMappingHandlerMapping類實例,那這個類實例實在哪里加載的呢?
2.RequestMappingHandlerMapping處理reques請求
前端發(fā)送過來一個request請求首先進入DispatcherServlet的doService方法,再進入doDispatch方法
本文的重點是通過request請求如何獲取HandlerExecutionChain處理程序執(zhí)行器鏈;HandlerExecutionChain是一個很重要的類,所有的HandlerMethod最終都要包裝成HandlerExecutionChain后才可以使用;
跳出正文主題分析下HandlerExecutionChain類:
Handler execution chain, consisting of handler object and any handler interceptors. Returned by HandlerMapping’s {@link HandlerMapping#getHandler} method.
上面官方說明已經(jīng)說的很清楚了,該類包含handler object(HandlerMethod)和所有的handler interceptors;而且該類的生成只能通過HandlerMapping接口的getHandler方法,具體由AbstractHandlerMapping抽象類實現(xiàn);AbstractHandlerMapping類實現(xiàn)源碼:
/** * 根據(jù)request請求獲取HandlerExecutionChain處理程序執(zhí)行器鏈 * Look up a handler for the given request, falling back to the default * handler if no specific one is found. * @param request current HTTP request * @return the corresponding handler instance, or the default handler * @see #getHandlerInternal */ @Override @Nullable public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { //獲取Handler object即HandlerMethod對象 Object handler = getHandlerInternal(request); if (handler == null) { //如果沒有找到匹配的HandlerMethod對象,那么就獲取默認的handler handler = getDefaultHandler(); } if (handler == null) { return null; } // Bean name or resolved handler? if (handler instanceof String) { String handlerName = (String) handler; handler = obtainApplicationContext().getBean(handlerName); } //根據(jù)上面獲取的handler object獲取處理程序執(zhí)行器鏈類HandlerExecutionChain HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request); if (logger.isTraceEnabled()) { logger.trace("Mapped to " + handler); } else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) { logger.debug("Mapped to " + executionChain.getHandler()); } //有跨域相關配置時執(zhí)行如下代碼 if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) { CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(request) : null); CorsConfiguration handlerConfig = getCorsConfiguration(handler, request); config = (config != null ? config.combine(handlerConfig) : handlerConfig); executionChain = getCorsHandlerExecutionChain(request, executionChain, config); } return executionChain; }
看下getHandlerInternal方法如何獲取HandlerMethod對象:
/** * 通過request請求獲取對應的HandlerMethod類 */ @Override protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { //獲取請求URL String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); request.setAttribute(LOOKUP_PATH, lookupPath); this.mappingRegistry.acquireReadLock(); try { //獲取URL對應的HandlerMethod,重點是我們?nèi)绾潍@取HandlerMethod對象? HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request); return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null); } finally { this.mappingRegistry.releaseReadLock(); } }
看lookupHandlerMethod源碼:
/** * 通過URL獲取HandlerMethod對象 */ @Nullable protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception { List<Match> matches = new ArrayList<>(); //通過URL獲取RequestMappingInfo集合 List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath); if (directPathMatches != null) { //將RequestMappingInfo對象和HandlerMethod對象封裝到Match對象中存到matches集合 addMatchingMappings(directPathMatches, matches, request); } if (matches.isEmpty()) { // No choice but to go through all mappings... addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request); } if (!matches.isEmpty()) { //獲取集合中第一個Match對象 Match bestMatch = matches.get(0); if (matches.size() > 1) { Comparator<Match> comparator = new MatchComparator(getMappingComparator(request)); matches.sort(comparator); bestMatch = matches.get(0); if (logger.isTraceEnabled()) { logger.trace(matches.size() + " matching mappings: " + matches); } 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(); String uri = request.getRequestURI(); throw new IllegalStateException( "Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}"); } } request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod); handleMatch(bestMatch.mapping, lookupPath, request); //返回HandlerMethod對象 return bestMatch.handlerMethod; } else { return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request); } }
上面的代碼有一個this.mappingRegistry.getMappingsByUrl(lookupPath)方法獲取url對應的RequestMappingInfo對象,源碼如下:
//URL和RequestMappingInfo集合 private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>(); /** * 返回與URL匹配的RequestMappingInfo集合 */ @Nullable public List<T> getMappingsByUrl(String urlPath) { return this.urlLookup.get(urlPath); }
上面的源碼顯示RequestMappingInfo對象是從urlLookup集合中獲取的,那urlLookup集合中的數(shù)據(jù)又是從哪里來的呢?請看我的另外一篇文章RequestMappingHandlerMapping源碼分析。
上面代碼有一個addMatchingMappings方法,是通過這個方法獲取到的HandlerMethod對象,看下源碼:
private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) { //循環(huán)遍歷RequestMappingInfo對象 for (T mapping : mappings) { // T match = getMatchingMapping(mapping, request); if (match != null) { //通過this.mappingRegistry.getMappings()獲取HandlerMethod對象 matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping))); } } } //RequestMappingInfo和HandlerMethod集合 private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>(); /** * Return all mappings and handler methods. Not thread-safe. * @see #acquireReadLock() */ public Map<T, HandlerMethod> getMappings() { return this.mappingLookup; }
上面源碼獲取HandlerMethod對象實際上是從mappingLookUp集合之中獲取的,那你可能會問mappingLookup集合中的數(shù)據(jù)又是從哪里獲取到的呢?請看我的另外一篇文章RequestMappingHandlerMapping源碼分析;
回到正文,我們看下mappedHandler = getHandler(processedRequest);方法,源碼如下:
/** * Return the HandlerExecutionChain for this request. * <p>Tries all handler mappings in order. * @param request current HTTP request * @return the HandlerExecutionChain, or {@code null} if no handler could be found */ @Nullable protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { if (this.handlerMappings != null) { //循環(huán)HandlerMapping實現(xiàn)類集合,其中包括RequestMappingHandlerMapping for (HandlerMapping mapping : this.handlerMappings) { //調(diào)用HandlerMapping實現(xiàn)類的getHandler方法,并獲取程序執(zhí)行器鏈類對象 HandlerExecutionChain handler = mapping.getHandler(request); if (handler != null) { //如果匹配直接返回,否則返回null return handler; } } } return null; }
通過上面我們一步步的分析源碼,知道了HandlerExecutionChain處理程序執(zhí)行器鏈是如何獲取的,HandlerExecutionChain里面包含了請求對應的HandlerMethod對象和所有有關的連接器;
到此這篇關于Spring中HandlerMapping接口源碼詳解的文章就介紹到這了,更多相關HandlerMapping接口源碼內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
java?LockSupport實現(xiàn)原理示例解析
這篇文章主要為大家介紹了java?LockSupport實現(xiàn)原理示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-01-01Spring Boot 中整合 MyBatis-Plus詳細步驟(最新推薦)
本文詳細介紹了如何在SpringBoot項目中整合MyBatis-Plus,包括整合步驟、基本CRUD操作、分頁查詢、批量操作、自定義SQL操作等,通過這些步驟,開發(fā)者可以快速實現(xiàn)數(shù)據(jù)庫操作,提高開發(fā)效率,感興趣的朋友一起看看吧2025-01-01mac下修改idea的jvm運行參數(shù)解決idea卡頓的情況
這篇文章主要介紹了mac下修改idea的jvm運行參數(shù)解決idea卡頓的情況,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-12-12JAVA發(fā)送HTTP請求的多種方式詳細總結(jié)
目前做項目中有一個需求是這樣的,需要通過Java發(fā)送url請求,查看該url是否有效,這時我們可以通過獲取狀態(tài)碼來判斷,下面這篇文章主要給大家介紹了關于JAVA發(fā)送HTTP請求的多種方式總結(jié)的相關資料,需要的朋友可以參考下2023-01-01