SpringMVC源码解析DispatcherServlet#doDispatch方法流程(下)

简介: SpringMVC源码解析DispatcherServlet#doDispatch方法流程(下)

getHandler(processedRequest);

  • 为此请求返回HandlerExecutionChain。按顺序尝试所有的handler mapping
  • image.png
  • 获取当前请求对应的处理类,在这个处理链中会包含对应的拦截器的信息。HandlerExecutionChain这个类中包含变和不变量的两部分内容
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
  for (HandlerMapping hm : this.handlerMappings) {
    HandlerExecutionChain handler = hm.getHandler(request);
    if (handler != null) {
      return handler;
    }
  }
  return null;
}

循环handlerMappings,然后获取对应的执行链,只要找到一个对应的执行链就返回


SpringMVC默认加载三个请求处理映射类:


RequestMappingHandlerMapping

SimpleUrlHandlerMapping

BeanNameUrlHandlerMapping

这三个类有一个共同的父类:AbstractHandlerMapping。


hm.getHandler(request)这个getHandler方法在AbstractHandlerMapping中,它的子类都没有重写这个方法。

AbstractHandlerMethodMapping#getHandler

@Override
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
  // 两个子类重写该方法:AbstractHandlerMethodMapping和AbstractUrlHandlerMapping
  Object handler = getHandlerInternal(request);
  // 如果没有找到的话,默认处理类
  if (handler == null) {
    handler = getDefaultHandler();
  }
  // 如果没有默认的处理类,返回null
  if (handler == null) {
    return null;
  }
  // Bean name or resolved handler?
  if (handler instanceof String) {
    String handlerName = (String) handler;
    handler = getApplicationContext().getBean(handlerName);
  }
  // 包装为执行器链
  HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
  // 是不是跨域请求
  if (CorsUtils.isCorsRequest(request)) {
    CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);
    CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
    CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
    executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
  }
  return executionChain;
}

AbstractHandlerMethodMapping#getHandlerInternal

  @Override
  protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
    // 得到请求 url 的查询路径
    String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
    }
    // 获取并发读锁
    this.mappingRegistry.acquireReadLock();
    try {
      // 得到查询路径对应的处理器方法
      HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
      return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
    }
    finally {
      this.mappingRegistry.releaseReadLock();
    }
  }

getHandlerExecutionChain

创建执行器链的内容:

protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
  //判断handler是不是执行器链,如果不是创建一个执行器链
  HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
      (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
  String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
  //包装拦截器
  for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
    if (interceptor instanceof MappedInterceptor) {
      MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
      if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
        chain.addInterceptor(mappedInterceptor.getInterceptor());
      }
    }
    else {
      chain.addInterceptor(interceptor);
    }
  }
  return chain;
}

这个方法主要是创建执行器链,添加拦截器.

判断

if (mappedHandler == null || mappedHandler.getHandler() == null) {
  noHandlerFound(processedRequest, response);
  return;
}

如果没有找到对应的处理类的话,这里通常会返回404,如果throwExceptionIfNoHandlerFound属性值为true的情况下会抛出异常。

我们继续往下分析:

HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

getHandlerAdapter

获取处理适配器

image.png

SimpleControllerHandlerAdapter

适配SimpleUrlHandlerMapping和BeanNameUrlHandlerMapping的映射的,也就是实现Controller接口的Handler

AbstractHandlerMethodAdapter

适配RequestMappingHandlerMapping,也就是我们常用的RequestMapping注解

HttpRequestHandlerAdapter

适配远程调用的

SimpleServletHandlerAdapter

适配Servlet实现类的

supports

image.png

supportsInternal

总是返回true ,因为任何方法参数和返回值类型会以某种方式加以处理。

不被任何HandlerMethodArgumentResolver识别的方法参数被解释为一个请求参数,如果它是一个简单的类型,或者作为模型属性否则。

没有任何HandlerMethodReturnValueHandler识别的返回值将被解释为一个模型属性

image.png

如果是GET请求,内容没有变化则直接返回

applyPreHandle

  • 应用已注册的preHandle拦截方法
  • image.png
  • image.png

handle()

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

执行handle

applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);

如果返回的mv不为null,并且没有设置view,则设置默认的view

应用postHandle拦截器。

processDispatchResult

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
    HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
  boolean errorView = false;
  if (exception != null) {
    if (exception instanceof ModelAndViewDefiningException) {
      mv = ((ModelAndViewDefiningException) exception).getModelAndView();
    }
    else {
      Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
      mv = processHandlerException(request, response, handler, exception);
      errorView = (mv != null);
    }
  }
  //返回的ModelAndView不为null
  // Did the handler return a view to render?
  if (mv != null && !mv.wasCleared()) {
    //解析页面
    render(mv, request, response);
    if (errorView) {
      WebUtils.clearErrorRequestAttributes(request);
    }
  }
  else {
  }
  if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
    // Concurrent handling started during a forward
    return;
  }
  // 调用处理拦截器的afterCompletion
  if (mappedHandler != null) {
    mappedHandler.triggerAfterCompletion(request, response, null);
  }
}

如果


出现异常,返回异常页面

没有异常,ModelAndView不为null,则正常渲染页面,调用拦截器的afterCompletion方法


目录
相关文章
|
2月前
|
存储 域名解析 弹性计算
阿里云上云流程参考:云服务器+域名+备案+域名解析绑定,全流程图文详解
对于初次通过阿里云完成上云的企业和个人用户来说,很多用户不仅是需要选购云服务器,同时还需要注册域名以及完成备案和域名的解析相关流程,从而实现网站的上线。本文将以上云操作流程为核心,结合阿里云的活动政策与用户系统梳理云服务器选购、域名注册、备案申请及域名绑定四大关键环节,以供用户完成线上业务部署做出参考。
|
8月前
|
监控 Shell Linux
Android调试终极指南:ADB安装+多设备连接+ANR日志抓取全流程解析,覆盖环境变量配置/多设备调试/ANR日志分析全流程,附Win/Mac/Linux三平台解决方案
ADB(Android Debug Bridge)是安卓开发中的重要工具,用于连接电脑与安卓设备,实现文件传输、应用管理、日志抓取等功能。本文介绍了 ADB 的基本概念、安装配置及常用命令。包括:1) 基本命令如 `adb version` 和 `adb devices`;2) 权限操作如 `adb root` 和 `adb shell`;3) APK 操作如安装、卸载应用;4) 文件传输如 `adb push` 和 `adb pull`;5) 日志记录如 `adb logcat`;6) 系统信息获取如屏幕截图和录屏。通过这些功能,用户可高效调试和管理安卓设备。
|
8月前
|
前端开发 数据安全/隐私保护 CDN
二次元聚合短视频解析去水印系统源码
二次元聚合短视频解析去水印系统源码
331 4
|
8月前
|
安全 IDE Java
重学Java基础篇—Java Object类常用方法深度解析
Java中,Object类作为所有类的超类,提供了多个核心方法以支持对象的基本行为。其中,`toString()`用于对象的字符串表示,重写时应包含关键信息;`equals()`与`hashCode()`需成对重写,确保对象等价判断的一致性;`getClass()`用于运行时类型识别;`clone()`实现对象复制,需区分浅拷贝与深拷贝;`wait()/notify()`支持线程协作。此外,`finalize()`已过时,建议使用更安全的资源管理方式。合理运用这些方法,并遵循最佳实践,可提升代码质量与健壮性。
257 1
|
8月前
|
JavaScript 算法 前端开发
JS数组操作方法全景图,全网最全构建完整知识网络!js数组操作方法全集(实现筛选转换、随机排序洗牌算法、复杂数据处理统计等情景详解,附大量源码和易错点解析)
这些方法提供了对数组的全面操作,包括搜索、遍历、转换和聚合等。通过分为原地操作方法、非原地操作方法和其他方法便于您理解和记忆,并熟悉他们各自的使用方法与使用范围。详细的案例与进阶使用,方便您理解数组操作的底层原理。链式调用的几个案例,让您玩转数组操作。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
8月前
|
负载均衡 JavaScript 前端开发
分片上传技术全解析:原理、优势与应用(含简单实现源码)
分片上传通过将大文件分割成多个小的片段或块,然后并行或顺序地上传这些片段,从而提高上传效率和可靠性,特别适用于大文件的上传场景,尤其是在网络环境不佳时,分片上传能有效提高上传体验。 博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
345 2
|
8月前
|
算法 测试技术 C语言
深入理解HTTP/2:nghttp2库源码解析及客户端实现示例
通过解析nghttp2库的源码和实现一个简单的HTTP/2客户端示例,本文详细介绍了HTTP/2的关键特性和nghttp2的核心实现。了解这些内容可以帮助开发者更好地理解HTTP/2协议,提高Web应用的性能和用户体验。对于实际开发中的应用,可以根据需要进一步优化和扩展代码,以满足具体需求。
831 29
|
8月前
|
移动开发 前端开发 JavaScript
从入门到精通:H5游戏源码开发技术全解析与未来趋势洞察
H5游戏凭借其跨平台、易传播和开发成本低的优势,近年来发展迅猛。接下来,让我们深入了解 H5 游戏源码开发的技术教程以及未来的发展趋势。

推荐镜像

更多
  • DNS