Author: HuiFer
源码阅读仓库: SourceHot-spring
源码路径:
org.springframework.jms.annotation.EnableJms
org.springframework.web.servlet.HandlerMapping
HandlerMapping 处理映射关系, 通过请求转换成对象
HandlerExecutionChain
publicinterfaceHandlerMapping { HandlerExecutionChaingetHandler(HttpServletRequestrequest) throwsException; // 其他静态变量省略 }
@Override@NullablepublicfinalHandlerExecutionChaingetHandler(HttpServletRequestrequest) throwsException { // 转换成handlerObjecthandler = getHandlerInternal(request); if (handler == null) { // 获取默认的 handlerhandler = getDefaultHandler(); } if (handler == null) { returnnull; } // Bean name or resolved handler?if (handlerinstanceofString) { // handler 是beanName 直接从容器中获取StringhandlerName = (String) handler; handler = obtainApplicationContext().getBean(handlerName); } HandlerExecutionChainexecutionChain = getHandlerExecutionChain(handler, request); if (logger.isTraceEnabled()) { logger.trace("Mapped to " + handler); } elseif (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) { logger.debug("Mapped to " + executionChain.getHandler()); } if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) { CorsConfigurationconfig = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(request) : null); CorsConfigurationhandlerConfig = getCorsConfiguration(handler, request); config = (config != null ? config.combine(handlerConfig) : handlerConfig); executionChain = getCorsHandlerExecutionChain(request, executionChain, config); } returnexecutionChain; }
getHandlerInternal
方法是一个抽象方法@NullableprotectedabstractObjectgetHandlerInternal(HttpServletRequestrequest) throwsException;
存在的实现方法
先看
org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#getHandlerInternal
方法是怎么一回事.
@OverrideprotectedHandlerMethodgetHandlerInternal(HttpServletRequestrequest) throwsException { // 获取当前请求路径StringlookupPath = getUrlPathHelper().getLookupPathForRequest(request); // 设置属性request.setAttribute(LOOKUP_PATH, lookupPath); // 上锁this.mappingRegistry.acquireReadLock(); try { // 寻找 handler methodHandlerMethodhandlerMethod = lookupHandlerMethod(lookupPath, request); return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null); } finally { // 释放锁this.mappingRegistry.releaseReadLock(); } }
全路径:
org.springframework.web.util.UrlPathHelper
几个属性
/** * 是否全路径标记 */privatebooleanalwaysUseFullPath = false; /** * 是否需要 decode */privatebooleanurlDecode = true; privatebooleanremoveSemicolonContent = true; /** * 默认的encoding编码格式 */privateStringdefaultEncoding = WebUtils.DEFAULT_CHARACTER_ENCODING;
publicStringgetPathWithinApplication(HttpServletRequestrequest) { // 获取 context pathStringcontextPath = getContextPath(request); // 获取 uriStringrequestUri = getRequestUri(request); Stringpath = getRemainingPath(requestUri, contextPath, true); if (path != null) { // Normal case: URI contains context path.return (StringUtils.hasText(path) ? path : "/"); } else { returnrequestUri; } }
- 从 request 中获取 context-path
- 从属性中直接获取
- 从 request 中调用 getContextPath 获取
- 判断是否是**
/
** - decode request string
- 从 request 中虎丘 request-uri
- 从属性中获取
- 从 request 中调用 getRequestURI 获取
- decode
- 获取剩余路径
- 获取 context-path 地址
publicStringgetContextPath(HttpServletRequestrequest) { // 从 request 获取 context pathStringcontextPath = (String) request.getAttribute(WebUtils.INCLUDE_CONTEXT_PATH_ATTRIBUTE); if (contextPath == null) { contextPath = request.getContextPath(); } if ("/".equals(contextPath)) { // Invalid case, but happens for includes on Jetty: silently adapt it.contextPath = ""; } // decode context pathreturndecodeRequestString(request, contextPath); }
- 判断是否需要编码, 需要编码就做编码操作,不需要就直接返回
publicStringdecodeRequestString(HttpServletRequestrequest, Stringsource) { // 判断是否需要编码if (this.urlDecode) { // 进行编码returndecodeInternal(request, source); } returnsource; }
- 编码方法
@SuppressWarnings("deprecation") privateStringdecodeInternal(HttpServletRequestrequest, Stringsource) { // 确定编码方式Stringenc = determineEncoding(request); try { // 将 source 编译成 enc 的编码方式returnUriUtils.decode(source, enc); } catch (UnsupportedCharsetExceptionex) { if (logger.isWarnEnabled()) { logger.warn("Could not decode request string [" + source + "] with encoding '" + enc + "': falling back to platform default encoding; exception message: " + ex.getMessage()); } // 直接编码,JDK底层编码returnURLDecoder.decode(source); } }
- 确认编码
protectedStringdetermineEncoding(HttpServletRequestrequest) { // 从 request 中获取编码方式Stringenc = request.getCharacterEncoding(); if (enc == null) { // 默认编码enc = getDefaultEncoding(); } returnenc; }
- 获取 uri 地址
publicStringgetRequestUri(HttpServletRequestrequest) { // 从属性中获取Stringuri = (String) request.getAttribute(WebUtils.INCLUDE_REQUEST_URI_ATTRIBUTE); if (uri == null) { // 调用方法获取uri = request.getRequestURI(); } //编码和清理数据returndecodeAndCleanUriString(request, uri); }
- 编码和清理数据
privateStringdecodeAndCleanUriString(HttpServletRequestrequest, Stringuri) { // 去掉分号uri = removeSemicolonContent(uri); // decodinguri = decodeRequestString(request, uri); // 去掉 // 双斜杠uri = getSanitizedPath(uri); returnuri; }
是否删除 servlet path 后的斜杠
默认是 false .
代码流程
- 通过 classLoader 加载
"com.ibm.ws.webcontainer.WebContainer"
- 调用方法
"getWebContainerProperties"
- 从方法结果中取
"getWebContainerProperties"
- 通过 classLoader 加载
privatebooleanshouldRemoveTrailingServletPathSlash(HttpServletRequestrequest) { if (request.getAttribute(WEBSPHERE_URI_ATTRIBUTE) == null) { // Regular servlet container: behaves as expected in any case,// so the trailing slash is the result of a "/" url-pattern mapping.// Don't remove that slash.returnfalse; } BooleanflagToUse = websphereComplianceFlag; if (flagToUse == null) { ClassLoaderclassLoader = UrlPathHelper.class.getClassLoader(); StringclassName = "com.ibm.ws.webcontainer.WebContainer"; StringmethodName = "getWebContainerProperties"; StringpropName = "com.ibm.ws.webcontainer.removetrailingservletpathslash"; booleanflag = false; try { Class<?> cl = classLoader.loadClass(className); Propertiesprop = (Properties) cl.getMethod(methodName).invoke(null); flag = Boolean.parseBoolean(prop.getProperty(propName)); } catch (Throwableex) { if (logger.isDebugEnabled()) { logger.debug("Could not introspect WebSphere web container properties: " + ex); } } flagToUse = flag; websphereComplianceFlag = flag; } // Don't bother if WebSphere is configured to be fully Servlet compliant.// However, if it is not compliant, do remove the improper trailing slash!return !flagToUse; }
- 编码修改方法
publicMultiValueMap<String, String> decodeMatrixVariables( HttpServletRequestrequest, MultiValueMap<String, String> vars) { // 判断是否需要重写编码if (this.urlDecode) { returnvars; } else { // 需要重写编码的情况MultiValueMap<String, String> decodedVars = newLinkedMultiValueMap<>(vars.size()); // 循环, 将 value 调用decodeInternal写到结果map返回vars.forEach((key, values) -> { for (Stringvalue : values) { decodedVars.add(key, decodeInternal(request, value)); } }); returndecodedVars; } }
- 与这个方法对应的还有
decodePathVariables
publicMap<String, String> decodePathVariables(HttpServletRequestrequest, Map<String, String> vars) { // 判断是否需要重写编码if (this.urlDecode) { returnvars; } else { Map<String, String> decodedVars = newLinkedHashMap<>(vars.size()); // 虚幻 decodingvars.forEach((key, value) -> decodedVars.put(key, decodeInternal(request, value))); returndecodedVars; } }
- 回到
org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#getHandlerInternal
StringlookupPath = getUrlPathHelper().getLookupPathForRequest(request);
- 设置属性上锁开锁就不具体展开了.
org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#lookupHandlerMethod
方法第一部分
@NullableprotectedHandlerMethodlookupHandlerMethod(StringlookupPath, HttpServletRequestrequest) throwsException { List<Match> matches = newArrayList<>(); // 从 MultiValueMap 获取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); } //... }
创建一个匹配 list,将匹配结果放入
List<Match> matches = new ArrayList<>();
从 map 中获取数据
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
@NullablepublicList<T> getMappingsByUrl(StringurlPath) { returnthis.urlLookup.get(urlPath); }
urlLookup 是
MultiValueMap
接口.key:url value:mapping
addMatchingMappings 方法
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); }
privatevoidaddMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequestrequest) { for (Tmapping : mappings) { // 抽象方法// 通过抽象方法获取 match 结果Tmatch = getMatchingMapping(mapping, request); // 是否为空if (match != null) { // 从 mappingLookup 获取结果并且插入到matches中matches.add(newMatch(match, this.mappingRegistry.getMappings().get(mapping))); } } }
getMatchingMapping
方法是一个抽象方法protectedabstractTgetMatchingMapping(Tmapping, HttpServletRequestrequest);
org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping#getMatchingMapping
@OverrideprotectedRequestMappingInfogetMatchingMapping(RequestMappingInfoinfo, HttpServletRequestrequest) { returninfo.getMatchingCondition(request); }
第二部分
if (!matches.isEmpty()) { // 比较对象Comparator<Match> comparator = newMatchComparator(getMappingComparator(request)); // 排序matches.sort(comparator); // 获取第一个 match 对象MatchbestMatch = matches.get(0); if (matches.size() > 1) { if (logger.isTraceEnabled()) { logger.trace(matches.size() + " matching mappings: " + matches); } if (CorsUtils.isPreFlightRequest(request)) { returnPREFLIGHT_AMBIGUOUS_MATCH; } MatchsecondBestMatch = matches.get(1); if (comparator.compare(bestMatch, secondBestMatch) == 0) { // 拿出 handlerMethod 进行比较Methodm1 = bestMatch.handlerMethod.getMethod(); Methodm2 = secondBestMatch.handlerMethod.getMethod(); Stringuri = request.getRequestURI(); thrownewIllegalStateException( "Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}"); } } request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod); handleMatch(bestMatch.mapping, lookupPath, request); returnbestMatch.handlerMethod; } else { returnhandleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request); }
- 一行行开始分析
Comparator<Match> comparator = newMatchComparator(getMappingComparator(request));
- 抽象方法
getMappingComparator
protectedabstractComparator<T> getMappingComparator(HttpServletRequestrequest);
实现方法
@OverrideprotectedComparator<RequestMappingInfo> getMappingComparator(finalHttpServletRequestrequest) { return (info1, info2) -> info1.compareTo(info2, request); }
内部定义了 compareTo 方法
执行完成比较方法后创建对象
MatchComparator
对象创建后进行排序,排序后取出第一个元素作为后续操作的基准对象
// 排序matches.sort(comparator); // 获取第一个 match 对象MatchbestMatch = matches.get(0);
if (matches.size() > 1) { if (logger.isTraceEnabled()) { logger.trace(matches.size() + " matching mappings: " + matches); } // 是否跨域请求if (CorsUtils.isPreFlightRequest(request)) { returnPREFLIGHT_AMBIGUOUS_MATCH; } // 取出第二个元素.MatchsecondBestMatch = matches.get(1); // 如果比较结果相同if (comparator.compare(bestMatch, secondBestMatch) == 0) { // 第二个元素和第一个元素的比较过程// 拿出 handlerMethod 进行比较Methodm1 = bestMatch.handlerMethod.getMethod(); Methodm2 = secondBestMatch.handlerMethod.getMethod(); Stringuri = request.getRequestURI(); thrownewIllegalStateException( "Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}"); } }
- 取出第一个元素和第二个元素进行比较. 如果两个 match 相同, 出现异常
最后两个方法
// 设置属性request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod); // 处理匹配的结果handleMatch(bestMatch.mapping, lookupPath, request); returnbestMatch.handlerMethod; } else { // 处理没有匹配的结果returnhandleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request); }
handleMatch
protectedvoidhandleMatch(Tmapping, StringlookupPath, HttpServletRequestrequest) { request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, lookupPath); }
设置一次属性
这个方法子类会继续实现
org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping#handleMatch
@OverrideprotectedvoidhandleMatch(RequestMappingInfoinfo, StringlookupPath, HttpServletRequestrequest) { super.handleMatch(info, lookupPath, request); StringbestPattern; Map<String, String> uriVariables; // 匹配器Set<String> patterns = info.getPatternsCondition().getPatterns(); // 如果空设置基本数据if (patterns.isEmpty()) { bestPattern = lookupPath; uriVariables = Collections.emptyMap(); } else { // 取出一个匹配器bestPattern = patterns.iterator().next(); // 地址匹配器比较 路由地址和匹配器比较uriVariables = getPathMatcher().extractUriTemplateVariables(bestPattern, lookupPath); } request.setAttribute(BEST_MATCHING_PATTERN_ATTRIBUTE, bestPattern); if (isMatrixVariableContentAvailable()) { // 处理多层参数, 带有;分号的处理Map<String, MultiValueMap<String, String>> matrixVars = extractMatrixVariables(request, uriVariables); request.setAttribute(HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE, matrixVars); } // 编码url参数Map<String, String> decodedUriVariables = getUrlPathHelper().decodePathVariables(request, uriVariables); request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, decodedUriVariables); if (!info.getProducesCondition().getProducibleMediaTypes().isEmpty()) { // 获取 media typeSet<MediaType> mediaTypes = info.getProducesCondition().getProducibleMediaTypes(); request.setAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, mediaTypes); } }
handleNoMatch
也是同类型操作org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#handleNoMatch
org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping#handleNoMatch
@OverrideprotectedHandlerMethodhandleNoMatch( Set<RequestMappingInfo> infos, StringlookupPath, HttpServletRequestrequest) throwsServletException { // 创建对象 PartialMatchHelperPartialMatchHelperhelper = newPartialMatchHelper(infos, request); if (helper.isEmpty()) { returnnull; } // 函数是否匹配if (helper.hasMethodsMismatch()) { Set<String> methods = helper.getAllowedMethods(); // 请求方式比较if (HttpMethod.OPTIONS.matches(request.getMethod())) { // handler 转换HttpOptionsHandlerhandler = newHttpOptionsHandler(methods); // 构建 handler methodreturnnewHandlerMethod(handler, HTTP_OPTIONS_HANDLE_METHOD); } thrownewHttpRequestMethodNotSupportedException(request.getMethod(), methods); } if (helper.hasConsumesMismatch()) { Set<MediaType> mediaTypes = helper.getConsumableMediaTypes(); MediaTypecontentType = null; if (StringUtils.hasLength(request.getContentType())) { try { // 字符串转换成对象contentType = MediaType.parseMediaType(request.getContentType()); } catch (InvalidMediaTypeExceptionex) { thrownewHttpMediaTypeNotSupportedException(ex.getMessage()); } } thrownewHttpMediaTypeNotSupportedException(contentType, newArrayList<>(mediaTypes)); } if (helper.hasProducesMismatch()) { Set<MediaType> mediaTypes = helper.getProducibleMediaTypes(); thrownewHttpMediaTypeNotAcceptableException(newArrayList<>(mediaTypes)); } if (helper.hasParamsMismatch()) { List<String[]> conditions = helper.getParamConditions(); thrownewUnsatisfiedServletRequestParameterException(conditions, request.getParameterMap()); } returnnull; }