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)写入所有支持的 HttpMethodRequestMethodsRequestCondition# /** * 支持的所有请求方法 */ 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)); }
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; }