源代码版本 : spring-webmvc-5.1.4.RELEASE
概述
之前我用一篇文章介绍了Spring MVC
的概念模型接口RequestCondition
及其一些基本的针对请求匹配某一方面的实现类。现在我们看看封装了一个请求匹配所有方面的实现类RequestMappingInfo
。
源代码分析
RequestMappingInfo
实现类的代码主体如下 :
package org.springframework.web.servlet.mvc.method;
import java.util.List;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import org.springframework.http.HttpMethod;
import org.springframework.lang.Nullable;
import org.springframework.util.PathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.mvc.condition.ConsumesRequestCondition;
import org.springframework.web.servlet.mvc.condition.HeadersRequestCondition;
import org.springframework.web.servlet.mvc.condition.ParamsRequestCondition;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.condition.ProducesRequestCondition;
import org.springframework.web.servlet.mvc.condition.RequestCondition;
import org.springframework.web.servlet.mvc.condition.RequestConditionHolder;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.util.UrlPathHelper;
public final class RequestMappingInfo implements RequestCondition<RequestMappingInfo> {
// 从下面的属性定义可以看出, RequestMappingInfo 综合包装了对一个请求进行匹配的各个方面的匹配条件
@Nullable
private final String name;
private final PatternsRequestCondition patternsCondition;
private final RequestMethodsRequestCondition methodsCondition;
private final ParamsRequestCondition paramsCondition;
private final HeadersRequestCondition headersCondition;
private final ConsumesRequestCondition consumesCondition;
private final ProducesRequestCondition producesCondition;
private final RequestConditionHolder customConditionHolder;
// 两个 RequestMappingInfo 的合并其实是它们所包含的同种类型的请求匹配条件的合并之后形成的新
// RequestMappingInfo 对象
@Override
public RequestMappingInfo combine(RequestMappingInfo other) {
String name = combineNames(other);
PatternsRequestCondition patterns = this.patternsCondition.combine(other.patternsCondition);
RequestMethodsRequestCondition methods = this.methodsCondition.combine(other.methodsCondition);
ParamsRequestCondition params = this.paramsCondition.combine(other.paramsCondition);
HeadersRequestCondition headers = this.headersCondition.combine(other.headersCondition);
ConsumesRequestCondition consumes = this.consumesCondition.combine(other.consumesCondition);
ProducesRequestCondition produces = this.producesCondition.combine(other.producesCondition);
RequestConditionHolder custom = this.customConditionHolder.combine(
other.customConditionHolder);
return new RequestMappingInfo(name, patterns,
methods, params, headers, consumes, produces, custom.getCondition());
}
@Nullable
private String combineNames(RequestMappingInfo other) {
if (this.name != null && other.name != null) {
String separator = RequestMappingInfoHandlerMethodMappingNamingStrategy.SEPARATOR;
return this.name + separator + other.name;
}
else if (this.name != null) {
return this.name;
}
else {
return other.name;
}
}
// 将一个 RequestMappingInfo 对象应用于指定的 request,其实是将其所包含的各个请求匹配条件分别应用于
// 该请求对象 request, 如果这些条件中任何一个不匹配,就返回 null, 如果这些条件都匹配,则将所获取的
// 条件匹配裁剪子集包装成一个新的 RequestMappingInfo 对象返回
@Override
@Nullable
public RequestMappingInfo getMatchingCondition(HttpServletRequest request) {
RequestMethodsRequestCondition methods = this.methodsCondition.getMatchingCondition(request);
ParamsRequestCondition params = this.paramsCondition.getMatchingCondition(request);
HeadersRequestCondition headers = this.headersCondition.getMatchingCondition(request);
ConsumesRequestCondition consumes = this.consumesCondition.getMatchingCondition(request);
ProducesRequestCondition produces = this.producesCondition.getMatchingCondition(request);
if (methods == null || params == null || headers == null || consumes == null
|| produces == null) {
return null;
}
PatternsRequestCondition patterns = this.patternsCondition.getMatchingCondition(request);
if (patterns == null) {
return null;
}
RequestConditionHolder custom = this.customConditionHolder.getMatchingCondition(request);
if (custom == null) {
return null;
}
return new RequestMappingInfo(this.name, patterns,
methods, params, headers, consumes, produces, custom.getCondition());
}
@Override
public int compareTo(RequestMappingInfo other, HttpServletRequest request) {
int result;
// Automatic vs explicit HTTP HEAD mapping
if (HttpMethod.HEAD.matches(request.getMethod())) {
result = this.methodsCondition.compareTo(other.getMethodsCondition(), request);
if (result != 0) {
return result;
}
}
result = this.patternsCondition.compareTo(other.getPatternsCondition(), request);
if (result != 0) {
return result;
}
result = this.paramsCondition.compareTo(other.getParamsCondition(), request);
if (result != 0) {
return result;
}
result = this.headersCondition.compareTo(other.getHeadersCondition(), request);
if (result != 0) {
return result;
}
result = this.consumesCondition.compareTo(other.getConsumesCondition(), request);
if (result != 0) {
return result;
}
result = this.producesCondition.compareTo(other.getProducesCondition(), request);
if (result != 0) {
return result;
}
// Implicit (no method) vs explicit HTTP method mappings
result = this.methodsCondition.compareTo(other.getMethodsCondition(), request);
if (result != 0) {
return result;
}
result = this.customConditionHolder.compareTo(other.customConditionHolder, request);
if (result != 0) {
return result;
}
return 0;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof RequestMappingInfo)) {
return false;
}
RequestMappingInfo otherInfo = (RequestMappingInfo) other;
return (this.patternsCondition.equals(otherInfo.patternsCondition) &&
this.methodsCondition.equals(otherInfo.methodsCondition) &&
this.paramsCondition.equals(otherInfo.paramsCondition) &&
this.headersCondition.equals(otherInfo.headersCondition) &&
this.consumesCondition.equals(otherInfo.consumesCondition) &&
this.producesCondition.equals(otherInfo.producesCondition) &&
this.customConditionHolder.equals(otherInfo.customConditionHolder));
}
@Override
public int hashCode() {
return (this.patternsCondition.hashCode() * 31 + // primary differentiation
this.methodsCondition.hashCode() + this.paramsCondition.hashCode() +
this.headersCondition.hashCode() + this.consumesCondition.hashCode() +
this.producesCondition.hashCode() + this.customConditionHolder.hashCode());
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder("{");
if (!this.methodsCondition.isEmpty()) {
Set<RequestMethod> httpMethods = this.methodsCondition.getMethods();
builder.append(httpMethods.size() == 1 ? httpMethods.iterator().next() : httpMethods);
}
if (!this.patternsCondition.isEmpty()) {
Set<String> patterns = this.patternsCondition.getPatterns();
builder.append(" ").append(patterns.size() == 1 ? patterns.iterator().next() : patterns);
}
if (!this.paramsCondition.isEmpty()) {
builder.append(", params ").append(this.paramsCondition);
}
if (!this.headersCondition.isEmpty()) {
builder.append(", headers ").append(this.headersCondition);
}
if (!this.consumesCondition.isEmpty()) {
builder.append(", consumes ").append(this.consumesCondition);
}
if (!this.producesCondition.isEmpty()) {
builder.append(", produces ").append(this.producesCondition);
}
if (!this.customConditionHolder.isEmpty()) {
builder.append(", and ").append(this.customConditionHolder);
}
builder.append('}');
return builder.toString();
}
}
由上面代码可以看出,RequestMappingInfo
包装了用于匹配一个请求对象的所有方面,事实上,Spring MVC
使用RequestMappingHandlerMapping
检测到各个控制器类/方法时,正是使用RequestMappingInfo
对各个控制器方法上定义的请求匹配条件进行封装的:
// RequestMappingHandlerMapping 类代码片段
// method 是控制器方法,handlerType 是控制器类
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
// 针对控制器方法(特指使用了注解@RequestMapping的方法)上注解信息,创建一个 RequestMappingInfo 对象,
// 如果这是一个控制器的普通方法,而不是一个控制器方法,则返回的 info 对象会是 null
RequestMappingInfo info = createRequestMappingInfo(method);
if (info != null) {
// 针对控制器类上注解信息,创建一个 RequestMappingInfo 对象
RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
if (typeInfo != null) {
// 合并控制器方法上的 RequestMappingInfo 和控制器类上的 RequestMappingInfo
info = typeInfo.combine(info);
}
String prefix = getPathPrefix(handlerType);
if (prefix != null) {
// 如果对控制器类定义了路径前缀信息 ,考虑合并该前缀信息
info = RequestMappingInfo.paths(prefix).build().combine(info);
}
}
return info;
}
// element 这里可能代表控制器类本身,也可能代表某个控制器方法
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
// 获取 element 的注解@RequestMapping信息
RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element,
RequestMapping.class);
// 缺省情况下这里 condition 为 nul (这里 condition 表示自定义请求匹配条件)
RequestCondition<?> condition = (element instanceof Class ?
getCustomTypeCondition((Class<?>) element) :
getCustomMethodCondition((Method) element));
return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}
@Nullable
protected RequestCondition<?> getCustomTypeCondition(Class<?> handlerType) {
return null;
}
@Nullable
protected RequestCondition<?> getCustomMethodCondition(Method method) {
return null;
}
/**
* Create a RequestMappingInfo from the supplied
* RequestMapping @RequestMapping annotation, which is either
* a directly declared annotation, a meta-annotation, or the synthesized
* result of merging annotation attributes within an annotation hierarchy.
*/
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();
}
从上面的代码可以看出,RequestMappingInfo
的主要作用就是用来对应封装一个控制方法的@RequestMapping
注解信息,该注解信息是综合了控制器方法以及控制器类上的@RequestMapping
等注解属性而合成的一个信息对象。
参考文章
Spring MVC 概念模型 : 接口 RequestCondition
Spring MVC HandlerMapping : RequestMappingHandlerMapping 源代码解析