当前位置: 代码迷 >> 综合 >> Spring 注解面面通 之 @RequestMapping 请求匹配处理方法源码解析
  详细解决方案

Spring 注解面面通 之 @RequestMapping 请求匹配处理方法源码解析

热度:2   发布时间:2024-01-17 00:59:07.0

??@RequestMapping源码解析主要分为两个阶段:

??① @RequestMapping注释的方法扫描注册。

??② 请求匹配@RequestMapping注释的方法。

??本文针对第阶段从源码角度进行解析,关于第阶段请参照《Spring 注解面面通 之 @RequestMapping 注册处理方法源码解析》。

???**注意:**请求匹配@RequestMapping注释方法的起点是DispatcherServlet.doDispatch(...)

??源码流程

??
??下面列出了源码的调用流程,其中仅涉及主要处理步骤。

??源码解析

??1) DispatcherServlet.doDispatch(...)方法。

??① 取得请求HttpServletRequest异步管理器WebAsyncManager

??② 确认请求HttpServletRequest是否为multipart/form-data类型请求。

??③ 通过请求HttpServletRequestDispatcherServlet.properties提供的默认策略中获取org.springframework.web.servlet.HandlerMapping列表,通过其取得对应的处理器。

??④ 通过请求HttpServletRequestDispatcherServlet.properties提供的默认策略中获取org.springframework.web.servlet.HandlerAdapter列表,通过其取得中处理器对应的处理器适配器。

??⑤ 当Http方法为GETHEAD时,处理Last-Modified请求标头。

??⑥ 按照执行链上拦截器的顺序调用其preHandle(...)方法,此时,若preHandle(...)返回false,会按照执行链上拦截器的反顺序调用其afterCompletion(...)方法。

??⑦ 调用实际的处理器的方法。

??⑧ 若是异步请求,则异步进行处理器处理,DispatcherServlet.doDispatch(...)返回。

??⑨ 若中返回的ModelAndView不包括视图,则配置默认视图。

??⑩ 按照执行链上拦截器的反顺序调用其postHandle(...)方法。

???? 调用DispatcherServlet.processDispatchResult(...)方法处理最终结果,并在方法最后按照执行链上拦截器的反顺序调用其afterCompletion(...)方法。

???? 此外,在最外层try { ... } catch (Exception ex) { ... } catch (Throwable err) { ... }catch { ... }中也会后按照执行链上拦截器的反顺序调用其afterCompletion(...)方法,但仔细查看代码会发现,这种情况发生概率几乎为零。

/*** 分发请求给处理程序.* 处理程序将按照顺序处理Servlet的HandlerMappings.* HandlerAdapter将通过查询Servlet已配置的HandlerAdapter来获取,以找到第一个支持handler类的HandlerAdapter.* 所有的HTTP方法都由这个方法处理.* 由HandlerAdapter或处理程序自己决定哪些方法是可接受的.* @param request 当前请求实例.* @param response 当前响应实例.* @throws Exception .*/
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;// 处理器执行链.HandlerExecutionChain mappedHandler = null;boolean multipartRequestParsed = false;WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);try {
    ModelAndView mv = null;Exception dispatchException = null;try {
    // 确认是否multipart/form-data请求.processedRequest = checkMultipart(request);multipartRequestParsed = (processedRequest != request);// 获得当前请求的处理器.mappedHandler = getHandler(processedRequest);if (mappedHandler == null) {
    noHandlerFound(processedRequest, response);return;}// 获取当前请求的处理器适配器.HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());// 处理last-modified请求头.String method = request.getMethod();boolean isGet = "GET".equals(method);if (isGet || "HEAD".equals(method)) {
    long lastModified = ha.getLastModified(request, mappedHandler.getHandler());if (logger.isDebugEnabled()) {
    logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);}if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
    return;}}// 调用拦截器的applyPreHandle方法.if (!mappedHandler.applyPreHandle(processedRequest, response)) {
    return;}// 实际调用处理器方法处理请求.mv = ha.handle(processedRequest, response, mappedHandler.getHandler());if (asyncManager.isConcurrentHandlingStarted()) {
    return;}// 处理默认视图.applyDefaultViewName(processedRequest, mv);// 调用拦截器的applyPostHandle方法.mappedHandler.applyPostHandle(processedRequest, response, mv);}catch (Exception ex) {
    dispatchException = ex;}catch (Throwable err) {
    // 从4.3开始,同时会处理从handler方法抛出的错误,使它们可以用于@ExceptionHandler方法和其他场景.dispatchException = new NestedServletException("Handler dispatch failed", err);}// 处理转发结果.processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}catch (Exception ex) {
    // 调用拦截器的triggerAfterCompletion方法.triggerAfterCompletion(processedRequest, response, mappedHandler, ex);}catch (Throwable err) {
    // 调用拦截器的triggerAfterCompletion方法.triggerAfterCompletion(processedRequest, response, mappedHandler,new NestedServletException("Handler processing failed", err));}finally {
    if (asyncManager.isConcurrentHandlingStarted()) {
    // 代替postHandle和afterCompletion.if (mappedHandler != null) {
    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);}}else {
    // 清除multipart/form-data请求使用的所有资源.if (multipartRequestParsed) {
    cleanupMultipart(processedRequest);}}}
}

???2) DispatcherServlet.getHandler(...)方法。

??遍历DispatcherServlet的映射处理器handlerMappings,默认情况下,在DispatcherServlet.properties内定义,查找HandlerExecutionChain

/*** 返回处理请求的HandlerExecutionChain.* 按顺序的所有映射.* @param request 当前请求实例.* @return HandlerExecutionChain.*/
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    if (this.handlerMappings != null) {
    for (HandlerMapping hm : this.handlerMappings) {
    if (logger.isTraceEnabled()) {
    logger.trace("Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");}// 根据Request获取处理器.HandlerExecutionChain handler = hm.getHandler(request);if (handler != null) {
    return handler;}}}return null;
}

??3) AbstractHandlerMapping.getHandler(...)方法。

???① 查找当前请求的最佳匹配处理程序方法。

??② 若未匹配到处理器,使用默认处理器。

???③ 若处理器是String类型,则作为Bean名称从ApplicationContext获取Bean

??④ 匹配的处理器转换为HandlerExecutionChain

??⑤ 若请求是有效的CORS请求,则设置相关配置信息。

/*** 查找给定请求的处理程序,如果找不到特定的处理程序,则返回到默认处理程序.* @param request 当前请求实例.* @return 相应的处理程序实例,或默认处理程序.*/
@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    // 查找当前请求的最佳匹配处理程序方法.Object handler = getHandlerInternal(request);// 若不存在,使用默认处理器.if (handler == null) {
    handler = getDefaultHandler();}if (handler == null) {
    return null;}// Bean名称或解析处理程序.if (handler instanceof String) {
    String handlerName = (String) handler;handler = obtainApplicationContext().getBean(handlerName);}// handler转换为HandlerExecutionChain.HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);// 请求是有效的CORS请求.if (CorsUtils.isCorsRequest(request)) {
    CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);executionChain = getCorsHandlerExecutionChain(request, executionChain, config);}return executionChain;
}

??4) AbstractHandlerMethodMapping.getHandlerInternal(...)方法。

??① 通过UrlPathHelper,从HttpServletRequest中获取查找路径。

??② 获取mappingRegistry的锁信息。

??③ 查找当前请求的最佳匹配处理方法。

??④ 释放mappingRegistry的锁信息。

/*** 查找给定请求的处理程序方法.*/
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
    // 获取请求的查找路径.String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);if (logger.isDebugEnabled()) {
    logger.debug("Looking up handler method for path " + lookupPath);}// 取得映射注册器的读取锁.this.mappingRegistry.acquireReadLock();try {
    // 查找当前请求的最佳匹配处理程序方法.HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);if (logger.isDebugEnabled()) {
    if (handlerMethod != null) {
    logger.debug("Returning handler method [" + handlerMethod + "]");}else {
    logger.debug("Did not find handler method for [" + lookupPath + "]");}}return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);}finally {
    this.mappingRegistry.releaseReadLock();}
}

??5) AbstractHandlerMethodMapping.lookupHandlerMethod(...)方法。

??① 直接从mappingRegistry中完全匹配查找lookupPath

??② 若无可完全匹配内容,则需对所有映射进行匹配。

??③ 取得比较器,在查找的匹配结果里得到最优匹配的处理方法。

/*** 查找当前请求的最佳匹配处理程序方法.* 如果找到多个匹配项,则选择最佳匹配项.* @param lookupPath 在当前Servlet映射中映射查找路径.* @param request 当前请求实例.* @return 最佳匹配的处理程序方法,如果不匹配,返回null.*/
@Nullable
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
    List<Match> matches = new ArrayList<>();// 查找完全匹配.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);if (logger.isTraceEnabled()) {
    logger.trace("Found " + matches.size() + " matching mapping(s) for [" + lookupPath + "] : " + matches);}// 选择优先级最高的匹配.Match bestMatch = matches.get(0);if (matches.size() > 1) {
    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();throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" +request.getRequestURL() + "': {" + m1 + ", " + m2 + "}");}}// 在找到匹配映射时调用.handleMatch(bestMatch.mapping, lookupPath, request);return bestMatch.handlerMethod;}else {
    return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);}
}

??6) RequestMappingInfoHandlerMapping.getMappingComparator(...)方法。

??通过Java 8Lambda表达式创建RequestMappingInfo的比较器Comparator

/*** 提供一个比较器来排序与请求匹配的RequestMappingInfos.*/
@Override
protected Comparator<RequestMappingInfo> getMappingComparator(final HttpServletRequest request) {
    return (info1, info2) -> info1.compareTo(info2, request);
}

??7) RequestMappingInfo.compareTo(...)方法。

??① 若Http请求方法是HEAD,则首先进行基于@RequestMapping.method的匹配,若匹配通过,不进行额外匹配。

??② 进行基于@RequestMapping.value@RequestMapping.path的匹配。

??③ 进行基于@RequestMapping.params的匹配。

??④ 进行基于@RequestMapping.headers的匹配。

???⑤ 进行基于@RequestMapping.consumes的匹配。

??⑥ 进行基于@RequestMapping.produces的匹配。

??⑦ 进行基于@RequestMapping.method的匹配。

??⑧ 进行基于自定义条件比较器的匹配.

/*** 将当前RequestMappingInfo(即当前实例)与请求上下文中的另一个RequestMappingInfo进行比较.* 注意: 假设这两个实例都是通过getMatchingCondition(HttpServletRequest)获得的,以确保它们具有与当前请求相关的内容的条件.*/
@Override
public int compareTo(RequestMappingInfo other, HttpServletRequest request) {
    int result;// 自动与显式HTTP头映射.if (HttpMethod.HEAD.matches(request.getMethod())) {
    result = this.methodsCondition.compareTo(other.getMethodsCondition(), request);if (result != 0) {
    return result;}}// RequestMapping.path匹配.result = this.patternsCondition.compareTo(other.getPatternsCondition(), request);if (result != 0) {
    return result;}// RequestMapping.params匹配.result = this.paramsCondition.compareTo(other.getParamsCondition(), request);if (result != 0) {
    return result;}// RequestMapping.headers匹配.result = this.headersCondition.compareTo(other.getHeadersCondition(), request);if (result != 0) {
    return result;}// RequestMapping.consumes匹配.result = this.consumesCondition.compareTo(other.getConsumesCondition(), request);if (result != 0) {
    return result;}// RequestMapping.produces匹配.result = this.producesCondition.compareTo(other.getProducesCondition(), request);if (result != 0) {
    return result;}// RequestMapping.method匹配.// 隐式(无方法)与显式HTTP方法映射.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;
}

??8) RequestMappingInfoHandlerMapping.handleMatch(...)方法。

/*** 暴露URI模板变量、矩阵变量和可生成的媒体类型.*/
@Override
protected void handleMatch(RequestMappingInfo info, String lookupPath, HttpServletRequest request) {
    super.handleMatch(info, lookupPath, request);String bestPattern;Map<String, String> uriVariables;Map<String, String> decodedUriVariables;Set<String> patterns = info.getPatternsCondition().getPatterns();// 若不存在匹配模式,则均设置为空.if (patterns.isEmpty()) {
    bestPattern = lookupPath;uriVariables = Collections.emptyMap();decodedUriVariables = Collections.emptyMap();}else {
    bestPattern = patterns.iterator().next();// 解析URI模板变量.uriVariables = getPathMatcher().extractUriTemplateVariables(bestPattern, lookupPath);// 对URI模板变量进行解码.decodedUriVariables = getUrlPathHelper().decodePathVariables(request, uriVariables);}// 设置匹配模式到HttpServletRequest的BEST_MATCHING_PATTERN_ATTRIBUTE.request.setAttribute(BEST_MATCHING_PATTERN_ATTRIBUTE, bestPattern);// 设置URI模板变量到HttpServletRequest的HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE.request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, decodedUriVariables);// 处理矩阵变量.if (isMatrixVariableContentAvailable()) {
    // 提取矩阵变量.Map<String, MultiValueMap<String, String>> matrixVars = extractMatrixVariables(request, uriVariables);// 设置矩阵变量到HttpServletRequest的HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE.request.setAttribute(HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE, matrixVars);}// 处理可生成的媒体类型.if (!info.getProducesCondition().getProducibleMediaTypes().isEmpty()) {
    // 获取可生成的媒体类型.Set<MediaType> mediaTypes = info.getProducesCondition().getProducibleMediaTypes();// 设置可生成媒体类型到HttpServletRequest的PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE.request.setAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, mediaTypes);}
}

??总结

??本文分析了请求匹配处理方法的流程,其中仅对重要步骤源码进行了分析。整个过程中如何进行条件匹配部分内容尚未分析,会在后续博文中解析。

??源码解析基于spring-framework-5.0.5.RELEASE版本源码。

??若文中存在错误和不足,欢迎指正!

  相关解决方案