我们通过debug方式阅读源码来探究ModelAndView执行流程,在这个过程中我们不需要每行代码都弄懂,从程序开始到结束我们只需关心我们关注的点,ModelAndView的作用以及和Model,Map的区别会在文章末尾揭示
这是ModelAndView类的主要结构:
其中的ModelMap可以看作是一个Map实现类
ModelAndView我们用来无非是往作用域传值和跳转页面,我们来看这两个方法:
-
mav.addObject:
我们可以看出其作用就相当于往map中添加键值对 -
mav.setViewName():
public void setViewName(@Nullable String viewName) {
this.view = viewName;
}
它是通过字符串的形式往成员变量中赋值
通过debug模式开始测试方法
1.这是我们这个程序的一个controller
2.
这三个箭头是我们需要关注的地方,现在开始运行,发现有很多我们并不知道的类和方法,但我们知道,我们的请求是通过DispatcherServlet被分发的,所以我们找到对应的类,如下图箭头所示,在相应的行打上断点,我们可以发现mv就是一个ModelAndView对象,此时的mv对象还为null,因为还没运行这个断点
3.
通过包围断点行的try—catch块我们可以判断出:
下一步要执行的必定是processDispatchResult
这个方法,我们可以看一下ha.handle()
方法执行后的结果
4.
我们可以发现mv对象已经经过处理,携带了转发的试图名和往作用域添加的对象,具体执行过程在这里不是我们讨论的重点,下一步就是执行processDispatchResult()
方法,通过字面意思可以了解到这个方法就是处理分发的结果,processDispatchResult()
执行完后DispatcherServlet流程也就结束,因此我们进入这个方法一探究竟
5.
上面的if (exception != null)
表示遇到异常后的处理,我们不需要关注,直接看下面被标记的行,也就是进入processDispatchResult()
方法后会执行的代码,它将我们之前的mv对象传进来,我们进入这个方法
6.
发现resolveViewName
,它的作用是解析视图名,返回view
对象,它分析出的结果是将我们添加的视图名映射到对应的视图解析器,我们继续往reder方法下面走,我们发现了上一步的view对象已经被解析出来,正是我们自己配置的内容
但是这个render方法对应的是view接口中的方法,但是实现的却是一个抽象类AbstractView
,我们进去看一下
void render(@Nullable Map<String, ?> model,
HttpServletRequest request, HttpServletResponse response) throws Exception;
7.
我们看到这个方法中首先创建了Map对象mergedModel,并将之前的mv当作model参数传入,这里我们需要关注renderMergedOutputModel
,因为它操作着我们的mv对象,
8.
首先第一个方法,通过字面意思我们可以看出它的大概意思:将model暴露为一个request,我们进入该方法:
结果一目了然:
通过循环的方式取出map中的键值对,并通过request.setAttribute()
方法往作用域中添加值
9.
接着这个方法往下走:我们可以看到创建了一个dispatcherPath的路径,我们接着往下看:
RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
帮我们获得请求转发的一个对象,并在最后实现了转发功能,跳转到我们指定的路径
总结:
通过对ModleAndView的源码分析,我们知道通过ModelAndView放作用域中放值其实是往request域中增加属性,并通过请求转发跳转到我们制定的资源路径
Map和ModelAndView
我们再次通过debug模式来分析:
我们发现:
通过map的方式和通过ModelAndView的作用是一模一样的,map也是通过ModelAndView来实现的
Model和ModelAndView
不用多说,通过Model的方式和通过ModelAndView的作用是一模一样的,Model也是通过ModelAndView来实现的