SpringMVC源碼解析之RequestMappingHandlerMapping:getHandler

SpringMVC源碼解析之Servlet
SpringMVC源碼解析之GenericServlet和HttpServlet
SpringMVC源碼解析之DispatcherServlet:生命週期init()和destroy()
SpringMVC源碼解析之DispatcherServlet:請求處理
SpringMVC源碼解析之RequestMappingHandlerMapping:getHandler

一、簡介

getHandler是HandlerMapping接口中的唯一方法,用於根據請求找到匹配的處理器。因此getHandler也是包括RequestMappingHandlerMapping在內的所有實現類中最核心的一個方法,其方法調用鏈如下:
getHandler

二、AbstractHandlerMapping

1. getHandler

//AbstractHandlerMapping#getHandler
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
	//獲取handler的具體邏輯,留給子類實現
	Object handler = getHandlerInternal(request);
	//null handler,採用默認的handler,即屬性defaultHandler
	if (handler == null) {
		handler = getDefaultHandler();
	}
	//還是null handler,直接返回null
	if (handler == null) {
		return null;
	}
	//handler是beanName, 從容器裏獲取對應的bean
	// Bean name or resolved handler?
	if (handler instanceof String) {
		String handlerName = (String) handler;
		handler = obtainApplicationContext().getBean(handlerName);
	}

	//根據handler和request獲取處理器鏈HandlerExecutionChain
	HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
	//CORS請求的處理
	if (CorsUtils.isCorsRequest(request)) {
		//全局配置
		CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
		//handler的單獨配置
		CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
		//handler的所有配置,全局配置+單獨配置
		CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
		//根據cors配置更新HandlerExecutionChain
		executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
	}
	return executionChain;
}

(1)調用getHandlerInternal獲取匹配的處理器
(2)如果獲取失敗,獲取默認的處理器。如果沒有設置默認處理器,直接返回null。
(3)如果獲取到的處理器是字符串,將其作爲beanName從容器中獲取對應的對象作爲處理器。
(4)調用getHandlerExecutionChain封裝成HandlerExecutionChain
(5)針對CORS請求做特殊處理

2. getHandlerExecutionChain

protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
	HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
			(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));

	String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
	for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
		//MappedInterceptor根據url判斷是否匹配
		if (interceptor instanceof MappedInterceptor) {
			MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
			if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
				chain.addInterceptor(mappedInterceptor.getInterceptor());
			}
		}
		//其它類型的Interceptor對所有請求都有效
		else {
			chain.addInterceptor(interceptor);
		}
	}
	return chain;
}

從請求中獲取路徑找到匹配的攔截器Interceptor集合,與Handler一起封裝成HandlerExecutionChain。
如上一篇博客中對onApplicationContext方法的介紹中提到的一樣,SpringMVC中配置的所有攔截器最終封裝成HandlerInterceptor保存在adaptedInterceptors中,其中MappedInterceptor類型的攔截器只針對特點的url有效,其它類型的攔截器對所有請求都有效。

三、AbstractHandlerMethodMapping

1. getHandlerInternal

//AbstractHandlerMethodMapping#getHandlerInternal
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
	//請求的路徑
	String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
	if (logger.isDebugEnabled()) {
		logger.debug("Looking up handler method for path " + lookupPath);
	}
	this.mappingRegistry.acquireReadLock();
	try {
		//找到匹配的處理器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();
	}
}

主要功能是在讀寫鎖加鎖的情況下調用lookupHandlerMethod找到匹配的處理器

2. lookupHandlerMethod

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
	List<Match> matches = new ArrayList<>();
	//根據url獲取對應的匹配條件集
	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()) {
		//getMappingComparator抽獎方法,用於匹配條件T的比較
		Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
		//排序
		matches.sort(comparator);
		if (logger.isTraceEnabled()) {
			logger.trace("Found " + matches.size() + " matching mapping(s) for [" + lookupPath + "] : " + matches);
		}
		Match bestMatch = matches.get(0);
		if (matches.size() > 1) {
			//CORS的預請求,返回new HandlerMethod(new EmptyHandler(), ClassUtils.getMethod(EmptyHandler.class, "handle"));
			if (CorsUtils.isPreFlightRequest(request)) {
				return PREFLIGHT_AMBIGUOUS_MATCH;
			}
			//確保最優的匹配條件T是唯一的,最優的匹配條件必須大於其它所有匹配條件,不能相同
			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 + "}");
			}
		}
		//找到匹配合適的匹配條件後的處理,默認加入請求的屬性Attributes中
		handleMatch(bestMatch.mapping, lookupPath, request);
		return bestMatch.handlerMethod;
	}
	else {
		//沒有找到合適的匹配條件T,handleNoMatch爲默認方案
		return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
	}
}

(1)調用addMatchingMappings查找合適的匹配條件並加入到matches
首先根據url在對應的匹配集directPathMatches內進行查找,如果查找不到則進行全局查找。
(2)找到優先級最高的匹配條件bestMatch。
要求bestMatch的優先級必須大於matches剩餘的所有的匹配條件,如果存在相同的,則拋出錯誤。
(3)調用handleMatch進行匹配成功後的處理
(4)調用handleNoMatch進行匹配失敗後的處理

3. addMatchingMappings

//根據請求request從匹配集mappings篩選出適用的匹配條件
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)));
		}
	}
}

根據getMatchingMapping進行篩選,符合的加入集合matches

4. handleMatch

protected void handleMatch(T mapping, String lookupPath, HttpServletRequest request) {
	request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, lookupPath);
}

默認的handleMatch爲在請求中添加屬性進行標誌

5. handleNoMatch

protected HandlerMethod handleNoMatch(Set<T> mappings, String lookupPath, HttpServletRequest request)
		throws Exception {

	return null;
}

默認的沒有找到處理器時不作處理,返回null。

四、RequestMappingInfoHandlerMapping

1. getMatchingMapping

protected RequestMappingInfo getMatchingMapping(RequestMappingInfo info, HttpServletRequest request) {
	return info.getMatchingCondition(request);
}

檢查匹配條件info與請求是否匹配,不匹配返回null,匹配返回匹配的RequestMappingInfo 對象,具體邏輯委派RequestMappingInfo 的getMatchingCondition處理。

2. getMappingComparator

protected Comparator<RequestMappingInfo> getMappingComparator(final HttpServletRequest request) {
	return (info1, info2) -> info1.compareTo(info2, request);
}

對於RequestMappingInfo大小的比較委派給RequestMappingInfo的compareTo方法處理。

3. handleMatch

protected void handleMatch(RequestMappingInfo info, String lookupPath, HttpServletRequest request) {
	//父類AbstractHandlerMethodMapping中設置lookupPath爲請求屬性
	super.handleMatch(info, lookupPath, request);

	String bestPattern;
	Map<String, String> uriVariables;
	Map<String, String> decodedUriVariables;

	Set<String> patterns = info.getPatternsCondition().getPatterns();
	if (patterns.isEmpty()) {
		bestPattern = lookupPath;
		uriVariables = Collections.emptyMap();
		decodedUriVariables = Collections.emptyMap();
	}
	else {
		bestPattern = patterns.iterator().next();
		uriVariables = getPathMatcher().extractUriTemplateVariables(bestPattern, lookupPath);
		decodedUriVariables = getUrlPathHelper().decodePathVariables(request, uriVariables);
	}

	request.setAttribute(BEST_MATCHING_PATTERN_ATTRIBUTE, bestPattern);
	request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, decodedUriVariables);

	if (isMatrixVariableContentAvailable()) {
		Map<String, MultiValueMap<String, String>> matrixVars = extractMatrixVariables(request, uriVariables);
		request.setAttribute(HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE, matrixVars);
	}

	if (!info.getProducesCondition().getProducibleMediaTypes().isEmpty()) {
		Set<MediaType> mediaTypes = info.getProducesCondition().getProducibleMediaTypes();
		request.setAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, mediaTypes);
	}
}

除了lookupPath外,還將更多的屬性設置到了請求中,方便後面的使用,與HandlerMapping本身並沒有作用。

4. handleNoMatch

protected HandlerMethod handleNoMatch(Set<RequestMappingInfo> infos, String lookupPath,
		HttpServletRequest request) throws ServletException {

	//記錄所有url匹配的匹配條件RequestMappingInfo集
	PartialMatchHelper helper = new PartialMatchHelper(infos, request);

	//沒有url匹配的RequestMappingInfo
	if (helper.isEmpty()) {
		return null;
	}

	//判斷url匹配的RequestMappingInfo集中最終匹配失敗的原因
	//1. Methods不匹配(只有有一個匹配則返回false)
	if (helper.hasMethodsMismatch()) {
		Set<String> methods = helper.getAllowedMethods();
		//OPTIONS請求HttpOptionsHandler做處理器,返回攜帶了所有允許的Methods的請求頭
		if (HttpMethod.OPTIONS.matches(request.getMethod())) {
			HttpOptionsHandler handler = new HttpOptionsHandler(methods);
			return new HandlerMethod(handler, HTTP_OPTIONS_HANDLE_METHOD);
		}
		//非OPTIONS請求拋出Methods不支持的異常
		throw new HttpRequestMethodNotSupportedException(request.getMethod(), methods);
	}

	//consumes不匹配,拋出MediaType不支持的異常
	if (helper.hasConsumesMismatch()) {
		Set<MediaType> mediaTypes = helper.getConsumableMediaTypes();
		MediaType contentType = null;
		if (StringUtils.hasLength(request.getContentType())) {
			try {
				contentType = MediaType.parseMediaType(request.getContentType());
			}
			catch (InvalidMediaTypeException ex) {
				throw new HttpMediaTypeNotSupportedException(ex.getMessage());
			}
		}
		throw new HttpMediaTypeNotSupportedException(contentType, new ArrayList<>(mediaTypes));
	}

	//produces不匹配,拋出MediaType不支持的異常
	if (helper.hasProducesMismatch()) {
		Set<MediaType> mediaTypes = helper.getProducibleMediaTypes();
		throw new HttpMediaTypeNotAcceptableException(new ArrayList<>(mediaTypes));
	}

	//params不匹配,拋出參數不匹配的異常
	if (helper.hasParamsMismatch()) {
		List<String[]> conditions = helper.getParamConditions();
		throw new UnsatisfiedServletRequestParameterException(conditions, request.getParameterMap());
	}

	return null;
}

細化了找不到匹配的匹配條件的原因,url匹配但methods(OPTIONS除外)/produces/consumes/params不匹配拋出對應異常,其它的返回null。
針對RequestMappingInfo集進行迭代,查找url匹配的請求。如果有url匹配的匹配條件,根據其匹配失敗的原因(如methods不匹配,produces/consumes不匹配,params不匹配)拋出對應的異常。

五、RequestMappingInfo

1. getMatchingCondition

public RequestMappingInfo getMatchingCondition(HttpServletRequest request) {
	//methodsCondition與請求的methods進行判斷,返回匹配的方法,下面的getMatchingCondition方法的同理
	RequestMethodsRequestCondition methods = this.methodsCondition.getMatchingCondition(request);
	ParamsRequestCondition params = this.paramsCondition.getMatchingCondition(request);
	HeadersRequestCondition headers = this.headersCondition.getMatchingCondition(request);
	ConsumesRequestCondition consumes = this.consumesCondition.getMatchingCondition(request);
	ProducesRequestCondition produces = this.producesCondition.getMatchingCondition(request);

	if (methods == null || params == null || headers == null || consumes == null || produces == null) {
		return null;
	}

	PatternsRequestCondition patterns = this.patternsCondition.getMatchingCondition(request);
	if (patterns == null) {
		return null;
	}

	RequestConditionHolder custom = this.customConditionHolder.getMatchingCondition(request);
	if (custom == null) {
		return null;
	}

	return new RequestMappingInfo(this.name, patterns,
			methods, params, headers, consumes, produces, custom.getCondition());
}

將RequestMappingInfo的methods等屬性與請求中的屬性進行匹配,只有有一個匹配失敗則認爲不匹配,返回false。
RequestMappingInfo的methods等屬性可能是一個列表,有多個值,但是最終匹配的只有一個,因此匹配成功需要新建一個RequestMappingInfo返回。

2. compareTo

public int compareTo(RequestMappingInfo other, HttpServletRequest request) {
	int result;
	// Automatic vs explicit HTTP HEAD mapping
	if (HttpMethod.HEAD.matches(request.getMethod())) {
		result = this.methodsCondition.compareTo(other.getMethodsCondition(), request);
		if (result != 0) {
			return result;
		}
	}
	result = this.patternsCondition.compareTo(other.getPatternsCondition(), request);
	if (result != 0) {
		return result;
	}
	result = this.paramsCondition.compareTo(other.getParamsCondition(), request);
	if (result != 0) {
		return result;
	}
	result = this.headersCondition.compareTo(other.getHeadersCondition(), request);
	if (result != 0) {
		return result;
	}
	result = this.consumesCondition.compareTo(other.getConsumesCondition(), request);
	if (result != 0) {
		return result;
	}
	result = this.producesCondition.compareTo(other.getProducesCondition(), request);
	if (result != 0) {
		return result;
	}
	// Implicit (no method) vs explicit HTTP method mappings
	result = this.methodsCondition.compareTo(other.getMethodsCondition(), request);
	if (result != 0) {
		return result;
	}
	result = this.customConditionHolder.compareTo(other.customConditionHolder, request);
	if (result != 0) {
		return result;
	}
	return 0;
}

按照METHODS(只針對HEAD請求),patterns,params,headers,consumes,produces,METHODS(HEAD以外的請求),自定義條件的順序進行比較確定優先級

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章