SpringMVC中的HandlerMapping詳解
1.簡(jiǎn)介
在SpringMVC請(qǐng)求流程中我們可以看出,HandlerMapping是請(qǐng)求映射處理器,也就是通過(guò)請(qǐng)求的url找到對(duì)應(yīng)的邏輯處理單元(Controller),注意這里只是建立請(qǐng)求與Controller的映射關(guān)系,最終的處理是通過(guò)HandlerAdapt來(lái)進(jìn)行處理的。
2.初始化分析
在初識(shí)DispatcherServlet中提到過(guò)DispatcherServlet的初始化中會(huì)調(diào)用initStrategies()方法,實(shí)際上HandlerMapping的初始化是調(diào)用initStrategies()中的
initHandlerMappings(context);
從方法名稱就可以看出來(lái)是進(jìn)行HandlerMapping的初始化。下面來(lái)看一下具體的源碼:
/** * Initialize the HandlerMappings used by this class. * <p>If no HandlerMapping beans are defined in the BeanFactory for this namespace, * we default to BeanNameUrlHandlerMapping. */ private void initHandlerMappings(ApplicationContext context) { this.handlerMappings = null; if (this.detectAllHandlerMappings) { //從ApplicationContext(包括繼承來(lái)的上下文)中獲取所有的HandlerMapping Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); if (!matchingBeans.isEmpty()) { this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values()); // We keep HandlerMappings in sorted order. AnnotationAwareOrderComparator.sort(this.handlerMappings); } } else { try { //從ApplicationContext中獲取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) { //若上下文中沒(méi)有handlerMapping,就使用Spring默認(rèn)的handlerMapping this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class); if (logger.isDebugEnabled()) { logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default"); } } }
從以上初始化源碼中我們可以看出handlerMapping的初始化:
1.通過(guò)detectAllHandlerMappings參數(shù)來(lái)控制是僅從當(dāng)前context中獲取handlerMapping,還是需要從當(dāng)前+繼承來(lái)的context中獲取所有的handlerMapping
2.若context中沒(méi)有獲取到handlerMapping,SpringMVC提供了默認(rèn)的handlerMapping來(lái)處理請(qǐng)求
下面我們來(lái)看一下,默認(rèn)的handlerMapping
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) { String key = strategyInterface.getName(); //關(guān)鍵是這句,從默認(rèn)配置中獲取handlerMapping String value = defaultStrategies.getProperty(key); if (value != null) { String[] classNames = StringUtils.commaDelimitedListToStringArray(value); List<T> strategies = new ArrayList<T>(classNames.length); for (String className : classNames) { try { Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader()); Object strategy = createDefaultStrategy(context, clazz); strategies.add((T) strategy); } catch (ClassNotFoundException ex) { throw new BeanInitializationException( "Could not find DispatcherServlet's default strategy class [" + className + "] for interface [" + key + "]", ex); } catch (LinkageError err) { throw new BeanInitializationException( "Error loading DispatcherServlet's default strategy class [" + className + "] for interface [" + key + "]: problem with class file or dependent class", err); } } return strategies; } else { return new LinkedList<T>(); } }
關(guān)鍵是defaultStrategies,它是個(gè)Properties對(duì)象,用來(lái)存儲(chǔ)讀取到的SpringMVC默認(rèn)的配置,那SpringMVC的默認(rèn)配置是在哪里定義的呢?在DispatcherServlet中有這么一段:
static { try { ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class); defaultStrategies = PropertiesLoaderUtils.loadProperties(resource); } catch (IOException ex) { throw new IllegalStateException("Could not load 'DispatcherServlet.properties': " + ex.getMessage()); } }
這段就是讀取默認(rèn)配置的代碼,它從DispatcherServlet同級(jí)目錄中的DispatcherServlet.properties文件中讀到相關(guān)配置,文件內(nèi)容如下:
. .****省略其他配置***** . org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\ org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
從默認(rèn)配置中可以明顯看出,SpringMVC默認(rèn)提供了兩種HandlerMapping:BeanNameUrlHandlerMapping、DefaultAnnotationHandlerMapping
3.請(qǐng)求處理分析
HandlerMapping是請(qǐng)求處理的映射器,那是它是如何做請(qǐng)求映射處理的呢?下面我們來(lái)看一下DispatcherServlet中對(duì)請(qǐng)求映射的處理。 DispatcherServlet的doDispatch方法中有這么一段:
mappedHandler = getHandler(processedRequest);
這一段就是HandlerMapping對(duì)請(qǐng)求映射的處理,返回一個(gè)Handler執(zhí)行鏈(HandlerExecutionChain)
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { for (HandlerMapping hm : this.handlerMappings) { if (logger.isTraceEnabled()) { logger.trace( "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'"); } HandlerExecutionChain handler = hm.getHandler(request); if (handler != null) { return handler; } } return null; } public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { //根據(jù)reuqest中的請(qǐng)求url獲取handler Object handler = getHandlerInternal(request); if (handler == null) { //若沒(méi)有獲取到handler,就獲取默認(rèn)的handler handler = getDefaultHandler(); } if (handler == null) { return null; } // Bean name or resolved handler? if (handler instanceof String) { String handlerName = (String) handler; handler = getApplicationContext().getBean(handlerName); } HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request); if (CorsUtils.isCorsRequest(request)) { CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request); CorsConfiguration handlerConfig = getCorsConfiguration(handler, request); CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig); executionChain = getCorsHandlerExecutionChain(request, executionChain, config); } return executionChain; }
4.請(qǐng)求是如何找到Controller的
我們知道Web容器在啟動(dòng)的時(shí)候,會(huì)自動(dòng)進(jìn)行Servlet的初始化,由Servlet的初始化來(lái)驅(qū)動(dòng)Spring容器(ApplicationContext)的初始化,在Spring容器初始化的時(shí)候,會(huì)把標(biāo)注為@Controller的類作為Bean加載到Spring容器中,如下圖方法的調(diào)用??梢钥闯龀跏蓟^(guò)程
//org.springframework.web.servlet.handler.AbstractDetectingUrlHandlerMapping protected void detectHandlers() throws BeansException { if (logger.isDebugEnabled()) { logger.debug("Looking for URL mappings in application context: " + getApplicationContext()); } //獲取Spring容器中的BeanName String[] beanNames = (this.detectHandlersInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) : getApplicationContext().getBeanNamesForType(Object.class)); // 遍歷Spring容器中的Bean for (String beanName : beanNames) { String[] urls = determineUrlsForHandler(beanName); if (!ObjectUtils.isEmpty(urls)) { // 將RequestMapping中的url與beanName建立關(guān)系,注冊(cè)到handlerMap中 registerHandler(urls, beanName); } else { if (logger.isDebugEnabled()) { logger.debug("Rejected bean name '" + beanName + "': no URL paths identified"); } } } } //注冊(cè)Handler protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException { Assert.notNull(urlPath, "URL path must not be null"); Assert.notNull(handler, "Handler object must not be null"); Object resolvedHandler = handler; // Eagerly resolve handler if referencing singleton via name. if (!this.lazyInitHandlers && handler instanceof String) { String handlerName = (String) handler; if (getApplicationContext().isSingleton(handlerName)) { resolvedHandler = getApplicationContext().getBean(handlerName); } } Object mappedHandler = this.handlerMap.get(urlPath); if (mappedHandler != null) { if (mappedHandler != resolvedHandler) { throw new IllegalStateException( "Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath + "]: There is already " + getHandlerDescription(mappedHandler) + " mapped."); } } else { if (urlPath.equals("/")) { if (logger.isInfoEnabled()) { logger.info("Root mapping to " + getHandlerDescription(handler)); } setRootHandler(resolvedHandler); } else if (urlPath.equals("/*")) { if (logger.isInfoEnabled()) { logger.info("Default mapping to " + getHandlerDescription(handler)); } setDefaultHandler(resolvedHandler); } else { //建立url與beanName的關(guān)系,方便后面通過(guò)請(qǐng)求url找到對(duì)應(yīng)的Bean this.handlerMap.put(urlPath, resolvedHandler); if (logger.isInfoEnabled()) { logger.info("Mapped URL path [" + urlPath + "] onto " + getHandlerDescription(handler)); } } } }
讓我們?cè)倩氐紿andlerMapping的處理,當(dāng)請(qǐng)求到來(lái)時(shí),會(huì)先到DispatcherServlet,然后會(huì)執(zhí)行HandlerMapping相關(guān)方法,下面我們?cè)俅慰匆幌?/p>
//DispatcherServlet protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { for (HandlerMapping hm : this.handlerMappings) { if (logger.isTraceEnabled()) { logger.trace( "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'"); } //獲取Handler HandlerExecutionChain handler = hm.getHandler(request); if (handler != null) { return handler; } } return null; }
//org.springframework.web.servlet.handler.AbstractHandlerMapping public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { //通過(guò)請(qǐng)求中的url拿到之前初始化時(shí)handlerMap中的bean,并返回handler Object handler = getHandlerInternal(request); if (handler == null) { handler = getDefaultHandler(); } if (handler == null) { return null; } // Bean name or resolved handler? if (handler instanceof String) { String handlerName = (String) handler; handler = getApplicationContext().getBean(handlerName); } HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request); if (CorsUtils.isCorsRequest(request)) { CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request); CorsConfiguration handlerConfig = getCorsConfiguration(handler, request); CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig); executionChain = getCorsHandlerExecutionChain(request, executionChain, config); } return executionChain; }
protected Object getHandlerInternal(HttpServletRequest request) throws Exception { //從request中拿到請(qǐng)求的url String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); //通過(guò)請(qǐng)求的url拿到handler,具體怎么拿的,請(qǐng)往下看lookupHandler()方法的實(shí)現(xiàn) Object handler = lookupHandler(lookupPath, request); if (handler == null) { // We need to care for the default handler directly, since we need to // expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well. Object rawHandler = null; if ("/".equals(lookupPath)) { rawHandler = getRootHandler(); } if (rawHandler == null) { rawHandler = getDefaultHandler(); } if (rawHandler != null) { // Bean name or resolved handler? if (rawHandler instanceof String) { String handlerName = (String) rawHandler; rawHandler = getApplicationContext().getBean(handlerName); } validateHandler(rawHandler, request); handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null); } } if (handler != null && logger.isDebugEnabled()) { logger.debug("Mapping [" + lookupPath + "] to " + handler); } else if (handler == null && logger.isTraceEnabled()) { logger.trace("No handler mapping found for [" + lookupPath + "]"); } return handler; }
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception { // 從handlerMap中拿handler,有沒(méi)有很熟悉的感覺(jué),對(duì),前面初始化時(shí)我們把url與BeanName的對(duì)應(yīng)關(guān)系放到handlerMap中,現(xiàn)在取出Handler,實(shí)際上取出來(lái)的就是beanName,此時(shí)通過(guò)請(qǐng)求的url就拿到了對(duì)應(yīng)的beanName,handlerMapping也光榮的完成了它的任務(wù) Object handler = this.handlerMap.get(urlPath); if (handler != null) { // Bean name or resolved handler? if (handler instanceof String) { String handlerName = (String) handler; //通過(guò)beanName從Spring容器中拿到Bean對(duì)象 handler = getApplicationContext().getBean(handlerName); } validateHandler(handler, request); return buildPathExposingHandler(handler, urlPath, urlPath, null); } // Pattern match? List<String> matchingPatterns = new ArrayList<String>(); for (String registeredPattern : this.handlerMap.keySet()) { if (getPathMatcher().match(registeredPattern, urlPath)) { matchingPatterns.add(registeredPattern); } else if (useTrailingSlashMatch()) { if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) { matchingPatterns.add(registeredPattern +"/"); } } } String bestPatternMatch = null; Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath); if (!matchingPatterns.isEmpty()) { Collections.sort(matchingPatterns, patternComparator); if (logger.isDebugEnabled()) { logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns); } bestPatternMatch = matchingPatterns.get(0); } if (bestPatternMatch != null) { handler = this.handlerMap.get(bestPatternMatch); if (handler == null) { Assert.isTrue(bestPatternMatch.endsWith("/")); handler = this.handlerMap.get(bestPatternMatch.substring(0, bestPatternMatch.length() - 1)); } // Bean name or resolved handler? if (handler instanceof String) { String handlerName = (String) handler; handler = getApplicationContext().getBean(handlerName); } validateHandler(handler, request); String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestPatternMatch, urlPath); // There might be multiple 'best patterns', let's make sure we have the correct URI template variables // for all of them Map<String, String> uriTemplateVariables = new LinkedHashMap<String, String>(); for (String matchingPattern : matchingPatterns) { if (patternComparator.compare(bestPatternMatch, matchingPattern) == 0) { Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath); Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars); uriTemplateVariables.putAll(decodedVars); } } if (logger.isDebugEnabled()) { logger.debug("URI Template variables for request [" + urlPath + "] are " + uriTemplateVariables); } return buildPathExposingHandler(handler, bestPatternMatch, pathWithinMapping, uriTemplateVariables); } // No handler found... return null; }
總結(jié)
1、HandlerMapping的任務(wù)就是建立url與Bean(Controller)的對(duì)應(yīng)關(guān)系,并將對(duì)應(yīng)關(guān)系保存在handlerMap中
2、對(duì)應(yīng)關(guān)系在HandlerMapping初始化的時(shí)候就已經(jīng)建立好了
3、當(dāng)請(qǐng)求到來(lái)時(shí),會(huì)通過(guò)DispatcherServlet找到HandlerMapping,然后通過(guò)請(qǐng)求的url在handlerMap中找到對(duì)應(yīng)的Controller,至此HandlerMapping的任務(wù)完成了,下一步就是HandlerAdapter出場(chǎng)了
到此這篇關(guān)于SpringMVC中的HandlerMapping詳解的文章就介紹到這了,更多相關(guān)HandlerMapping詳解內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
如何解決UnsupportedOperationException異常問(wèn)題
這篇文章主要介紹了如何解決UnsupportedOperationException異常問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-05-05Java任意長(zhǎng)度byte數(shù)組轉(zhuǎn)換為int數(shù)組的方法
這篇文章主要給大家介紹了關(guān)于Java任意長(zhǎng)度byte數(shù)組轉(zhuǎn)換為int數(shù)組的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者使用Java具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07解決spring-boot 打成jar包后 啟動(dòng)時(shí)指定參數(shù)無(wú)效的問(wèn)題
這篇文章主要介紹了解決spring-boot 打成jar包后 啟動(dòng)時(shí)指定參數(shù)無(wú)效的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06Spring中的NamespaceHandler加載過(guò)程源碼詳解
這篇文章主要介紹了Spring中的NamespaceHandler加載過(guò)程源碼詳解,Spring提供的NamespaceHandler的處理機(jī)制,簡(jiǎn)單來(lái)說(shuō)就是命名空間處理器,Spring為了開(kāi)放性提供了NamespaceHandler機(jī)制,這樣我們就可以根據(jù)需求自己來(lái)處理我們?cè)O(shè)置的標(biāo)簽元素,需要的朋友可以參考下2024-02-02解決resultMap映射數(shù)據(jù)錯(cuò)誤的問(wèn)題
這篇文章主要介紹了解決resultMap映射數(shù)據(jù)錯(cuò)誤的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08Java實(shí)現(xiàn)文件上傳與文件下載的示例代碼
在開(kāi)發(fā)中項(xiàng)目難免會(huì)遇到文件上傳和下載的情況,這篇文章主要為大家詳細(xì)介紹了Java中實(shí)現(xiàn)文件上傳與文件下載的示例代碼,希望對(duì)大家有所幫助2023-07-07Java實(shí)現(xiàn)提取Word文檔表格數(shù)據(jù)
使用Java實(shí)現(xiàn)Word文檔表格數(shù)據(jù)的提取,可以確保數(shù)據(jù)處理的一致性和準(zhǔn)確性,同時(shí)大大減少所需的時(shí)間和成本,下面我們來(lái)看看具體實(shí)現(xiàn)方法吧2025-01-01Java實(shí)現(xiàn)統(tǒng)計(jì)在線人數(shù)功能的方法詳解
很多人在筆試或者面試中問(wèn)到:現(xiàn)在要你實(shí)現(xiàn)一個(gè)統(tǒng)計(jì)在線人數(shù)的功能,你該怎么設(shè)計(jì)?不知道的朋友,這篇文章就來(lái)告訴你具體實(shí)現(xiàn)方法2022-08-08