1. 前言
Spring MVC将请求处理器定义为handler,因为handler可以以很多形式存在,所以Spring并没有限制handler的类型,用Object来表示。然后又因为这个原因,Spring MVC针对不同的handler设计了不同的HandlerAdapter来协调handler处理请求。
那么,Spring是怎么根据请求Request查找到对应的处理器handler的呢?
2. HandlerMapping
根据请求Request查找到对应的处理器handler,这个职责Spring交给了HandlerMapping处理。HandlerMapping是处理器映射器,它的职责就是查找请求对应的处理器。
java
public interface HandlerMapping {
@Nullable
HandlerExecutionChain getHandler(HttpServletRequest request);
}
HandlerMapping接口定义足够简单,只有一个方法getHandler()
,根据请求request对象,查找到可以处理请求的处理器handler。又因为handler是可以支持自定义拦截器HandlerInterceptor的,所以Spring将handler和一堆拦截器统一封装为HandlerExecutionChain对象。
3. HandlerExecutionChain
因为除了handler本身处理业务逻辑之外,Spring MVC还支持给handler注册拦截器HandlerInterceptor,所以Spring MVC把它们封装成了HandlerExecutionChain对象。
java
public class HandlerExecutionChain {
// 目标处理器
private final Object handler;
// 一堆拦截器
@Nullable
private HandlerInterceptor[] interceptors;
@Nullable
private List<HandlerInterceptor> interceptorList;
// 拦截器索引
private int interceptorIndex = -1;
}
HandlerExecutionChain对象通过HandlerMapping#getHandler()
方法构建:
java
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// 子类获取Handler
Object handler = getHandlerInternal(request);
if (handler == null) {
// 没有,尝试用默认处理器
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// 如果返回的是字符串,则认定为它的处理器的beanName,去容器加载handler
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
// 基于handler和一堆拦截器 构建HandlerExecutionChain
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (logger.isTraceEnabled()) {
logger.trace("Mapped to " + handler);
} else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
logger.debug("Mapped to " + executionChain.getHandler());
}
if (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this.corsConfigurationSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
构建HandlerExecutionChain的流程是:
- 调用子类自定义获取handler的逻辑,如果没有尝试使用默认处理器。
- 如果handler是字符串类型,则认为它是beanName,通过容器获取。
- 基于handler和一堆拦截器构建HandlerExecutionChain。
4. 获取Handler
父类主要是基于子类返回的handler和拦截器构建HandlerExecutionChain,子类获取handler的逻辑才是核心,方法是AbstractHandlerMapping#getHandlerInternal()
。HandlerMethod是我们常用的handler,所以我们重点看AbstractHandlerMethodMapping类。
java
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
// 查找HandlerMethod的路径 即解析请求URI
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
this.mappingRegistry.acquireReadLock();// 加读锁
try {
// 从MappingRegistry中查找Handler
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
} finally {
this.mappingRegistry.releaseReadLock();
}
}
主要做了两件事:
- 解析请求的URI,作为查找handler的依据。
- 通过MappingRegistry查找handler。
MappingRegistry是映射器注册表,Spring MVC启动时会将扫描到的handler注册到MappingRegistry,查找handler的代码是:
java
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<>();
// URI直接路径匹配
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
// 没有直接匹配到,只能模糊匹配
addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
}
if (!matches.isEmpty()) {
// 匹配到多个,按优先级排序
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
matches.sort(comparator);
Match bestMatch = matches.get(0);
if (matches.size() > 1) {
if (logger.isTraceEnabled()) {
logger.trace(matches.size() + " matching mappings: " + matches);
}
if (CorsUtils.isPreFlightRequest(request)) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
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 + "}");
}
}
// 把匹配到的HandlerMethod写入request 最佳匹配handler
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.handlerMethod;
} else {
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
}
步骤:
- 先按URI直接路径匹配
- URI没有直接匹配到,只能模糊匹配
- 如果匹配到多个,按优先级排序
- 把匹配到的HandlerMethod写入request属性,记为"最佳匹配handler"
5. HandlerMapping怎么来的
HandlerMapping可以有多个,通过属性handlerMappings
保存,它是一个List。查找handler的过程也很简单,遍历所有HandlerMapping挨个查找,只要找到了handler就直接返回。
java
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
那HandlerMapping是怎么来的呢???
Spring MVC通过变量detectAllHandlerMappings
来控制是否要自动检测所有HandlerMapping,默认是true。
- 如果detectAllHandlerMappings为true,则加载容器内所有的HandlerMapping,否则只加载beanName为"handlerMapping"的单个HandlerMapping。
- 如果容器内没有可用的HandlerMapping,Spring还有一个兜底方案,就是通过
getDefaultStrategies()
加载默认的handlerMappings。
默认的handlerMappings是通过classPath下名为DispatcherServlet.properties
的配置文件来加载的,如下是Spring MVC的默认配置:
java
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping