• SpringtMVC运行流程:@RequestMapping 方法中的 Map、HttpServletRequest等参数信息是如何封装和传递的(源码理解)


    在平时开发SpringtMVC程序时,在Controller的方法上,通常会传入如Map、HttpServletRequest类型的参数,并且可以方便地向里面添加数据。同时,在Jsp中还可以直接使用request等对象方便地获取出来。

    如下面2图所示:

     

    可问题是:@RequestMapping 方法中的 Map、HttpServletRequest等参数信息是如何封装和传递的?

    带着这个问题,写了个简单的Demo,来进行源码调试。

    Demo代码地址:

    https://github.com/cyhbyw/springMVC_atguigu_TongGang

    工程名称:

    springMVC_DebugSourceCode

     

    ===========以下是源码调试==================

    ==============>>>>
    PS:图片可能不是很清晰,可以右击图片、选择在新标签页中查看
    或者,可以右击图片,选择“图片另存为”保存在本地并编好号(建议直接以01、02、03……来编号)
    或者,可以右击图片,选择“复制图片”,再保存到本地并编好号(建议直接以01、02、03……来编号)
    以上三种办法,任意选择喜欢的一种,以获得并查看更清晰的图片~~
    <<<<==============

    01.首先,浏览器发出的请求到DispatcherServlet;然后,找到合适的HandlerAdapter(此处是RequestMappingHandlerAdapter);然后调用RequestMappingHandlerAdapter的handle()方法。此时,方法堆栈从Line959开始。

     

      

    02.还是在DispatcherServlet的Line959的doDispatch()方法内,又调用了几个方法,到达invokeForRequest()方法;顾名思义,此方法会真正的调用Request方法(即Controller中的方法);不过,先会在Line128解析参数。

     

     

     03.解析参数的方法又会走到Line161.

     

     

     04.再经过两个方法的调用,可以看到上述的参数解析方法是直接返回了 mavContainer.getModle()

    而由06步可以看到,Model实际上是个map对象,那么,此处直接返回map对象就相当于返回了其引用,外部任何地方的修改都将影响此map中的值!!!

    这个引用会给第08步中的 Object[] args 以及第09、10步中的 map,所以第10步中向 map 中添加数据,其实也就等价于向此 Model 中添加了数据!!!(背景知识:Java对象引用)

     

      

    05.而getModel() 方法返回 defaulutModel 的成员变量。

     

      

    06. defaulutModel 其实就是一个 BindingAwareModelMap

     

     

     07.回到刚才的Line161,可以看到 args[i] 指向了刚才得到的BindingAwareModelMap@4821。

     

      

    08.再返回一层,此处的 Object[] args 还是BindingAwareModelMap@4821;并且到目前为止,其中的元素仍然为空;此处Line136的 doInvoke(args) 方法就是通过反射调用真实的Controller中的方法。

     

      

    09.调用真实方法(由08步中的Line136反射调用);可以看到,真实Controller方法中的入参 map 还是BindingAwareModelMap@4821(说明是同一个对象——非常重要!!)

     

     

    10.将值写入map中

     

     

    11.真实Controller方法调用返回后,可以看到 mavContainer 对象中的 defaultModel 属性已经被赋值,且这个值就是BindingAwareModelMap@4821,与args是同一个对象(非常重要,而且从前面的分析中也可以看到)!!!!

    (备注:SpringMVC是如何为mavContainer 对象中的 defaultModel 属性赋值的,最开始调试了很久也没有发现,心想着,它既然是个Map,那应该是调用setXXX(), put(), putAll()这样的方法赋值进去的,但调试了很久始终没发现这样的方法被调用;同时,也可以确定它是在Line136行调用后就被赋值的;最后猜测,是同一个对象引用;现在,证明确实如此)

    再啰嗦一句,其实就是:Line128的 Object[] args 变量指向了 mavContainer 对象的 defaultModel 属性!所以在将 args 通过Line136反射调用真实的Controller方法并填充数据后,defaultModel中也就有了相应数据!

    注意结合第04步中的文字进行理解!

     

     12.真实Controller方法调用完成后,开始处理返回值

     

     13.设置视图名称

     

     

     14.准备创建ModelAndView对象

     

      

    15.从 mavContainer 中取出Model数据,并通过构造函数传入ModelAndView

     

      

    16.已经获得ModelAndView对象,进行后续操作(如渲染);注意,此时DispatcherServlet中的方法堆栈从Line971开始。

     

      

    17.准备渲染

     

      

    18.得到View对象

     

      

    19.遍历 viewResolvers 找到一个合适的就返回给18步中的View对象

     

      

    20.准备渲染

     

      

    21.将Model数据暴露为RequestAttribute

     

      

    22.暴露的本质其实是:request.setAttribute(modelName, modelValue)

    重要:在这里是 request.setAttribute 所以在 Jsp 中才可以 request.getAttribute,先有 set 才有 get 嘛。

     

      

    23.最后是一个转发操作

     

     

  • 相关阅读:
    我爱java系列之---【微服务间的认证—Feign拦截器】
    我爱java系列之---【设置权限的三种解决方案】
    581. Shortest Unsorted Continuous Subarray
    129. Sum Root to Leaf Numbers
    513. Find Bottom Left Tree Value
    515. Find Largest Value in Each Tree Row
    155. Min Stack max stack Maxpop O(1) 操作
    painting house
    Minimum Adjustment Cost
    k Sum
  • 原文地址:https://www.cnblogs.com/cyhbyw/p/8618747.html
Copyright © 2020-2023  润新知