1. 前言
Spring MVC将请求处理器定义为handler,因为handler可以以很多形式存在,所以Spring并没有限制handler的类型,用Object来表示。然后又因为这个原因,Spring MVC针对不同的handler设计了不同的HandlerAdapter来协调handler处理请求。
那么,Spring是怎么根据请求Request查找到对应的处理器handler的呢?
2. HandlerMapping
根据请求Request查找到对应的处理器handler,这个职责Spring交给了HandlerMapping处理。HandlerMapping是处理器映射器,它的职责就是查找请求对应的处理器。
public interface HandlerMapping {@NullableHandlerExecutionChain getHandler(HttpServletRequest request);
}
HandlerMapping接口定义足够简单,只有一个方法getHandler()
,根据请求request对象,查找到可以处理请求的处理器handler。又因为handler是可以支持自定义拦截器HandlerInterceptor的,所以Spring将handler和一堆拦截器统一封装为HandlerExecutionChain对象。
3. HandlerExecutionChain
因为除了handler本身处理业务逻辑之外,Spring MVC还支持给handler注册拦截器HandlerInterceptor,所以Spring MVC把它们封装成了HandlerExecutionChain对象。
public class HandlerExecutionChain {// 目标处理器private final Object handler;// 一堆拦截器@Nullableprivate HandlerInterceptor[] interceptors;@Nullableprivate List<HandlerInterceptor> interceptorList;// 拦截器索引private int interceptorIndex = -1;
}
HandlerExecutionChain对象通过HandlerMapping#getHandler()
方法构建:
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {// 子类获取HandlerObject handler = getHandlerInternal(request);if (handler == null) {// 没有,尝试用默认处理器handler = getDefaultHandler();}if (handler == null) {return null;}// 如果返回的是字符串,则认定为它的处理器的beanName,去容器加载handlerif (handler instanceof String) {String handlerName = (String) handler;handler = obtainApplicationContext().getBean(handlerName);}// 基于handler和一堆拦截器 构建HandlerExecutionChainHandlerExecutionChain 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类。
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {// 查找HandlerMethod的路径 即解析请求URIString lookupPath = getUrlPathHelper().getLookupPathForRequest(request);this.mappingRegistry.acquireReadLock();// 加读锁try {// 从MappingRegistry中查找HandlerHandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);} finally {this.mappingRegistry.releaseReadLock();}
}
主要做了两件事:
- 解析请求的URI,作为查找handler的依据。
- 通过MappingRegistry查找handler。
MappingRegistry是映射器注册表,Spring MVC启动时会将扫描到的handler注册到MappingRegistry,查找handler的代码是:
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 最佳匹配handlerrequest.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就直接返回。
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的默认配置:
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping