zoukankan      html  css  js  c++  java
  • 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;
        }
    
  • 相关阅读:
    PHP使用引用变量foreach时,切记其他循环不要使用同一个名字的变量
    PHP 获取给定时间的周日时间或月末时间或每天
    MySQL Load Data InFile 文件内容导入数据库和 Into OutFile导出数据到文件
    直接拿来用!最火的iOS开源项目(一)
    12个有趣的C语言问答
    Flex,Flash,AS3,AIR的关系和区别
    Stage3D大冒险
    c/c++程序中内存区划分
    IOS—— strong weak retain assign 学习
    如何提高你的移动开发中AS3/AIR性能
  • 原文地址:https://www.cnblogs.com/zhuxudong/p/10263071.html
Copyright © 2011-2022 走看看