SpringMVC核心——返回值问题
一、SpringMVC 使用 ModelAndView 来处理返回值问题。
1.ModelAndView
官方描述:
|
Holder for both Model and View in the web MVC framework. <p>Represents a model and view returned by a handler, to be resolved |
说明一下:
在 springmvc 框架中,ModelAndView 表示同时持有 Model 和 View 。Model 和 View 是完全不同的。
这个类使一个控制器返回单独的一个值同时包含 model 和 view 成为一种可能。
同时也代表着一个处理器返回一个 model 和 view ,会被 DispatcherServlet 解析。
View 对象如果是通过一个字符串形式的 view name 获取到的,则能被 ViewResolver 对象解析。
或者也可以直接指定 View 对象。model 是一个 Map 类型,可以使用多个对象的键值对。
2.这里要搞明白一点,为什么说是通过 ModelAndView 来处理返回值问题,事实上,返回的类型可以是很多种,可以是 ModelAndView 类型,
也可以是String类型,还可以是View类型,还有很多。但是他们最终会被解析为 ModelAndView 类型的对象。
通过源码来证实一下:
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter#invokeHandlerMethod
在这个方法中:
Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);
ModelAndView mav = methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);
其中 result 就是我们调用 handler 方法后的返回值。
通过 mehtodInvoker.getModelAndView() 方法将 result 最终解析为 ModelAndView 对象。
来看看具体过程:
public ModelAndView getModelAndView(Method handlerMethod, Class<?> handlerType, Object returnValue,
ExtendedModelMap implicitModel, ServletWebRequest webRequest) throws Exception {
ResponseStatus responseStatusAnn = AnnotationUtils.findAnnotation(handlerMethod, ResponseStatus.class);
if (responseStatusAnn != null) {
HttpStatus responseStatus = responseStatusAnn.value();
String reason = responseStatusAnn.reason();
if (!StringUtils.hasText(reason)) {
webRequest.getResponse().setStatus(responseStatus.value());
}
else {
webRequest.getResponse().sendError(responseStatus.value(), reason);
} // to be picked up by the RedirectView
webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, responseStatus); responseArgumentUsed = true;
} // Invoke custom resolvers if present...
if (customModelAndViewResolvers != null) {
for (ModelAndViewResolver mavResolver : customModelAndViewResolvers) {
ModelAndView mav = mavResolver.resolveModelAndView(
handlerMethod, handlerType, returnValue, implicitModel, webRequest);
if (mav != ModelAndViewResolver.UNRESOLVED) {
return mav;
}
}
} if (returnValue instanceof HttpEntity) {
handleHttpEntityResponse((HttpEntity<?>) returnValue, webRequest);
return null;
}
else if (AnnotationUtils.findAnnotation(handlerMethod, ResponseBody.class) != null) {
handleResponseBody(returnValue, webRequest);
return null;
}
else if (returnValue instanceof ModelAndView) {
ModelAndView mav = (ModelAndView) returnValue;
mav.getModelMap().mergeAttributes(implicitModel);
return mav;
}
else if (returnValue instanceof Model) {
return new ModelAndView().addAllObjects(implicitModel).addAllObjects(((Model) returnValue).asMap());
}
else if (returnValue instanceof View) {
return new ModelAndView((View) returnValue).addAllObjects(implicitModel);
}
else if (AnnotationUtils.findAnnotation(handlerMethod, ModelAttribute.class) != null) {
addReturnValueAsModelAttribute(handlerMethod, handlerType, returnValue, implicitModel);
return new ModelAndView().addAllObjects(implicitModel);
}
else if (returnValue instanceof Map) {
return new ModelAndView().addAllObjects(implicitModel).addAllObjects((Map<String, ?>) returnValue);
}
else if (returnValue instanceof String) {
return new ModelAndView((String) returnValue).addAllObjects(implicitModel);
}
else if (returnValue == null) {
// Either returned null or was 'void' return.
if (this.responseArgumentUsed || webRequest.isNotModified()) {
return null;
}
else {
// Assuming view name translation...
return new ModelAndView().addAllObjects(implicitModel);
}
}
else if (!BeanUtils.isSimpleProperty(returnValue.getClass())) {
// Assume a single model attribute...
addReturnValueAsModelAttribute(handlerMethod, handlerType, returnValue, implicitModel);
return new ModelAndView().addAllObjects(implicitModel);
}
else {
throw new IllegalArgumentException("Invalid handler method return value: " + returnValue);
}
}
这个方法很重要,它描述的是将 handler 方法返回值解析为 ModelAndView 的过程,从中可以看出返回值可以为很多类型。建议大家都看看。这篇文章不对具体的每个返回值类型进行说明。
3.从官方描述中,也可以看出 ModelAndView 是分为两部分的,Model 和 View。这里就分两部分来说明。其中 View 部分其实是视图渲染问题。
4.Model 模型。
这里所说的 Model ,不单单指的就是 Model 具体这个类。而是描述的 SpringMVC 如何将 数据存放到 Model 中,以便在目标页面中使用。
(1)怎么存放
上面部分已经说过,通过 ModelAndView 对象来处理返回值问题。那么就可以通过如下的方式向 Model 中存入数据。如:
@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView() {
ModelAndView mv = new ModelAndView();
mv.setViewName("success");
mv.addObject("testKey", "testValue");
return mv;
}
通过 ModelAndView 对象的 addObject() 方法 可以向模型中添加数据。
事实上,可以在方法的入参处添加 Model 类型 或 Map 类型的参数,可以通过向其中添加数据来完成向模型中数据的添加。如:
@RequestMapping("/testModelAndView02")
public String testModelAndView2(Map map) {
map.put("testKey", "testValue");
return "success";
}
为什么向 handler 方法的入参处添加 Model 类型或 Map 类型参数添加数据,就能完成 模型数据的添加。
通过断点发现传入目标方法的 Map 实际类型是 BindingAwareModelMap 这个类型。
源码解析:
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter#invokeHandlerMethod() 方法中:
ExtendedModelMap implicitModel = new BindingAwareModelMap(); // 在这里创建的
Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);
org.springframework.web.bind.annotation.support.HandlerMethodInvoker#resolveHandlerArguments() 方法中
if (Model.class.isAssignableFrom(paramType) || Map.class.isAssignableFrom(paramType)) {
if (!paramType.isAssignableFrom(implicitModel.getClass())) {
throw new IllegalStateException("Argument [" + paramType.getSimpleName() + "] is of type " +
"Model or Map but is not assignable from the actual model. You may need to switch " +
"newer MVC infrastructure classes to use this argument.");
}
args[i] = implicitModel;//然后在这个地方进行的赋值。
}
可以看出在目标方法处的 所有 Map 类型(包括其子类型)或是 Model 类型(包括其子类型)的参数都会被转换为 BindingAwareModelMap 这个类型。
这里对 BindingAwareModelMap 类型进行说明:

(2)存放什么
从上面可以看出,存放的是键值对类型的 Map 或 Model。
(3)存放到何处
看下面这个例子:
@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView() {
ModelAndView mv = new ModelAndView();
mv.setViewName("success");
mv.addObject("testKey", "testValue");
return mv;
}
success.jsp

通过测试,发现 Model 中的数据默认被存放到了 Request 域中。
5.总结
SpringMVC 通过 ModelAndView 解决了 handler 方法返回值问题,明白了 handler 方法返回值可以是何种类型,为什么说 ModelAndView 解决了 handler方法返回值问题,因为 handler 方法的
返回值最终都会被转换成 ModelAndView 对象。也详细的介绍了 Model 可以作为 handler 方法的入参使用,这里所说的 Model 也不仅仅是指 Model 这个类型,也指实现了 Model 或 Map 接口的
类型。也明白了 Model 中存放的什么,存放到了哪里。至此,SpringMVC 方法的返回值问题已经学习完。接下来要学习的是:既然返回值都已经有了,那么该如何去处理呢?——即handler 方法返回
值处理问题,也指视图渲染问题。另外,还有两个注解没有进行说明,@SessionAttribute和@ModelAttribute,仔细来说,其实他们两个注解也可以算到 Model 中,这里会再写一篇文章来单独说明它两。
SpringMVC核心——返回值问题的更多相关文章
- SpringMVC Controller 返回值几种类型
SpringMVC Controller 返回值几种类型 2016年06月21日 19:31:14 为who而生 阅读数:4189 标签: Controller 返回值类型spring mvc 更多 ...
- springmvc 之 返回值
springMVC对于controller处理方法返回值的可选类型 spring mvc 支持如下的返回方式:ModelAndView, Model, ModelMap, Map,View, Stri ...
- SpringMVC 拦截返回值,并自定义
有关取代mvc:annotation-driven使用自定义配置请看: http://blog.csdn.net/cml_blog/article/details/45222431 1.在项目开发中, ...
- SpringMVC Controller 返回值的可选类型
spring mvc 支持如下的返回方式:ModelAndView, Model, ModelMap, Map,View, String, void. ModelAndView @RequestMap ...
- 转SpringMVC Controller 返回值的可选类型
spring mvc 支持如下的返回方式:ModelAndView, Model, ModelMap, Map,View, String, void. ModelAndView @RequestMap ...
- spring mvc 第三天【注解实现springmvc Handler返回值为Object 的配置】
这里使用的是在前台发起请求Handler,后台伪造数据响应给前台, 解决方案:将之前的viewResolver抹掉,配置对应(request)请求的Handler信息如下 之前Handler返回的都直 ...
- SpringMVC学习笔记三:Controller的返回值
springMVC的返回值有ModelAndView,String,void,Object类型 项目目录树: 该项目是在前面项目的基础上修改的,这里的pom.xml文件需要加入使用到的包,应为@Res ...
- Spring MVC controller返回值类型
SpringMVC controller返回值类型: 1 String return "user":将请求转发到user.jsp(forword) return "red ...
- mvc 各种返回值
一个例子胜过千言万语,直接上代码 SpringMVC的Controller控制器返回值详解 SpringMVC Controller 返回值几种类型 Spring MVC 更灵活的控制 json 返回 ...
随机推荐
- Javascript 严格模式
简介 严格模式是一种将更好的错误检查引入代码中的方法. 在使用严格模式时,你无法使用隐式声明的变量.将值赋给只读属性或将属性添加到不可扩展的对象等. 声明严格模式 可以通过在文件.程序或函数的开头添加 ...
- 【ASP.NET MVC 5】第27章 Web API与单页应用程序
注:<精通ASP.NET MVC 3框架>受到了出版社和广大读者的充分肯定,这让本人深感欣慰.目前该书的第4版不日即将出版,现在又已开始第5版的翻译,这里先贴出该书的最后一章译稿,仅供大家 ...
- 每日英语:Mistrust Between U.S., Malaysia Strains Probe
Mistrust between U.S. and Malaysian air-accident investigators has hampered a multinational probe in ...
- LOB字段存放在指定表空间 清理CLOB字段及压缩CLOB空间
LOB字段存放在指定表空间 清理CLOB字段及压缩CLOB空间 把LOB字段的SEGMENT 存放在指定表空间.清理CLOB字段及压缩CLOB空间 1.创建LOB字段存放表空间:create ...
- VirtualBox不能为虚拟电脑 Ubuntu 打开一个新任务
今天在用Vbox中的Ubuntu系统准备测试Python代码时,Vbox报了一个错误:"不能为虚拟电脑 Ubuntu 打开一个新任务".因为之前用的时候还好好的,也不知道是不是最近 ...
- cmd实用命令
1.netstat 查看电脑端口状况 实际应用举例:查看某软件坚监听的电脑端口. 在任务管理器中选择列...,打开PID的显示.在这里查看某个应用程序的线程ID是多少.例如QQ:4904. 运行,cm ...
- ValidationExpression="http(s)?://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?" can not work
ValidationExpression="http(s)?://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?" can not work &quo ...
- win2008远程桌面会话数增加
1.[解决]由于没有远程桌面授权服务器可以提供许可证,远程回话被中断 你看到的这个文章来自于http://www.cnblogs.com/ayanmw 由于windows server 2008 R2 ...
- Apache2.4中开通HTTP基本认证
Apache2.4中开通HTTP基本认证,需要在Apache的配置文件中增加如下代码 WSGIPassAuthorization On 否则则无法认证
- Unity3D 中 Generic 动画导入设置和 Root Motion 之间的关系
2条评论 Unity3D 的 Mecanim 动画系统可以直接复用 3DS MAX 中制作的动画文件中的位移,这个就是通过 applyRootMotion 来达成的,我们只需要在使用 Animator ...