RequestMapping 注解的解析、匹配、注册

RequestMapping 注解的解析、匹配、注册

1)创建 RequestMappingHandlerMapping 实例时会触发 afterPropertiesSet 调用。
2)读取容器中所有带有 Controller 或 RequestMapping 注解的类。
3)读取此类中所有满足过滤器 ReflectionUtils.USER_DECLARED_METHODS 的方法,
读取处理方法上的  RequestMapping 注解信息,
将其解析并封装为 RequestMappingInfo 注册到 RequestMappingHandlerMapping#mappingRegistry 中。

RequestMappingHandlerMapping#
    @Nullable
    protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
        // 1)从处理方法中读取 RequestMapping 信息并创建 RequestMappingInfo
        RequestMappingInfo info = createRequestMappingInfo(method);
        if (info != null) {
            // 2)从处理器类中读取 RequestMapping 信息并创建 RequestMappingInfo
            RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
            if (typeInfo != null) {
                // 如果存在,则合并
                info = typeInfo.combine(info);
            }
            // 3)如果处理类上配置了前缀路径
            String prefix = getPathPrefix(handlerType);
            if (prefix != null) {
                // 则完成路径拼接
                info = RequestMappingInfo.paths(prefix).build().combine(info);
            }
        }
        return info;
    }

    @Nullable
    private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
        // 读取注解元素上的 RequestMapping 注解信息
        RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
        if (requestMapping == null) {
            return null;
        }
        
        /**
         * 1)如果是 class,则通过 getCustomTypeCondition 读取 RequestCondition
         * 2)如果是 method,则通过 getCustomMethodCondition 读取 RequestCondition
         *  特性未实现,都返回 null
         */
        RequestCondition<?> condition = (element instanceof Class ?
                getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
        return createRequestMappingInfo(requestMapping, condition);
    }

    protected RequestMappingInfo createRequestMappingInfo(
            RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {

        RequestMappingInfo.Builder builder = RequestMappingInfo
                .paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
                .methods(requestMapping.method())
                .params(requestMapping.params())
                .headers(requestMapping.headers())
                .consumes(requestMapping.consumes())
                .produces(requestMapping.produces())
                .mappingName(requestMapping.name());
        if (customCondition != null) {
            builder.customCondition(customCondition);
        }
        return builder.options(this.config).build();
    }

解析 RequestMapping#path

1)解析 path/value 参数中指定的所有路径
2)如果合并的 path 参数不以 / 开头,则添加前置的 /【最佳实践:编写的每个请求路径都以 / 开头,避免不必要的调用】
3)注入 UrlPathHelper 用于读取 request 的请求路径,注入 AntPathMatcher 用于完成路径匹配【如果未指定】。

PatternsRequestCondition#
    /**
     *  指定的所有请求路径
     */
    private final Set<String> patterns;
    /**
     *  用于读取请求路径的工具类 
     */
    private final UrlPathHelper pathHelper;
    /**
     *  用于执行路径匹配的 AntPathMatcher
     */
    private final PathMatcher pathMatcher;
    /**
     *  是否启用后缀模式,默认为 false
     */
    private final boolean useSuffixPatternMatch;
    /**
     *  是否自动添加尾部 /,默认为 true
     */
    private final boolean useTrailingSlashMatch;

    public PatternsRequestCondition(String[] patterns, @Nullable UrlPathHelper urlPathHelper,
            @Nullable PathMatcher pathMatcher, boolean useSuffixPatternMatch,
            boolean useTrailingSlashMatch, @Nullable List<String> fileExtensions) {

        this(Arrays.asList(patterns), urlPathHelper, pathMatcher, useSuffixPatternMatch,
                useTrailingSlashMatch, fileExtensions);
    }

    private PatternsRequestCondition(Collection<String> patterns, @Nullable UrlPathHelper urlPathHelper,
            @Nullable PathMatcher pathMatcher, boolean useSuffixPatternMatch,
            boolean useTrailingSlashMatch, @Nullable List<String> fileExtensions) {

        this.patterns = Collections.unmodifiableSet(prependLeadingSlash(patterns));
        this.pathHelper = (urlPathHelper != null ? urlPathHelper : new UrlPathHelper());
        this.pathMatcher = (pathMatcher != null ? pathMatcher : new AntPathMatcher());
        this.useSuffixPatternMatch = useSuffixPatternMatch;
        this.useTrailingSlashMatch = useTrailingSlashMatch;

        if (fileExtensions != null) {
            for (String fileExtension : fileExtensions) {
                if (fileExtension.charAt(0) != '.') {
                    fileExtension = "." + fileExtension;
                }
                this.fileExtensions.add(fileExtension);
            }
        }
    }

    private static Set<String> prependLeadingSlash(Collection<String> patterns) {
        Set<String> result = new LinkedHashSet<>(patterns.size());
        for (String pattern : patterns) {
            // 如果请求路径不是以 / 开头,则添加 / 前缀
            if (StringUtils.hasLength(pattern) && !pattern.startsWith("/")) {
                pattern = "/" + pattern;
            }
            result.add(pattern);
        }
        return result;
    }

  • RequestMapping#path 的匹配过程
PatternsRequestCondition#
    /**
     * Checks if any of the patterns match the given request and returns an instance
     * that is guaranteed to contain matching patterns, sorted via
     * {@link PathMatcher#getPatternComparator(String)}.
     * <p>A matching pattern is obtained by making checks in the following order:
     * <ul>
     * <li>Direct match
     * <li>Pattern match with ".*" appended if the pattern doesn't already contain a "."
     * <li>Pattern match
     * <li>Pattern match with "/" appended if the pattern doesn't already end in "/"
     * </ul>
     */
    @Override
    @Nullable
    public PatternsRequestCondition getMatchingCondition(HttpServletRequest request) {
        // 1)如果未指定请求路径,则默认匹配
        if (patterns.isEmpty()) {
            return this;
        }
        // 读取请求路径
        final String lookupPath = pathHelper.getLookupPathForRequest(request);
        // 读取匹配的所有路径
        final List<String> matches = getMatchingPatterns(lookupPath);
        return !matches.isEmpty() ?
                new PatternsRequestCondition(matches, pathHelper, pathMatcher,
                        useSuffixPatternMatch, useTrailingSlashMatch, fileExtensions) : null;
    }

    /**
     * Find the patterns matching the given lookup path.
     */
    public List<String> getMatchingPatterns(String lookupPath) {
        final List<String> matches = new ArrayList<>();
        for (final String pattern : patterns) {
            final String match = getMatchingPattern(pattern, lookupPath);
            if (match != null) {
                matches.add(match);
            }
        }
        if (matches.size() > 1) {
            matches.sort(pathMatcher.getPatternComparator(lookupPath));
        }
        return matches;
    }

    @Nullable
    private String getMatchingPattern(String pattern, String lookupPath) {
        // 1)pattern 和请求路径相等
        if (pattern.equals(lookupPath)) {
            return pattern;
        }
        // 2)是否使用后缀模式,默认为 false
        if (useSuffixPatternMatch) {
            if (!fileExtensions.isEmpty() && lookupPath.indexOf('.') != -1) {
                for (final String extension : fileExtensions) {
                    if (pathMatcher.match(pattern + extension, lookupPath)) {
                        return pattern + extension;
                    }
                }
            }
            else {
                final boolean hasSuffix = pattern.indexOf('.') != -1;
                if (!hasSuffix && pathMatcher.match(pattern + ".*", lookupPath)) {
                    return pattern + ".*";
                }
            }
        }
        // 3)使用 pathMatcher 指定路径匹配,默认是 AntPathMatcher
        if (pathMatcher.match(pattern, lookupPath)) {
            return pattern;
        }
        // 4)默认为 true
        if (useTrailingSlashMatch) {
            // 给 pattern 添加 / 后缀之后再次进行匹配
            if (!pattern.endsWith("/") && pathMatcher.match(pattern + "/", lookupPath)) {
                return pattern +"/";
            }
        }
        return null;
    }

解析 RequestMapping#method

1)写入所有支持的 HttpMethod
RequestMethodsRequestCondition#
    /**
     *  支持的所有请求方法
     */
    private final Set<RequestMethod> methods;

    public RequestMethodsRequestCondition(RequestMethod... requestMethods) {
        this(Arrays.asList(requestMethods));
    }

    private RequestMethodsRequestCondition(Collection<RequestMethod> requestMethods) {
        this.methods = Collections.unmodifiableSet(new LinkedHashSet<>(requestMethods));
    }
  • RequestMapping#method 的匹配过程
RequestMethodsRequestCondition#
    public RequestMethodsRequestCondition getMatchingCondition(HttpServletRequest request) {
        if (CorsUtils.isPreFlightRequest(request)) {
            return matchPreFlight(request);
        }
        
        // 1)如果未指定 RequestMapping#method
        if (getMethods().isEmpty()) {
            // 请求方法为 OPTIONS && 请求的分派类型不是 DispatcherType.ERROR
            if (RequestMethod.OPTIONS.name().equals(request.getMethod()) &&
                    !DispatcherType.ERROR.equals(request.getDispatcherType())) {
                return null; // No implicit match for OPTIONS (we handle it)
            }
            return this;
        }

        return matchRequestMethod(request.getMethod());
    }

    @Nullable
    private RequestMethodsRequestCondition matchRequestMethod(String httpMethodValue) {
        HttpMethod httpMethod = HttpMethod.resolve(httpMethodValue);
        if (httpMethod != null) {
            // 1)支持的请求方法列表中存在此 HttpMethod
            for (RequestMethod method : getMethods()) {
                if (httpMethod.matches(method.name())) {
                    return new RequestMethodsRequestCondition(method);
                }
            }
            /**
             * 2)如果是 HttpMethod.HEAD 方式 
             * && 支持的请求方式列表中存在 RequestMethod.GET,则返回 GET
             */
            if (httpMethod == HttpMethod.HEAD && getMethods().contains(RequestMethod.GET)) {
                return GET_CONDITION;
            }
        }
        return null;
    }

解析 RequestMapping#params

1)将请求参数封装为 ParamExpression 后写入
ParamsRequestCondition
    /**
     *  参数表达式集合
     */
    private final Set<ParamExpression> expressions;

    public ParamsRequestCondition(String... params) {
        this(parseExpressions(params));
    }

    private ParamsRequestCondition(Collection<ParamExpression> conditions) {
        this.expressions = Collections.unmodifiableSet(new LinkedHashSet<>(conditions));
    }
  • RequestMapping#params 匹配过程
ParamsRequestCondition#
    @Override
    @Nullable
    public ParamsRequestCondition getMatchingCondition(HttpServletRequest request) {
        // 只要有一个参数不匹配,则请求不匹配
        for (final ParamExpression expression : expressions) {
            if (!expression.match(request)) {
                return null;
            }
        }
        return this;
    }

AbstractNameValueExpression#
    public final boolean match(HttpServletRequest request) {
        boolean isMatch;
        // 1)如果指定了参数值,则执行值匹配
        if (this.value != null) {
            isMatch = matchValue(request);
        }
        // 2)执行名称匹配
        else {
            isMatch = matchName(request);
        }
        return (this.isNegated ? !isMatch : isMatch);
    }

ParamExpression#
    @Override
    protected boolean matchName(HttpServletRequest request) {
        // 表单参数中存在该参数 || 参数集合中存在该参数
        return (WebUtils.hasSubmitParameter(request, this.name) ||
                request.getParameterMap().containsKey(this.name));
    }
    
    @Override
    protected boolean matchValue(HttpServletRequest request) {
        // 请求参数 name 的参数值和配置值相等
        return ObjectUtils.nullSafeEquals(this.value, request.getParameter(this.name));
    }

解析 RequestMapping#headers

HeadersRequestCondition#
    /**
     *  解析的 header 参数集合
     */
    private final Set<HeaderExpression> expressions;

    public HeadersRequestCondition(String... headers) {
        this(parseExpressions(headers));
    }

    private HeadersRequestCondition(Collection<HeaderExpression> conditions) {
        this.expressions = Collections.unmodifiableSet(new LinkedHashSet<>(conditions));
    }


    private static Collection<HeaderExpression> parseExpressions(String... headers) {
        Set<HeaderExpression> expressions = new LinkedHashSet<>();
        for (String header : headers) {
            HeaderExpression expr = new HeaderExpression(header);
            // 如果是 Accept 和 Content-Type 头,则忽略
            if ("Accept".equalsIgnoreCase(expr.name) || "Content-Type".equalsIgnoreCase(expr.name)) {
                continue;
            }
            expressions.add(expr);
        }
        return expressions;
    }
  • RequestMapping#headers 匹配过程
HeadersRequestCondition#
    public HeadersRequestCondition getMatchingCondition(HttpServletRequest request) {
        if (CorsUtils.isPreFlightRequest(request)) {
            return PRE_FLIGHT_MATCH;
        }
        // 只要有一个请求头不匹配,则该请求不匹配
        for (final HeaderExpression expression : expressions) {
            if (!expression.match(request)) {
                return null;
            }
        }
        return this;
    }

AbstractNameValueExpression#
    public final boolean match(HttpServletRequest request) {
        boolean isMatch;
        // 1)如果配置了请求头的值,则执行值匹配
        if (this.value != null) {
            isMatch = matchValue(request);
        }
        // 2)执行请求头名称匹配
        else {
            isMatch = matchName(request);
        }
        return (this.isNegated ? !isMatch : isMatch);
    }

HeaderExpression#
    // 存在目标请求头
    @Override
    protected boolean matchName(HttpServletRequest request) {
        return request.getHeader(name) != null;
    }
    
    // 请求头的值和配置值相等
    @Override
    protected boolean matchValue(HttpServletRequest request) {
        return ObjectUtils.nullSafeEquals(value, request.getHeader(name));
    }

解析 RequestMapping#consumes

1)如果 headers 中指定了 Content-Type 属性,则将其解析并加入到 ConsumesRequestCondition#expressions 中。
2)解析 consumes 参数中配置的所有 MediaType,并将其加入到 ConsumesRequestCondition#expressions 中。

ConsumesRequestCondition#
    public ConsumesRequestCondition(String[] consumes, @Nullable String[] headers) {
        this(parseExpressions(consumes, headers));
    }

    private ConsumesRequestCondition(Collection<ConsumeMediaTypeExpression> expressions) {
        this.expressions = new ArrayList<>(expressions);
        Collections.sort(this.expressions);
    }

    private static Set<ConsumeMediaTypeExpression> parseExpressions(String[] consumes, @Nullable String[] headers) {
        final Set<ConsumeMediaTypeExpression> result = new LinkedHashSet<>();
        // 1)如果 headers 参数不为 null && headers 中存在 Content-Type 配置,则将其加入到 result 中。
        if (headers != null) {
            for (final String header : headers) {
                final HeaderExpression expr = new HeaderExpression(header);
                if ("Content-Type".equalsIgnoreCase(expr.name) && expr.value != null) {
                    for (final MediaType mediaType : MediaType.parseMediaTypes(expr.value)) {
                        result.add(new ConsumeMediaTypeExpression(mediaType, expr.isNegated));
                    }
                }
            }
        }
        // 2)解析 consumes 参数中配置的所有 MediaType,将其加入到 result 中。
        for (final String consume : consumes) {
            result.add(new ConsumeMediaTypeExpression(consume));
        }
        return result;
    }


AbstractMediaTypeExpression#
    /**
     *  解析完成的 MediaType 类型
     */
    private final MediaType mediaType;
    /**
     *  是否是反向匹配
     */
    private final boolean isNegated;
    
    AbstractMediaTypeExpression(String expression) {
        /**
         *  如果表达式以 ! 开头,则表示反向匹配
         */
        if (expression.startsWith("!")) {
            this.isNegated = true;
            expression = expression.substring(1);
        }
        else {
            this.isNegated = false;
        }
        this.mediaType = MediaType.parseMediaType(expression);
    }

MediaType#
    public static MediaType parseMediaType(String mediaType) {
        MimeType type;
        try {
            type = MimeTypeUtils.parseMimeType(mediaType);
        }
        catch (InvalidMimeTypeException ex) {
            throw new InvalidMediaTypeException(ex);
        }
        try {
            return new MediaType(type.getType(), type.getSubtype(), type.getParameters());
        }
        catch (IllegalArgumentException ex) {
            throw new InvalidMediaTypeException(mediaType, ex.getMessage());
        }
    }


  • RequestMapping#consumes 匹配过程
ConsumesRequestCondition#
    @Override
    @Nullable
    public ConsumesRequestCondition getMatchingCondition(HttpServletRequest request) {
        if (CorsUtils.isPreFlightRequest(request)) {
            return PRE_FLIGHT_MATCH;
        }

        // 1)如果未指定 consumes 参数则默认匹配
        if (isEmpty()) {
            return this;
        }

        // 2)RequestMapping 指定了 consumes 参数,则执行匹配过程
        MediaType contentType;
        try {
            // 读取请求的 Content-Type 属性并将其转换为 MediaType
            contentType = StringUtils.hasLength(request.getContentType()) ?
                    MediaType.parseMediaType(request.getContentType()) :
                        MediaType.APPLICATION_OCTET_STREAM;
        }
        catch (final InvalidMediaTypeException ex) {
            // 3)如果请求的 Content-Type 非法,则不匹配
            return null;
        }

        // 3)读取所有指定的 consumes 参数表达式,进行逐个匹配
        final Set<ConsumeMediaTypeExpression> result = new LinkedHashSet<>(expressions);
        return result.stream()
                .anyMatch(expression->expression.match(contentType)) ? new ConsumesRequestCondition(result) : null;
    }

单个 MediaType 的匹配过程
ConsumeMediaTypeExpression#
        public final boolean match(MediaType contentType) {
            // 当前 MediaType 是否匹配目标 contentType
            final boolean match = getMediaType().includes(contentType);
            // 是否是反向匹配 && 读取匹配结果
            return !isNegated() ? match : !match;
        }

解析 RequestMapping#produces

ProducesRequestCondition#
    /**
     *  支持的结果类型 MediaType
     */
    private final List<ProduceMediaTypeExpression> expressions;

    public ProducesRequestCondition(String[] produces, @Nullable String[] headers,
            @Nullable ContentNegotiationManager manager) {

        expressions = new ArrayList<>(parseExpressions(produces, headers));
        Collections.sort(expressions);
        contentNegotiationManager = manager != null ? manager : new ContentNegotiationManager();
    }

    private Set<ProduceMediaTypeExpression> parseExpressions(String[] produces, @Nullable String[] headers) {
        final Set<ProduceMediaTypeExpression> result = new LinkedHashSet<>();
        // 1)如果存在 headers 配置 && 将 Accept 头配置加入到 result 中
        if (headers != null) {
            for (final String header : headers) {
                final HeaderExpression expr = new HeaderExpression(header);
                if ("Accept".equalsIgnoreCase(expr.name) && expr.value != null) {
                    for (final MediaType mediaType : MediaType.parseMediaTypes(expr.value)) {
                        result.add(new ProduceMediaTypeExpression(mediaType, expr.isNegated));
                    }
                }
            }
        }
        // 2)将所有配置的 MediaType 加入到 result 中
        for (final String produce : produces) {
            result.add(new ProduceMediaTypeExpression(produce));
        }
        return result;
    }
  • RequestMapping#produces 的匹配过程
ProducesRequestCondition#
    public ProducesRequestCondition getMatchingCondition(HttpServletRequest request) {
        if (CorsUtils.isPreFlightRequest(request)) {
            return PRE_FLIGHT_MATCH;
        }
        // 1)如果未配置 produces 则匹配
        if (isEmpty()) {
            return this;
        }
        // 2)解析客户端能接受的所有 MediaType
        List<MediaType> acceptedMediaTypes;
        try {
            acceptedMediaTypes = getAcceptedMediaTypes(request);
        }
        catch (final HttpMediaTypeException ex) {
            return null;
        }

        // 3)配置的 MediaType 列表中存在请求能接受的 MediaType
        final Set<ProduceMediaTypeExpression> result = new LinkedHashSet<>(expressions);
        result.removeIf(expression -> !expression.match(acceptedMediaTypes));
        if (!result.isEmpty()) {
            return new ProducesRequestCondition(result, contentNegotiationManager);
        }
        // 4)如果客户端能接受所有结果类型 */*
        else if (acceptedMediaTypes.contains(MediaType.ALL)) {
            return EMPTY_CONDITION;
        }
        else {
            return null;
        }
    }

ProduceMediaTypeExpression#
        public final boolean match(List<MediaType> acceptedMediaTypes) {
            final boolean match = matchMediaType(acceptedMediaTypes);
            return !isNegated() ? match : !match;
        }

        private boolean matchMediaType(List<MediaType> acceptedMediaTypes) {
            for (final MediaType acceptedMediaType : acceptedMediaTypes) {
                // 当前 MediaType 和目标 MediaType 匹配
                if (getMediaType().isCompatibleWith(acceptedMediaType)) {
                    return true;
                }
            }
            return false;
        }

RequestMappingInfo 的匹配过程

RequestMappingInfo#
    /**
     * @RequestMapping 的 name 属性值
     */
    @Nullable
    private final String name;

    /**
     *  @RequestMapping path 参数匹配条件
     */
    private final PatternsRequestCondition patternsCondition;
    /**
     *  @RequestMapping method 参数匹配条件
     */
    private final RequestMethodsRequestCondition methodsCondition;
    /**
     *  @RequestMapping params 参数匹配条件
     */
    private final ParamsRequestCondition paramsCondition;
    /**
     *  @RequestMapping headers 参数匹配条件
     */
    private final HeadersRequestCondition headersCondition;
    /**
     * @RequestMapping consumers 参数匹配条件
     */
    private final ConsumesRequestCondition consumesCondition;
    /**
     * @RequestMapping produces 参数匹配条件
     */
    private final ProducesRequestCondition producesCondition;

    private final RequestConditionHolder customConditionHolder;

    /**
     *  使用此  RequestMappingInfo 中的所有条件来匹配目标请求,如果匹配,
     *  则返回一个新的 RequestMappingInfo,否则返回 null。
     */
    @Override
    @Nullable
    public RequestMappingInfo getMatchingCondition(HttpServletRequest request) {
        final RequestMethodsRequestCondition methods = methodsCondition.getMatchingCondition(request);
        if (methods == null) {
            return null;
        }

        final ParamsRequestCondition params = paramsCondition.getMatchingCondition(request);
        if (params == null) {
            return null;
        }

        final HeadersRequestCondition headers = headersCondition.getMatchingCondition(request);
        if (headers == null) {
            return null;
        }

        final ConsumesRequestCondition consumes = consumesCondition.getMatchingCondition(request);
        if (consumes == null) {
            return null;
        }

        final ProducesRequestCondition produces = producesCondition.getMatchingCondition(request);
        if (produces == null) {
            return null;
        }

        final PatternsRequestCondition patterns = patternsCondition.getMatchingCondition(request);
        if (patterns == null) {
            return null;
        }

        final RequestConditionHolder custom = customConditionHolder.getMatchingCondition(request);
        if (custom == null) {
            return null;
        }

        return new RequestMappingInfo(name, patterns,
                methods, params, headers, consumes, produces, custom.getCondition());
    }

RequestMappingInfo 的注册过程

AbstractHandlerMethodMapping#
    private final MappingRegistry mappingRegistry = new MappingRegistry();

    class MappingRegistry {
        /**
         * RequestMappingInfo 和 MappingRegistration 的注册缓存
         */
        private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
        /**
         * RequestMappingInfo 和 HandlerMethod 的注册缓存
         */
        private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();
        /**
         * url 和 RequestMappingInfo 的注册缓存
         */
        private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
        /**
         * MappingName 和 List<HandlerMethod> 的注册缓存
         */
        private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();
        /**
         * HandlerMethod 和 CorsConfiguration 的注册缓存
         */
        private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();
        /**
         *  保障线程安全的读写锁
         */
        private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

        public void register(T mapping, Object handler, Method method) {
            // 获取读锁
            this.readWriteLock.writeLock().lock();
            try {
                // 创建封装了 handler 和 method 的 HandlerMethod 实例,
                final HandlerMethod handlerMethod = createHandlerMethod(handler, method);
                // 确保映射是唯一的
                assertUniqueMethodMapping(handlerMethod, mapping);
                // 写入 RequestMappingInfo 和 handlerMethod 映射到 mappingLookup 缓存
                this.mappingLookup.put(mapping, handlerMethod);

                final List<String> directUrls = getDirectUrls(mapping);
                // 将配置的 url 和 RequestMappingInfo 映射写入 urlLookup 缓存
                for (final String url : directUrls) {
                    this.urlLookup.add(url, mapping);
                }

                String name = null;
                if (getNamingStrategy() != null) {
                    /**
                     *  根据命名策略读取映射的名称
                     * HelloCont.hello() => HC#hello
                     */
                    name = getNamingStrategy().getName(handlerMethod, mapping);
                    addMappingName(name, handlerMethod);
                }

                // 读取控制器或处理方法上 @CrossOrigin 注解配置的跨域信息
                final CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
                if (corsConfig != null) {
                    // 写入 corsLookup 缓存中
                    this.corsLookup.put(handlerMethod, corsConfig);
                }

                // 将 RequestMappingInfo 和 MappingRegistration 映射写入 registry 中
                this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
            }
            finally {
                this.readWriteLock.writeLock().unlock();
            }
        }

        private List<String> getDirectUrls(T mapping) {
            final List<String> urls = new ArrayList<>(1);
            // 从 RequestMappingInfo 中读取配置的请求映射集合
            for (final String path : getMappingPathPatterns(mapping)) {
                // 如果是直接的 Url【pattern 不包含 * 和 ?】
                if (!getPathMatcher().isPattern(path)) {
                    urls.add(path);
                }
            }
            return urls;
        }

        private void addMappingName(String name, HandlerMethod handlerMethod) {
            // 根据 MappingName 读取 HandlerMethod 列表
            List<HandlerMethod> oldList = this.nameLookup.get(name);
            if (oldList == null) {
                oldList = Collections.emptyList();
            }

            // 如果目标 HandlerMethod 已经存在,则直接返回
            for (final HandlerMethod current : oldList) {
                if (handlerMethod.equals(current)) {
                    return;
                }
            }

            // 将 MappingName 和 HandlerMethod 映射写入 nameLookup 中
            final List<HandlerMethod> newList = new ArrayList<>(oldList.size() + 1);
            newList.addAll(oldList);
            newList.add(handlerMethod);
            this.nameLookup.put(name, newList);
        }
    }

    private static class MappingRegistration<T> {
        private final T mapping;
        private final HandlerMethod handlerMethod;
        private final List<String> directUrls;
        @Nullable
        private final String mappingName;
    }

    private class Match {
        private final T mapping;
        private final HandlerMethod handlerMethod;
    }
原文地址:https://www.cnblogs.com/zhuxudong/p/10263071.html