SpringMVCソース解読-HandlerMapping-RequestMappingHandlerMappingリクエスト配信

32358 ワード

AbstractHandlerMethodMapping実装インタフェースgetHandlerInternal,定義検索プロセス
RequestMappingInfoHandlerMapping RequestMappingInfoに従ってマッチング条件を細分化し、マッチングができない場合はRequestConditionを使用してマッチングを繰り返します
RequestMappingHandlerMappingは受益者ですが、こちらでは何もしていません(初期化時に@Controller,@RequestMapping注記に基づいてRequestMappingInfoを生成し、この2つの注記に基づいてターゲットHandlerがisHandlerを実現するか否かを判断します)
 
AbstractHandlerMethodMapping実装インタフェースgetHandlerInternal
  1. UrlPathHelperを使用してrequest対応pathを検索
  2. パスに対応するHandlerMethodを検索
2.1 urlMapからの直接等値照合照合照合照合条件RequestMappingInfoの検索
2.2等値が一致条件を見つけたらmatch条件に追加する
2.3一致条件が見つからない場合、すべてのhandlerMethodのRequestMappingInfoを使用して一致させる
2.4マッチしたMatchを並べ替え、最優先度のMatchを取り出し、唯一の最優先度かどうかを確認する
2.5条件にマッチし、条件にマッチしていない2つのケースをそれぞれカプセル化する
  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     }