SpringMVC源码解读 - HandlerMapping - RequestMappingHandlerMapping请求分发

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

 1 package org.springframework.web.servlet.handler
 2     // AbstractHandlerMethodMapping<T>
 3     /**
 4      * Look up a handler method for the given request.
 5      */
 6     @Override
 7     protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
 8         // 就是request对应的url
 9         String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
10         // 查找到处理器,这边的处理器会封装成HandlerMethod
11         HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
12         // 确保bean中存的是实例
13         return (handlerMethod != null) ? handlerMethod.createWithResolvedBean() : null;
14     }

 

// 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

 1 package org.springframework.web.servlet.handler;
 2 public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
 3 
 4     private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
 5         for (T mapping : mappings) {
 6             T match = getMatchingMapping(mapping, request);
 7             if (match != null) {
 8                 matches.add(new Match(match, handlerMethods.get(mapping)));
 9             }
10         }
11     }

 

// AbstractHandlerMethodMapping

1     /** 
2      * Check if a mapping matches the current request and return a (potentially
3      * new) mapping with conditions relevant to the current request.
4      * @param mapping the mapping to get a match for
5      * @param request the current HTTP servlet request
6      * @return the match, or {@code null} if the mapping doesn't match
7      */
8     protected abstract T getMatchingMapping(T mapping, HttpServletRequest request);

 

我们来看看RequestMappingInfoHandlerMapping中的实现,从RequestMappingInfo中查找符合的RequestCondition

// RequestMappingInfoHandlerMapping

 1     /**
 2      * Check if the given RequestMappingInfo matches the current request and
 3      * return a (potentially new) instance with conditions that match the
 4      * current request -- for example with a subset of URL patterns.
 5      * @return an info in case of a match; or {@code null} otherwise.
 6      */
 7     @Override
 8     protected RequestMappingInfo getMatchingMapping(RequestMappingInfo info, HttpServletRequest request) {
 9         return info.getMatchingCondition(request);
10     }

 

// AbstractHandlerMethodMapping

1     /**
2      * Invoked when a matching mapping is found.
3      * @param mapping the matching mapping
4      * @param lookupPath mapping lookup path within the current servlet mapping
5      * @param request the current request
6      */
7     protected void handleMatch(T mapping, String lookupPath, HttpServletRequest request) {
8         request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, lookupPath);
9     }

 

RequestMappingInfoHandlerMapping中又对其进行了覆写,具体是干啥用的,等看了HandlerAdaptor再说吧

 1     /**
 2      * Expose URI template variables, matrix variables, and producible media types in the request.
 3      * @see HandlerMapping#URI_TEMPLATE_VARIABLES_ATTRIBUTE
 4      * @see HandlerMapping#MATRIX_VARIABLES_ATTRIBUTE
 5      * @see HandlerMapping#PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE
 6      */
 7     @Override
 8     protected void handleMatch(RequestMappingInfo info, String lookupPath, HttpServletRequest request) {
 9         super.handleMatch(info, lookupPath, request);
10 
11         Set<String> patterns = info.getPatternsCondition().getPatterns();
12         String bestPattern = patterns.isEmpty() ? lookupPath : patterns.iterator().next();
13         request.setAttribute(BEST_MATCHING_PATTERN_ATTRIBUTE, bestPattern);
14 
15         Map<String, String> uriVariables = getPathMatcher().extractUriTemplateVariables(bestPattern, lookupPath);
16         Map<String, String> decodedUriVariables = getUrlPathHelper().decodePathVariables(request, uriVariables);
17         request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, decodedUriVariables);
18 
19         if (isMatrixVariableContentAvailable()) {
20             request.setAttribute(HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE, extractMatrixVariables(request, uriVariables));
21         }
22 
23         if (!info.getProducesCondition().getProducibleMediaTypes().isEmpty()) {
24             Set<MediaType> mediaTypes = info.getProducesCondition().getProducibleMediaTypes();
25             request.setAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, mediaTypes);
26         }
27     }

 

 

 1     /**
 2      * Invoked when no matching mapping is not found.
 3      * @param mappings all registered mappings
 4      * @param lookupPath mapping lookup path within the current servlet mapping
 5      * @param request the current request
 6      * @throws ServletException in case of errors
 7      */
 8     protected HandlerMethod handleNoMatch(Set<T> mappings, String lookupPath, HttpServletRequest request)
 9             throws Exception {
10 
11         return null;
12     }

RequestMappingInfoHandlerMapping,覆写,不死心,再匹配一次

// RequestMappingInfoHandlerMapping

 1     /**
 2      * Iterate all RequestMappingInfos once again, look if any match by URL at
 3      * least and raise exceptions accordingly.
 4      * @throws HttpRequestMethodNotSupportedException if there are matches by URL
 5      * but not by HTTP method
 6      * @throws HttpMediaTypeNotAcceptableException if there are matches by URL
 7      * but not by consumable/producible media types
 8      */
 9     @Override
10     protected HandlerMethod handleNoMatch(Set<RequestMappingInfo> requestMappingInfos,
11             String lookupPath, HttpServletRequest request) throws ServletException {
12 
13         Set<String> allowedMethods = new LinkedHashSet<String>(4);
14 
15         Set<RequestMappingInfo> patternMatches = new HashSet<RequestMappingInfo>();
16         Set<RequestMappingInfo> patternAndMethodMatches = new HashSet<RequestMappingInfo>();
17 
18         for (RequestMappingInfo info : requestMappingInfos) {
19             if (info.getPatternsCondition().getMatchingCondition(request) != null) {
20                 patternMatches.add(info);
21                 if (info.getMethodsCondition().getMatchingCondition(request) != null) {
22                     patternAndMethodMatches.add(info);
23                 }
24                 else {
25                     for (RequestMethod method : info.getMethodsCondition().getMethods()) {
26                         allowedMethods.add(method.name());
27                     }
28                 }
29             }
30         }
31 
32         if (patternMatches.isEmpty()) {
33             return null;
34         }
35         else if (patternAndMethodMatches.isEmpty() && !allowedMethods.isEmpty()) {
36             throw new HttpRequestMethodNotSupportedException(request.getMethod(), allowedMethods);
37         }
38 
39         Set<MediaType> consumableMediaTypes;
40         Set<MediaType> producibleMediaTypes;
41         Set<String> paramConditions;
42 
43         if (patternAndMethodMatches.isEmpty()) {
44             consumableMediaTypes = getConsumableMediaTypes(request, patternMatches);
45             producibleMediaTypes = getProducibleMediaTypes(request, patternMatches);
46             paramConditions = getRequestParams(request, patternMatches);
47         }
48         else {
49             consumableMediaTypes = getConsumableMediaTypes(request, patternAndMethodMatches);
50             producibleMediaTypes = getProducibleMediaTypes(request, patternAndMethodMatches);
51             paramConditions = getRequestParams(request, patternAndMethodMatches);
52         }
53 
54         if (!consumableMediaTypes.isEmpty()) {
55             MediaType contentType = null;
56             if (StringUtils.hasLength(request.getContentType())) {
57                 try {
58                     contentType = MediaType.parseMediaType(request.getContentType());
59                 }
60                 catch (IllegalArgumentException ex) {
61                     throw new HttpMediaTypeNotSupportedException(ex.getMessage());
62                 }
63             }
64             throw new HttpMediaTypeNotSupportedException(contentType, new ArrayList<MediaType>(consumableMediaTypes));
65         }
66         else if (!producibleMediaTypes.isEmpty()) {
67             throw new HttpMediaTypeNotAcceptableException(new ArrayList<MediaType>(producibleMediaTypes));
68         }
69         else if (!CollectionUtils.isEmpty(paramConditions)) {
70             String[] params = paramConditions.toArray(new String[paramConditions.size()]);
71             throw new UnsatisfiedServletRequestParameterException(params, request.getParameterMap());
72         }
73         else {
74             return null;
75         }
76     }

 

posted @ 2016-02-23 14:45  出门向左  阅读(4374)  评论(0编辑  收藏  举报