【原創】遨遊springmvc之HandlerMapping

1.前言

以前咱們springmvc原理篇裏已經介紹過,從springmvc核心處理器DispatcherServlet派遣請求的時候,首先會將請求包裝,這就是咱們這邊介紹的HandlerMappingjava

在springmvc源碼介紹中咱們知道,HandlerMapping是一個處理對象映射和映射對應的處理程序的一箇中間體,它也能夠由開發者本身去實現,可是咱們通常沒有這個必要,由於springmvc框架已經給咱們提供了足夠的映射處理器給咱們,一般在初始化咱們的web項目的時候,springmvc會初始化2個處理器:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping、org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,其中RequestMappingHandlerMapping即可以處理咱們在Controller中帶@RequestMapping的方法,HandlerMapping能夠直接實現攔截器,可是不會這麼作,一般HandlerMapping都會用一個叫HandlerExecutionChain的東西給包裝起來,而HandlerExecutionChain則引入了HandlerInterceptor(攔截器),在包裝類中,springmvc已經幫咱們處理好了映射對應的handler(處理方法)和攔截器web

 

2.源代碼解刨

2.1依賴關係

2.2 源碼解刨

DispatcherServlet中首先給當前請求指定對應的HandlerExecutionChainspring

源碼2.2.1mvc

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpServletRequest processedRequest = request;
		HandlerExecutionChain mappedHandler = null;
		boolean multipartRequestParsed = false;

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

		try {
			ModelAndView mv = null;
			Exception dispatchException = null;

			try {
				processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);

				// Determine handler for the current request.(給當前請求指派對應的處理程序)
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null || mappedHandler.getHandler() == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				//省略代碼
		}
	}

DispatcherServlet提供循環HandlerMapping的方法,將請求包裝app

源碼2.2.2cors

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;
	}

在AbstractHandlerMapping(抽象HandlerMapping)中處理了HandlerMapping對應的處理程序,加入攔截器框架

源碼2.2.3async

@Override
	public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        //處理映射對應的處理程序(所說的控制器)
		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;
	}

那麼問題來了?HandlerMapping如何處理帶@RequestMapping的方法?ide

springmvc很機制,直接將兩個類的字母鏈接起來造成一個叫RequestMappingHandlerMapping,RequestMappingHandlerMapping依賴RequestMappingInfoHandlerMapping、RequestMappingInfoHandlerMapping依賴AbstractHandlerMethodMapping(看依賴圖),在AbstractHandlerMethodMapping中的一個內部類MappingRegistry中,經過註冊方法,直接將ui

@RequestMappng中包含的信息RequestMappingInfo和HandlerMethod經過LinkedHashMap(key-value)方式鏈接,也就是說有了RequestMapping(RequestMappingInfo)就能獲得HandlerMethod(看源碼2.2.7)HandlerMethod包裝了執行類、執行方法、參數、橋接處理器 

源碼2.2.4

@Override
	protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
        //獲取url
		String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
		if (logger.isDebugEnabled()) {
			logger.debug("Looking up handler method for path " + lookupPath);
		}
		this.mappingRegistry.acquireReadLock();
		try {
            //根據url獲取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 + "]");
				}
			}
			return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
		}
		finally {
			this.mappingRegistry.releaseReadLock();
		}
	}

源碼2.2.5

//查找當前請求的最優處理方法。若是找到多個匹配,選擇最佳匹配。	
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
		List<Match> matches = new ArrayList<Match>();
        //mappingRegistry註冊器
		List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
		if (directPathMatches != null) {
			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()) {
			Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
			Collections.sort(matches, comparator);
			if (logger.isTraceEnabled()) {
				logger.trace("Found " + matches.size() + " matching mapping(s) for [" +
						lookupPath + "] : " + matches);
			}
			Match bestMatch = matches.get(0);
			if (matches.size() > 1) {
				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 + "}");
				}
			}
			handleMatch(bestMatch.mapping, lookupPath, request);
			return bestMatch.handlerMethod;
		}
		else {
			return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
		}
	}

源碼2.2.6

註冊器註冊了映射和HandlerMethod的關係,而且使得後續適配器中獲得適配

源碼2.2.7

public void register(T mapping, Object handler, Method method) {
			this.readWriteLock.writeLock().lock();
			try {
				HandlerMethod handlerMethod = createHandlerMethod(handler, method);
				assertUniqueMethodMapping(handlerMethod, mapping);

				if (logger.isInfoEnabled()) {
					logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod);
				}
                //將requestMappingInfo和handlerMethod綁定
				this.mappingLookup.put(mapping, handlerMethod);

				List<String> directUrls = getDirectUrls(mapping);
				for (String url : directUrls) {
					this.urlLookup.add(url, mapping);
				}

				String name = null;
				if (getNamingStrategy() != null) {
					name = getNamingStrategy().getName(handlerMethod, mapping);
					addMappingName(name, handlerMethod);
				}

				CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
				if (corsConfig != null) {
					this.corsLookup.put(handlerMethod, corsConfig);
				}

				this.registry.put(mapping, new MappingRegistration<T>(mapping, handlerMethod, directUrls, name));
			}
			finally {
				this.readWriteLock.writeLock().unlock();
			}
		}

 

3.小結

到此爲止,springmvc已經將請求包裝處理好,也就是對應第一張原理圖中的以下標記是二、3的部分

期待下一章HandlerAdapter

 

 

發現一個機智的導航😳

相關文章
相關標籤/搜索