天天看點

spring mvc根據請求查找處理方法原理前言整體流程AbstractHandlerMapping的Handler比對

前言

本文主要是分析一下當一個請求過來的時候,spring mvc如何根據請求資訊查到映射的方法(HandlerMethod)的基本流程。

這裡主要對spring mvc的AbstractHandlerMethodMapping類及其子類的處理流程進行跟蹤,其它的HandlerMapping不作說明。

關于HandlerMethod的注冊及相關類說明,可以檢視spring mvc的RequestMappingHandlerMapping注冊HandlerMethod源碼分析。

整體流程

1. 周遊容器内注冊的所有HandlerMapping執行個體

2. 如果按序周遊時,使用某個HandlerMapping執行個體擷取到Handler不為空,則傳回擷取到結果,後面的HandlerMapping執行個體不再周遊

3. 周遊結束未擷取到比對的Handler,則傳回空

AbstractHandlerMapping的Handler比對

重點分析AbstractHandlerMapping的Handler比對實作,下面進入它的方法源碼,一些代碼我删除了:

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		Object handler = getHandlerInternal(request);
		if (handler == null) {
			handler = getDefaultHandler();
		}
		if (handler == null) {
			return null;
		}
		...
		HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, reque
...
		return executionChain;
	}
           

預設Handler(為空)這個就不看了,代碼最下面是在擷取到比對的Handler的時候封裝了攔截器的類似職責鍊的實作,這裡也不關注。是以,接下來隻看下getHandlerInternal這個方法 ,下面的源碼中,大部分不影響邏輯的代碼删除了:

protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
		String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
		
			HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
	}
           

主要邏輯就上面的2行代碼:

1. 擷取到請求路徑lookupPath(預設相對路徑,不是完整的url)

2. 根據lookupPath查找到合适的HandlerMethod

繼續,進入lookupHandlerMethod方法,對着代碼看我加的注釋會清晰點:

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
		List<Match> matches = new ArrayList<Match>();
		// 根據lookupPath擷取對應的RequestMappingInfo,這個資訊是在注冊的時候已經緩存好了。
		List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
		if (directPathMatches != null) {
			// 根據上面擷取到的RequestMappingInfo周遊來比對HandlerMethod。
			//這個比對涉及到多個條件:如請求方法、路徑、參數等一系列資訊,過程一兩句話也說不清楚,	但是依然可能會比對到多個
			addMatchingMappings(directPathMatches, matches, request);
		}
		if (matches.isEmpty()) {
			// 如果根據比對的RequestMappingInfo比對失敗,那就嘗試周遊所有的RequestMappingInfo比對
			addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
		}

		// 可能存在多個,下面的操作會選出一個最合适的HandlerMethod
		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);
		}
           

如果比對到了,到這裡基本就結束了,也就是根據請求找到了合适的controller。如果沒有比對到就接着周遊剩餘的HandlerMapping繼續比對。

p.s. :本文對比對的主要流程進行了說明,比對的很多細節并沒涉及,因為這個過程中,每一步都能說出很多。

繼續閱讀