AbstractHandlerMethodMapping实现接口getHandlerInternal,定义查找流程
RequestMappingInfoHandlerMapping根据RequestMappingInfo,细化匹配条件,并在匹配不到情况下,顽强的使用RequestCondition一再尝试匹配
虽然 RequestMappingHandlerMapping是受益方,但在这边什么都没做(就是在初始化时,根据@Controller,@RequestMapping注解生成RequestMappingInfo;并根据这两个注解判断是否目标Handler 实现isHandler)
AbstractHandlerMethodMapping实现接口getHandlerInternal
1. 使用UrlPathHelper查找request对应的path
2. 查找path对应的HandlerMethod
2.1 从urlMap中直接等值匹配查找匹配条件RequestMappingInfo
2.2 如果等值查找到匹配条件,将其添加到match条件中
2.3 如果没有找到匹配条件,使用所有的handlerMethod的RequestMappingInfo进行匹配
2.4 对匹配到的Match进行排序,取出最高优先级的Match,并核对是否是唯一的最高优先级
2.5 对匹配到条件,没有匹配到条件的两种情况,分别进行封装
3. 封装HandlerMethod,确保bean中存的是实例
// AbstractHandlerMethodMapping
实现接口getHandlerInternal
package org.springframework.web.servlet.handler
// AbstractHandlerMethodMapping<T>
/**
* Look up a handler method for the given request.
*/
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
// 就是request对应的url
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
// 查找到处理器,这边的处理器会封装成HandlerMethod
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
// 确保bean中存的是实例
return (handlerMethod != null) ? handlerMethod.createWithResolvedBean() : null;
}
// AbstractHandlerMethodMapping
package org.springframework.web.servlet.handler;
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
/**
* Look up the best-matching handler method for the current request.
* If multiple matches are found, the best match is selected.
* @param lookupPath mapping lookup path within the current servlet mapping
* @param request the current request
* @return the best-matching handler method, or {@code null} if no match
* @see #handleMatch(Object, String, HttpServletRequest)
* @see #handleNoMatch(Set, String, HttpServletRequest)
*/
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<Match>();
// 从urlMap中直接等值匹配查找匹配条件RequestMappingInfo
List<T> directPathMatches = this.urlMap.get(lookupPath);
if (directPathMatches != null) {
//
addMatchingMappings(directPathMatches, matches, request);
} if (matches.isEmpty()) {
// No choice but to go through all mappings
// 没有匹配的情况下,遍历handlerMethods的全部匹配条件进行查找
addMatchingMappings(this.handlerMethods.keySet(), matches, request);
} if (!matches.isEmpty()) {
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
Collections.sort(matches, comparator); Match bestMatch = matches.get(0);
if (matches.size() > 1) {
Match secondBestMatch = matches.get(1);
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
// 不能有相同的最优Match
throw new IllegalStateException(
"Ambiguous handler methods mapped for HTTP path '" + request.getRequestURL() + "': {" +
m1 + ", " + m2 + "}");
}
}
// 就是往request域中缓存url中解析出来的参数,mediaType等,这边RequestMappingHandlerMapping也覆写了一下
handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.handlerMethod;
}
else {
// RequestMappingHandlerMapping
return handleNoMatch(handlerMethods.keySet(), lookupPath, request);
}
}
}
// AbstractHandlerMethodMapping
查找具体符合条件的RequestCondition
package org.springframework.web.servlet.handler;
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean { 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, handlerMethods.get(mapping)));
}
}
}
// AbstractHandlerMethodMapping
/**
* Check if a mapping matches the current request and return a (potentially
* new) mapping with conditions relevant to the current request.
* @param mapping the mapping to get a match for
* @param request the current HTTP servlet request
* @return the match, or {@code null} if the mapping doesn't match
*/
protected abstract T getMatchingMapping(T mapping, HttpServletRequest request);
我们来看看RequestMappingInfoHandlerMapping中的实现,从RequestMappingInfo中查找符合的RequestCondition
// RequestMappingInfoHandlerMapping
/**
* Check if the given RequestMappingInfo matches the current request and
* return a (potentially new) instance with conditions that match the
* current request -- for example with a subset of URL patterns.
* @return an info in case of a match; or {@code null} otherwise.
*/
@Override
protected RequestMappingInfo getMatchingMapping(RequestMappingInfo info, HttpServletRequest request) {
return info.getMatchingCondition(request);
}
// AbstractHandlerMethodMapping
/**
* Invoked when a matching mapping is found.
* @param mapping the matching mapping
* @param lookupPath mapping lookup path within the current servlet mapping
* @param request the current request
*/
protected void handleMatch(T mapping, String lookupPath, HttpServletRequest request) {
request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, lookupPath);
}
RequestMappingInfoHandlerMapping中又对其进行了覆写,具体是干啥用的,等看了HandlerAdaptor再说吧
/**
* Expose URI template variables, matrix variables, and producible media types in the request.
* @see HandlerMapping#URI_TEMPLATE_VARIABLES_ATTRIBUTE
* @see HandlerMapping#MATRIX_VARIABLES_ATTRIBUTE
* @see HandlerMapping#PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE
*/
@Override
protected void handleMatch(RequestMappingInfo info, String lookupPath, HttpServletRequest request) {
super.handleMatch(info, lookupPath, request); Set<String> patterns = info.getPatternsCondition().getPatterns();
String bestPattern = patterns.isEmpty() ? lookupPath : patterns.iterator().next();
request.setAttribute(BEST_MATCHING_PATTERN_ATTRIBUTE, bestPattern); Map<String, String> uriVariables = getPathMatcher().extractUriTemplateVariables(bestPattern, lookupPath);
Map<String, String> decodedUriVariables = getUrlPathHelper().decodePathVariables(request, uriVariables);
request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, decodedUriVariables); if (isMatrixVariableContentAvailable()) {
request.setAttribute(HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE, extractMatrixVariables(request, uriVariables));
} if (!info.getProducesCondition().getProducibleMediaTypes().isEmpty()) {
Set<MediaType> mediaTypes = info.getProducesCondition().getProducibleMediaTypes();
request.setAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, mediaTypes);
}
}
/**
* Invoked when no matching mapping is not found.
* @param mappings all registered mappings
* @param lookupPath mapping lookup path within the current servlet mapping
* @param request the current request
* @throws ServletException in case of errors
*/
protected HandlerMethod handleNoMatch(Set<T> mappings, String lookupPath, HttpServletRequest request)
throws Exception { return null;
}
RequestMappingInfoHandlerMapping,覆写,不死心,再匹配一次
// RequestMappingInfoHandlerMapping
/**
* Iterate all RequestMappingInfos once again, look if any match by URL at
* least and raise exceptions accordingly.
* @throws HttpRequestMethodNotSupportedException if there are matches by URL
* but not by HTTP method
* @throws HttpMediaTypeNotAcceptableException if there are matches by URL
* but not by consumable/producible media types
*/
@Override
protected HandlerMethod handleNoMatch(Set<RequestMappingInfo> requestMappingInfos,
String lookupPath, HttpServletRequest request) throws ServletException { Set<String> allowedMethods = new LinkedHashSet<String>(4); Set<RequestMappingInfo> patternMatches = new HashSet<RequestMappingInfo>();
Set<RequestMappingInfo> patternAndMethodMatches = new HashSet<RequestMappingInfo>(); for (RequestMappingInfo info : requestMappingInfos) {
if (info.getPatternsCondition().getMatchingCondition(request) != null) {
patternMatches.add(info);
if (info.getMethodsCondition().getMatchingCondition(request) != null) {
patternAndMethodMatches.add(info);
}
else {
for (RequestMethod method : info.getMethodsCondition().getMethods()) {
allowedMethods.add(method.name());
}
}
}
} if (patternMatches.isEmpty()) {
return null;
}
else if (patternAndMethodMatches.isEmpty() && !allowedMethods.isEmpty()) {
throw new HttpRequestMethodNotSupportedException(request.getMethod(), allowedMethods);
} Set<MediaType> consumableMediaTypes;
Set<MediaType> producibleMediaTypes;
Set<String> paramConditions; if (patternAndMethodMatches.isEmpty()) {
consumableMediaTypes = getConsumableMediaTypes(request, patternMatches);
producibleMediaTypes = getProducibleMediaTypes(request, patternMatches);
paramConditions = getRequestParams(request, patternMatches);
}
else {
consumableMediaTypes = getConsumableMediaTypes(request, patternAndMethodMatches);
producibleMediaTypes = getProducibleMediaTypes(request, patternAndMethodMatches);
paramConditions = getRequestParams(request, patternAndMethodMatches);
} if (!consumableMediaTypes.isEmpty()) {
MediaType contentType = null;
if (StringUtils.hasLength(request.getContentType())) {
try {
contentType = MediaType.parseMediaType(request.getContentType());
}
catch (IllegalArgumentException ex) {
throw new HttpMediaTypeNotSupportedException(ex.getMessage());
}
}
throw new HttpMediaTypeNotSupportedException(contentType, new ArrayList<MediaType>(consumableMediaTypes));
}
else if (!producibleMediaTypes.isEmpty()) {
throw new HttpMediaTypeNotAcceptableException(new ArrayList<MediaType>(producibleMediaTypes));
}
else if (!CollectionUtils.isEmpty(paramConditions)) {
String[] params = paramConditions.toArray(new String[paramConditions.size()]);
throw new UnsatisfiedServletRequestParameterException(params, request.getParameterMap());
}
else {
return null;
}
}