我们经常使用它SpringMVC在框架开发项目的过程中,我们会遇到各种问题,SpringMVC返回值问题毫无疑问,其中一个值得单独说出来。我们来谈谈这篇文章。ModelAndView处理SpringMVC返回值问题。
1.ModelAndView的定义
让我们来看看官方给出的信息ModelAndView的描述:
在 springmvc 框架中,ModelAndView 表示同时持有Model和View同时持有。Model和View完全不同。这一类使控制器返回一个单独的值,同时包括model和view。
同时,它也代表着处理器的返回model和view,会被 DispatcherServlet 解析。
如果View对象是通过字符串形式的 view name 获得的,可以被接受 ViewResolver 对象分析。或者可以直接指定 View 对象。model 是一个 Map 多个对象的键值对可用于类型。
2.为什么说通过 ModelAndView 来处理SpringMVC返回值问题
事实上,返回的类型有很多种,可以是 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) 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.Model 模型。
这里所说的 Model ,不仅仅是指 Model 具体类别。但是描述 SpringMVC 如何将数据存储在Model中?,在目标页面使用。
上面已经说过了,通过了 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 说明类型。
从上面可以看出,存储键值对类型 Map 或 Model。
下面的例子:
@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView() {
ModelAndView mv = new ModelAndView();
mv.setViewName("success");
mv.addObject("testKey", "testValue");
return mv;
}
success.jsp
通过测试,发现 Model 默认存储在中间的数据中 Request 域中。
SpringMVC通过ModelAndView解决了handler方法返回值的问题 handler 方法的返回值最终将转换为ModelAndView对象,还详细介绍了一下 Model 可以作为 handler 这里提到的方法的入参使用 Model 不仅仅是指 Model 这种类型也指实现 Model 或 Map 界面的类型。也了解了界面的类型。 Model 存放在哪里,存放在哪里。当然,后续会有更多的关系Spring MVC返回值的难点知识等着我们去学习和探索。如果你觉得自己对这些知识掌握不够透彻,可以看看这个网站Spring MVC视频课程,当然,我们也坚信这将是你的学习Spring MVC框架的转折点!