SpringMVC底层——请求参数处理流程描述

在DispatcherServlet.java的doDispatch方法中,springmvc通过handlermapping里面找哪个handler能处理请求,handler封装了目标方法的信息,

mappedHandler = getHandler(processedRequest);

然后为当前的handler找到一个适配器HandlerAdapter,寻找的过程为:在DispatcherServlet.java的getHandlerAdapter方法中,挨个匹配,判断当前adapter是否支持当前handler,判断方法为只要handler是handlerMethod类型就生效,就支持

HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
if (this.handlerAdapters != null) {
    for (HandlerAdapter adapter : this.handlerAdapters) {
        if (adapter.supports(handler)) {
            return adapter;
            }
    }
}

找到适配器以后判断当前请求是不是”GET”方法以及”HEAD”,”HEAD”不是服务器真正处理的

适配器 HandlerAdapter把(目标方法、request、response)传入handle执行目标方法

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

怎么执行目标方法:

  • 先得到handler
return this.handler;
  • 再进入内部处理细节RequestMappingHandlerAdapter.java,调用的invokeHandlerMethod就是 *执行目标方法
mav = invokeHandlerMethod(request,response,handlerMethod);
  • 在RequestMappingHandlerAdapter.java的invokeHandlerMethod方法中,
  • 为invocableMethod方法设置 参数解析器argumentResolvers,参数解析器确定将要执行的目标方法的每一个参数的值是什么
    • 当前解析器是否支持解析这种参数
    • 支持就调用 resolveArgument
  • 为invocableMethod方法设置 返回值处理器returnValueHandlers
  • 把26个argumentResolvers和15个returnValueHandlers都放入目标方法包装的ServletInvocableHandlerMethod中
  • 然后真正执行目标方法的语句
invocableMethod.invokeAndHandle(WebRequest,mavContainer);
  • 在ServletInvocableHandlerMethod.java的invokeAndHandle方法中,执行了controller
Object returnValue = invokeForRequest(webRequest,mavContainer,proviedArgs);
+ step into 进入InvocableHandlerMethod.java, **确定目标方法每一个参数的值**
Object[] agrs = getMethodArgumentValues(request,mavContainer,providerArgs)
  * 在InvocableHandlerMethod.java的getMethodArgumentValues方法中,先获取方法所有的参数声明(详细信息)。
MethodParameter[] parameters = getMethodParameters();
  * 判断参数是否为空,为空则无需确定任何值直接返回;
if (ObjectUtils.isEmpty(parameters)) {
    return EMPTY_ARGS;
}

如果有参数列表,new一个Object[],参数列表有多少个Object[]就有多长

Object[] args = new Object[parameters.length];
    for (int i = 0; i < parameters.length; i++) {
        MethodParameter parameter = parameters[i];
        parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
        args[i] = findProvidedArgument(parameter, providedArgs);

先声明args遍历parameters,给args[i]赋值,args[i]的值解析器解析了才有
* 解析之前,判断26个解析器是不是 supportsParamter支持这个参数类型。

this.resolvers.supportsParameter(parameter)
    - HandlerMethodArgumentResolverComposite.java的getArgumentResolver方法中
    - 获取一个缓存result
    - result==null,进入增强for循环,逐个确定26个解析器谁能支持这种参数
      + supportsParameter方法,传来的参数有没有hasParameterAnnotation标注注解
      + 没有就return false
      + 如果标了,再判断参数是否map类型,
      + return true支持解析
    - 当前resolver支持解析,放到缓存里边,判断成功,进入解析
  * **解析参数**
this.resolvers.resolveArgument
    - HandlerMethodArgumentResolverComposite.java的resolverArgument方法中,先拿到所有的参数解析器getArgumentResolver
    - 调用参数解析器的resolverArgument方法进行解析
      + 获取参数名字信息
      + 解析参数的名字,placeholderResolved、BeanExpressionResolver解析evaluate计算名字,按照正则匹配的方式
      + 解析参数的值
        * uriTemlateVars 在request请求域中拿到值;UrlPathHelper会把uri地址里边的所有的路径变量全部解析出来并保存到请求域中
  * 遍历循环所有参数
+ 最终返回args,args就是确定好的值
  • 处理返回结果的时候,把mavContainer传进去,
this.returnValueHandlers.handlerReturnValue
+ 在handlerMethod.java的getReturnValueType方法中获取返回的结果类型
+ HandlerMethodArgumentResolverComposite.java的handleReturnValue方法中, 找到返回值的处理器 如果返回值是一个字符串,拿到字符串然后保存到mavContainer
  • 返回值处理完以后,getModelAndView
  • 目标方法执行完成 将所有的数据都放在 ModelAndViewContainer;包含要去的页面地址View。还包含Model数据。
  • 从ModelAndViewContainer拿到默认的Model,updateBindingResult,拿到key放到绑定里边,又被封装成ModelAndView,然后返回这个新封装的mav
  • 处理派发结果
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

拿到所有请求域中的属性,解析得到视图名 渲染页面

view.render(mv.getModelInternal(),request,response);

拿到页面数据

createMergeOutputModel(model,request,response);
&#x521B;&#x9020;&#x5408;&#x5E76;&#x7684;&#x8F93;&#x51FA;&#x6A21;&#x578B;

如果model不等于空

mergeModel.putAll(model)//&#x5373;&#x628A;&#x6570;&#x636E;&#x8F6C;&#x79FB;&#x5230;HashMap

渲染合并输出的模型数据

renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
+ 把上面的Hashmap传进来了
+ 拿到请求对象,获取的原生的Servletrequest
return originalRequest
+ 暴露模型作为请求域属性
// Expose the model object as request attributes.
        exposeModelAsRequestAttributes(model, request);
  * 把model里面的东西进行遍历
  * 遍历以后每一个request setAttribute

Original: https://www.cnblogs.com/Salinger/p/16492350.html
Author: Lincee
Title: SpringMVC底层——请求参数处理流程描述

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/574004/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • MySQL实现备份案例(2)

    案例1:MySQL8.0实现数据库冷备份和还原 10.0.0.10 — MySQL8.0 #&#x505C;&#x6B62;&#x6570;&a…

    Linux 2023年6月7日
    0104
  • 设置Docker的默认文件存储位置

    对于windows下,直接修改docker desktop界面的配置项目。对于rocky linux下面,对应的配置文件存储在: vim /etc/docker/daemon.js…

    Linux 2023年6月6日
    077
  • jenkins

    jenkins 1.简介 1.1 SVN介绍 1.2 Maven介绍 1.3 Ant介绍 1.4 Gradle介绍 1.5 jenkins工作原理 1.6 jenkins特点 2….

    Linux 2023年6月7日
    0115
  • TCP/UDP 编程模型

    TCP编程模型 server创建socket套接字 socket套接字–可以理解为文件描述符(file descriptor),UNIX把网络看成文件 /** * @p…

    Linux 2023年6月6日
    0108
  • Linux ARMv7中断向量表搬移(2)【转】

    经历过kernel的汇编阶段,进入C语言start_kernel后对中断向量表的位置进行搬移,搬移函数是early_trap_init。 early_trap_init函数的调用流…

    Linux 2023年6月8日
    0106
  • git reset 命令删除本地文件怎么恢复

    执行 git reflog命令可以看到曾经执行过的操作,还有版本序号。 执行 git reset –hard HEAD@{【填那个序号】}就可以恢复本地删除的文件了! …

    Linux 2023年6月14日
    0107
  • Linux巡检脚本

    #!/bin/bash sys:centos6.x/7.x [ $(id -u) -ne 0 ] && echo "&#x8BF7;&#x…

    Linux 2023年6月6日
    097
  • python 练习题:将列表中的大写字母转换成小写

    将列表中的大写字母转换成小写如果list中既包含字符串,又包含整数,由于非字符串类型没有lower()方法,L1 = [‘Hello’, ‘World’, 18, ‘Apple’,…

    Linux 2023年6月8日
    0145
  • redis如何设置密码

    密码设置这里简单介绍一下redis如何设置密码redis密码设置有两种方式,一种需要重启redis服务,一种不需要重启redis服务。 首先,介绍一下需要重启redis服务的设置方…

    Linux 2023年5月28日
    0100
  • redis重点是 dir 的默认配置一定要改

    find / -name dump.rdb 发现有两个dump文件,这两个文件目录不一致,问题在于 redis.conf 文件属性dir,默认配置是dir ./ 表示在哪启动ser…

    Linux 2023年5月28日
    083
  • Python 多线程

    import threading import time def userTest(aa,bb): print(aa) time.sleep(3) print(bb) if __n…

    Linux 2023年6月6日
    080
  • 个人学习-Linux文件系统架构

    个人学习-Linux文件系统架构 1. 参考文章 [1] https://blog.csdn.net/Holy_666/article/details/86532671 [2]CS…

    Linux 2023年6月6日
    0102
  • 位图实现

    位图就是用每个字节中的bit位代表一组资源的映射。 例如:一个字节有8位,在操作系统中可以用一个bit位代表一个4K的页,那一个字节就可以代表8页32K内存。 可以利用位图进行资源…

    Linux 2023年6月7日
    088
  • Linux下使用压力测试工具stress

    首先解压安装包到/usr/local/src/下 mv stress-1.0.4.tar.gz /usr/local/src​tar -zxf stress-1.0.4.tar.g…

    Linux 2023年6月13日
    089
  • js学习笔记——条件 循环

    今天发现之前学的爱前端的课中JS部分函数等不全,果断换了一个课——渡一的《Web前端开发JavaScript高薪课堂》接着学习,不过废话有点多 语法:1、单if,条件成立,执行语句…

    Linux 2023年6月13日
    073
  • 原来这就是网络

    你是一台电脑,你的名字叫A,很久很久之前,你不与任何其他电脑相连接,孤苦伶仃。 直到有一天,你希望与另一台电脑B建立连接,于是你们各开了一个网口,用一根网线连接了起来。 用一根网线…

    Linux 2023年6月14日
    0115
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球