• SpringMVC源码解读


    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     }
  • 相关阅读:
    mongodb笔记一
    mysql的备份和恢复
    explain的type列
    Debian下apache2设置并发
    nginx基本调优
    c语言struct
    Centos 安装nginx + php + mysql
    Debian下系统启动时执行脚本
    centos5.5服务器基本篇
    分治法
  • 原文地址:https://www.cnblogs.com/leftthen/p/5209875.html
Copyright © 2020-2023  润新知