【RequestMapping 注解的解析匹配注册】犀渠玉剑良家子,白马金羁侠少年。这篇文章主要讲述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;
}
推荐阅读
- windows7网卡驱动,本文教您如何容易修好网络问题
- Mybatis学习第二天——mapper的动态代理
- Android-PopupWindow
- Android——输入法搜索
- virtualenv 和 virtualenvwrapper 使用教程
- goreplay 镜像nginx web app流量
- androidrecyclerview+GalleryLayoutManager 实现广告画廊效果
- Flutter - 给App增加启动屏幕(Splash Screen)并且设置背景颜色
- spring BeanFactory和ApplicatContext