SpringMVC源码解析之RequestMappingHandlerMapping:getHandler

SpringMVC源码解析之Servlet
SpringMVC源码解析之GenericServlet和HttpServlet
SpringMVC源码解析之DispatcherServlet:生命周期init()和destroy()
SpringMVC源码解析之DispatcherServlet:请求处理
SpringMVC源码解析之RequestMappingHandlerMapping:getHandler

一、简介

getHandler是HandlerMapping接口中的唯一方法,用于根据请求找到匹配的处理器。因此getHandler也是包括RequestMappingHandlerMapping在内的所有实现类中最核心的一个方法,其方法调用链如下:
SpringMVC源码解析之RequestMappingHandlerMapping:getHandler_第1张图片

二、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 matches = new ArrayList<>();
	//根据url获取对应的匹配条件集
	List 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 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 mappings, List 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 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 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 uriVariables;
	Map decodedUriVariables;

	Set 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> matrixVars = extractMatrixVariables(request, uriVariables);
		request.setAttribute(HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE, matrixVars);
	}

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

除了lookupPath外,还将更多的属性设置到了请求中,方便后面的使用,与HandlerMapping本身并没有作用。

4. handleNoMatch

protected HandlerMethod handleNoMatch(Set 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 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 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 mediaTypes = helper.getProducibleMediaTypes();
		throw new HttpMediaTypeNotAcceptableException(new ArrayList<>(mediaTypes));
	}

	//params不匹配,抛出参数不匹配的异常
	if (helper.hasParamsMismatch()) {
		List 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以外的请求),自定义条件的顺序进行比较确定优先级

你可能感兴趣的:(SpringMVC)