文章目录
- 前言
- 一、AbstractHandlerMethodMapping
- 1.1 mappingRegistry
- 1.2 MappingRegistry 注册表
- 1.3 getHandlerInternal
- 1.4 lookupHandlerMethod
- 二、RequestMappingInfoHandlerMapping
- 三、RequestMappingHandlerMapping
- 总结
前言
RequestMappingHandlerMapping
是Spring MVC中的一个请求映射处理器,它负责将HTTP请求映射到特定的@RequestMapping
注解的方法上。允许你使用简单的注解(如@GetMapping
、@PostMapping
、@RequestMapping
等)来定义请求路径和HTTP方法。
工作机制:
* 当Spring MVC的DispatcherServlet
接收到一个HTTP请求时,它会查找一个合适的HandlerMapping
来处理这个请求。
* RequestMappingHandlerMapping
会检查它的映射注册表,该注册表包含了所有使用@RequestMapping
注解的方法的映射信息。
* 如果找到了一个匹配的映射,那么RequestMappingHandlerMapping
会返回一个HandlerExecutionChain
,它包含了要执行的处理器(通常是HandlerMethod
,代表一个@Controller
中的方法)和任何与之关联的拦截器。
与其他组件的关系:
* RequestMappingHandlerMapping
与HandlerAdapter
(如RequestMappingHandlerAdapter
)紧密合作。一旦RequestMappingHandlerMapping
找到了一个匹配的处理器,它会将这个处理器传递给HandlerAdapter
,后者负责调用处理器并执行相应的逻辑。
* RequestMappingHandlerMapping
还可能与HandlerInterceptor
一起使用,以在请求处理过程中添加额外的逻辑(如日志记录、身份验证等)。
由类图可知,RequestMappingHandlerMapping的父类AbstractHandlerMethodMapping实现了InitializingBean接口,RequestMappingHandlerMapping和AbstractHandlerMethodMapping均实现了afterPropertiesSet() 方法,该方法会在bean属性完成初始化后被调用。
一、AbstractHandlerMethodMapping
AbstractHandlerMethodMapping,实现 InitializingBean 接口,继承 AbstractHandlerMapping 抽象类,以 Method 方法 作为 Handler 处理器 的 HandlerMapping 抽象类,提供 Mapping 的初始化、注册等通用的骨架方法。
那么具体是什么呢?AbstractHandlerMethodMapping 定义为了 泛型,交给子类做决定。例如,子类 RequestMappingInfoHandlerMapping 使用 RequestMappingInfo 类作为 泛型,也就是我们在上面注解模块看到的 @RequestMapping 等注解信息。
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
/**
* 是否只扫描可访问的 HandlerMethod 们
*/
private boolean detectHandlerMethodsInAncestorContexts = false;
/**
* Mapping 命名策略
*/
@Nullable
private HandlerMethodMappingNamingStrategy<T> namingStrategy;
/**
* Mapping 注册表
*/
private final MappingRegistry mappingRegistry = new MappingRegistry();
}
1.1 mappingRegistry
HandlerMethodMappingNamingStrategy 接口,HandlerMethod 的 Mapping 的名字生成策略接口。
@FunctionalInterface
public interface HandlerMethodMappingNamingStrategy<T> {
/**
* 根据 HandlerMethod 获取名称,就是为对应的 Mappring 对象生成一个名称,便于获取
*/
String getName(HandlerMethod handlerMethod, T mapping);
}
public class RequestMappingInfoHandlerMethodMappingNamingStrategy implements HandlerMethodMappingNamingStrategy<RequestMappingInfo> {
/** Separator between the type and method-level parts of a HandlerMethod mapping name. */
public static final String SEPARATOR = "#";
@Override
public String getName(HandlerMethod handlerMethod, RequestMappingInfo mapping) {
// 情况一,mapping 名字非空,则使用 mapping 的名字
if (mapping.getName() != null) {
return mapping.getName();
}
// 情况二,使用类名大写 + "#" + 方法名
StringBuilder sb = new StringBuilder();
String simpleTypeName = handlerMethod.getBeanType().getSimpleName();
for (int i = 0; i < simpleTypeName.length(); i++) {
if (Character.isUpperCase(simpleTypeName.charAt(i))) {
sb.append(simpleTypeName.charAt(i));
}
}
sb.append(SEPARATOR).append(handlerMethod.getMethod().getName());
return sb.toString();
}
}
情况一,如果 Mapping 已经配置名字,则直接返回。例如,@RequestMapping(name = “login”, value = “user/login”) 注解的方法,它对应的 Mapping 的名字就是 login
情况二,如果 Mapping 未配置名字,则使用使用类名大写 + “#” + 方法名。例如,@RequestMapping(value = “user/login”) 注解的方法,假设它所在的类为 UserController ,对应的方法名为 login ,则它对应的 Mapping 的名字就是 USERCONTROLLER#login
1.2 MappingRegistry 注册表
class MappingRegistry {
/**
* 注册表
*
* Key: Mapping
* Value:{@link MappingRegistration}(Mapping + HandlerMethod)
*/
private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
/**
* 注册表2
*
* Key:Mapping
* Value:{@link HandlerMethod}
*/
private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();
/**
* 直接 URL 的映射
*
* Key:直接 URL(就是固定死的路径,而非多个)
* Value:Mapping 数组
*/
private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
/**
* Mapping 的名字与 HandlerMethod 的映射
*
* Key:Mapping 的名字
* Value:HandlerMethod 数组
*/
private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();
private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();
/**
* 读写锁
*/
private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
public void register(T mapping, Object handler, Method method) {
// <1> 获得写锁
this.readWriteLock.writeLock().lock();
try {
// <2.1> 创建 HandlerMethod 对象
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
// <2.2> 校验当前 mapping 是否存在对应的 HandlerMethod 对象,如果已存在但不是当前的 handlerMethod 对象则抛出异常
assertUniqueMethodMapping(handlerMethod, mapping);
// <2.3> 将 mapping 与 handlerMethod 的映射关系保存至 this.mappingLookup
this.mappingLookup.put(mapping, handlerMethod);
// <3.1> 获得 mapping 对应的普通 URL 数组
List<String> directUrls = getDirectUrls(mapping);
// <3.2> 将 url 和 mapping 的映射关系保存至 this.urlLookup
for (String url : directUrls) {
this.urlLookup.add(url, mapping);
}
// <4> 初始化 nameLookup
String name = null;
if (getNamingStrategy() != null) {
// <4.1> 获得 Mapping 的名字
name = getNamingStrategy().getName(handlerMethod, mapping);
// <4.2> 将 mapping 的名字与 HandlerMethod 的映射关系保存至 this.nameLookup
addMappingName(name, handlerMethod);
}
// <5> 初始化 CorsConfiguration 配置对象
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
if (corsConfig != null) {
this.corsLookup.put(handlerMethod, corsConfig);
}
// <6> 创建 MappingRegistration 对象
// 并与 mapping 映射添加到 registry 注册表中
this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
}
finally {
// <7> 释放写锁
this.readWriteLock.writeLock().unlock();
}
}
}
1.3 getHandlerInternal
通过 AbstractHandlerMapping 的 getHandler(HttpServletRequest request) 方法获取 HandlerExecutionChain 处理器执行链时,需要调用 getHandlerInternal 抽象方法获取处理器,这个方法由子类去实现,就到这里了。
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
// <1> 获得请求的路径
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
// <2> 获得读锁
this.mappingRegistry.acquireReadLock();
try {
// <3> 获得 HandlerMethod 对象
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
// <4> 进一步,获得一个新的 HandlerMethod 对象
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
// <5> 释放读锁
this.mappingRegistry.releaseReadLock();
}
}
1.4 lookupHandlerMethod
获得请求对应的 HandlerMethod 处理器对象
@Nullable
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
// <1> Match 数组,存储匹配上当前请求的结果(Mapping + HandlerMethod)
List<Match> matches = new ArrayList<>();
// <1.1> 优先,基于直接 URL (就是固定死的路径,而非多个)的 Mapping 们,进行匹配
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
addMatchingMappings(directPathMatches, matches, request);
}
// <1.2> 其次,扫描注册表的 Mapping 们,进行匹配
if (matches.isEmpty()) {
// No choice but to go through all mappings...
addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
}
// <2> 如果匹配到,则获取最佳匹配的 Match 结果的 `HandlerMethod`属性
if (!matches.isEmpty()) {
// <2.1> 创建 MatchComparator 对象,排序 matches 结果,排序器
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
matches.sort(comparator);
// <2.2> 获得首个 Match 对象,也就是最匹配的
Match bestMatch = matches.get(0);
// <2.3> 处理存在多个 Match 对象的情况!!
if (matches.size() > 1) {
if (logger.isTraceEnabled()) {
logger.trace(matches.size() + " matching mappings: " + matches);
}
if (CorsUtils.isPreFlightRequest(request)) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
// 比较 bestMatch 和 secondBestMatch ,如果相等,说明有问题,抛出 IllegalStateException 异常
// 因为,两个优先级一样高,说明无法判断谁更优先
Match secondBestMatch = matches.get(1);
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
String uri = request.getRequestURI();
throw new IllegalStateException(
"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
}
}
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
// <2.4> 处理首个 Match 对象
handleMatch(bestMatch.mapping, lookupPath, request);
// <2.5> 返回首个 Match 对象的 handlerMethod 属性
return bestMatch.handlerMethod;
}
// <3> 如果匹配不到,则处理不匹配的情况
else {
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
}
二、RequestMappingInfoHandlerMapping
当 Spring MVC 收到一个 HTTP 请求时,会通过 RequestMappingInfoHandlerMapping 来查找与请求匹配的 HandlerMethod,然后执行该方法来处理请求。
RequestMappingInfo
org.springframework.web.servlet.mvc.method.RequestMappingInfo,实现 RequestCondition 接口,每个方法的定义的请求信息,也就是 @RequestMapping 等注解的信息
public final class RequestMappingInfo implements RequestCondition<RequestMappingInfo> {
@Nullable
private final String name;
//请求路径条件(url合并,筛选url,择优url)
private final PatternsRequestCondition patternsCondition;
//请求方法条件
private final RequestMethodsRequestCondition methodsCondition;
//请求参数条件
private final ParamsRequestCondition paramsCondition;
//请求头条件
private final HeadersRequestCondition headersCondition;
//请求content-type条件
private final ConsumesRequestCondition consumesCondition;
//请求accept条件
private final ProducesRequestCondition producesCondition;
//自定义请求条件
private final RequestConditionHolder customConditionHolder;
}
getMatchingCondition
getMatchingCondition(HttpServletRequest request)方法,从当前 RequestMappingInfo 获得匹配的条件。如果匹配,则基于其匹配的条件,创建新的 RequestMappingInfo 对象,如果不匹配,则返回 null ,代码如下:
@Override
@Nullable
public RequestMappingInfo getMatchingCondition(HttpServletRequest request) {
// 匹配 methodsCondition、paramsCondition、headersCondition、consumesCondition、producesCondition
// 如果任一为空,则返回 null ,表示匹配失败
RequestMethodsRequestCondition methods = this.methodsCondition.getMatchingCondition(request);
if (methods == null) {
return null;
}
ParamsRequestCondition params = this.paramsCondition.getMatchingCondition(request);
if (params == null) {
return null;
}
HeadersRequestCondition headers = this.headersCondition.getMatchingCondition(request);
if (headers == null) {
return null;
}
ConsumesRequestCondition consumes = this.consumesCondition.getMatchingCondition(request);
if (consumes == null) {
return null;
}
ProducesRequestCondition produces = this.producesCondition.getMatchingCondition(request);
if (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;
}
/*
* 创建匹配的 RequestMappingInfo 对象
* 一个 methodsCondition 可以配置 GET、POST、DELETE 等等条件,
* 但是实际就匹配一个请求类型,此时 methods 只代表其匹配的那个。
*/
return new RequestMappingInfo(this.name, patterns,
methods, params, headers, consumes, produces, custom.getCondition());
}
三、RequestMappingHandlerMapping
RequestMappingHandlerMapping实现 MatchableHandlerMapping、EmbeddedValueResolverAware 接口,继承 RequestMappingInfoHandlerMapping 抽象类,基于@RequestMapping 注解来构建 RequestMappingInfo 对象。
类的属性定义
public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping
implements MatchableHandlerMapping, EmbeddedValueResolverAware {
private boolean useSuffixPatternMatch = true;
private boolean useRegisteredSuffixPatternMatch = false;
private boolean useTrailingSlashMatch = true;
private Map<String, Predicate<Class<?>>> pathPrefixes = new LinkedHashMap<>();
private ContentNegotiationManager contentNegotiationManager = new ContentNegotiationManager();
@Nullable
private StringValueResolver embeddedValueResolver;
//RequestMappingInfo 的构建器
private RequestMappingInfo.BuilderConfiguration config = new RequestMappingInfo.BuilderConfiguration();
afterPropertiesSet
因父类 AbstractHandlerMethodMapping 实现了 InitializingBean 接口,在 Sping 初始化该 Bean 的时候,会调用该方法,完成一些初始化工作,方法如下:
@Override
public void afterPropertiesSet() {
// 构建 RequestMappingInfo.BuilderConfiguration 对象
this.config = new RequestMappingInfo.BuilderConfiguration();
this.config.setUrlPathHelper(getUrlPathHelper());
this.config.setPathMatcher(getPathMatcher());
this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);
this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);
this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);
this.config.setContentNegotiationManager(getContentNegotiationManager());
// 调用父类,初始化
super.afterPropertiesSet();
}
isHandler
是否还记得 AbstractHandlerMethodMapping 的这个抽象方法?在它的 processCandidateBean 方法中,扫描 Spring 中所有 Bean 时会调用,判断是否需要扫描这个 Bean 中的方法,有 @Controller 或者 @RequestMapping 的注解的类才需要进行扫描,方法如下:
@Override
protected boolean isHandler(Class<?> beanType) {
// 判断是否有 @Controller 或者 @RequestMapping 的注解
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
getMappingForMethod
AbstractHandlerMethodMapping 的 detectHandlerMethods 方法中调用该方法,用于获取 Method 方法对应的 Mapping 对象,方法如下:
@Override
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
//1、基于方法上的 @RequestMapping 注解,创建 RequestMappingInfo 对象
RequestMappingInfo info = createRequestMappingInfo(method);
if (info != null) {
// 2、 基于类上的 @RequestMapping 注解,合并进去
RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
if (typeInfo != null) {
info = typeInfo.combine(info);
}
// 3、 如果有前缀,则设置到 info 中
String prefix = getPathPrefix(handlerType);
if (prefix != null) {
info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
}
}
return info;
}
createRequestMappingInfo
基于方法上的 @RequestMapping 注解,创建 RequestMappingInfo 对象
@Nullable
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
// <1> 获得 @RequestMapping 注解
RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
// <2> 获得自定义的条件。目前都是空方法,可以无视
RequestCondition<?> condition = (element instanceof Class ?
getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
// <3> 基于 @RequestMapping 注解,创建 RequestMappingInfo 对象
return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}
protected RequestMappingInfo createRequestMappingInfo(RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {
// 创建 RequestMappingInfo.Builder 对象,设置对应属性
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);
}
// 创建 RequestMappingInfo 对象
return builder.options(this.config).build();
}
总结
将RequestMappingHandlerMapping注入到 Spring 上下文时,会进行一些初始化工作,扫描 @Controller 或者 @RequestMapping 注解标注的 Bean 对象,会将带有 @RequestMapping 注解(包括其子注解)解析成 RequestMappingInfo 对象。接下来,会将 RequestMappingInfo、该方法对象、该方法所在类对象 往 MappingRegistry 注册表进行注册,其中会生成 HandlerMethod 处理器(方法的所有信息)对象保存起来。当处理某个请求时,HandlerMapping 找到该请求对应的 HandlerMethod 处理器对象后,就可以通过反射调用相应的方法了。