在了解这三者之前,需要知道一点:SpringMVC在调用方法前会创建一个隐含的数据模型,作为模型数据的存储容器, 成为”隐含模型”。
也就是说在每一次的前后台请求的时候会随带这一个背包,不管你用没有,这个背包确实是存在的,用来盛放我们请求交互传递的值;关于这一点,spring里面有一个注解:
@ModelAttribute :被该注解修饰的方法,会在每一次请求时优先执行,用于接收前台jsp页面传入的参数
例子:
@Controller
public class User1Controller{
private static final Log logger = LogFactory.getLog(User1Controller.class);
// @ModelAttribute修饰的方法会先于login调用,该方法用于接收前台jsp页面传入的参数
@ModelAttribute
public void userModel(String loginname,String password,
Model model){
logger.info("userModel");
// 创建User对象存储jsp页面传入的参数
User2 user = new User2();
user.setLoginname(loginname);
user.setPassword(password);
// 将User对象添加到Model当中
model.addAttribute("user", user);
}
@RequestMapping(value="/login1")
public String login(Model model){
logger.info("login");
// 从Model当中取出之前存入的名为user的对象
User2 user = (User2) model.asMap().get("user");
System.out.println(user);
// 设置user对象的username属性
user.setUsername("测试");
return "result1";
}
在前端向后台请求时,spring会自动创建Model与ModelMap实例,我们只需拿来使用即可;
无论是Mode还是ModelMap底层都是使用BindingAwareModelMap,所以两者基本没什么区别;
我们可以简单看一下两者区别:
①Model
Model是一个接口,它的实现类为ExtendedModelMap,继承ModelMap类
public class ExtendedModelMap extends ModelMap implements Model
②ModelMap
ModelMap继承LinkedHashMap,spring框架自动创建实例并作为controller的入参,用户无需自己创建
public class ModelMap extends LinkedHashMap
而是对于ModelAndView顾名思义,ModelAndView指模型和视图的集合,既包含模型 又包含视图;ModelAndView的实例是开发者自己手动创建的,这也是和ModelMap主要不同点之一;ModelAndView其实就是两个作用,一个是指定返回页面,另一个是在返回页面的同时添加属性; 它的源码:
public class ModelAndView {
/** View instance or view name String */
private Object view //该属性用来存储返回的视图信息
/** Model Map */
private ModelMap model;//该属性用来存储处理后的结果数据
/**
* Indicates whether or not this instance has been cleared with a call to {@link #clear()}.
*/
private boolean cleared = false;
/**
* Default constructor for bean-style usage: populating bean
* properties instead of passing in constructor arguments.
* @see #setView(View)
* @see #setViewName(String)
*/
public ModelAndView() {
}
/**
* Convenient constructor when there is no model data to expose.
* Can also be used in conjunction with addObject
.
* @param viewName name of the View to render, to be resolved
* by the DispatcherServlet's ViewResolver
* @see #addObject
*/
public ModelAndView(String viewName) {
this.view = viewName;
}
/**
* Convenient constructor when there is no model data to expose.
* Can also be used in conjunction with addObject
.
* @param view View object to render
* @see #addObject
*/
public ModelAndView(View view) {
this.view = view;
}
/**
* Creates new ModelAndView given a view name and a model.
* @param viewName name of the View to render, to be resolved
* by the DispatcherServlet's ViewResolver
* @param model Map of model names (Strings) to model objects
* (Objects). Model entries may not be null
, but the
* model Map may be null
if there is no model data.
*/
public ModelAndView(String viewName, Map model) {
this.view = viewName;
if (model != null) {
getModelMap().addAllAttributes(model);
}
}
/**
* Creates new ModelAndView given a View object and a model.
* Note: the supplied model data is copied into the internal
* storage of this class. You should not consider to modify the supplied
* Map after supplying it to this class
* @param view View object to render
* @param model Map of model names (Strings) to model objects
* (Objects). Model entries may not be null
, but the
* model Map may be null
if there is no model data.
*/
public ModelAndView(View view, Map model) {
this.view = view;
if (model != null) {
getModelMap().addAllAttributes(model);
}
}
/**
* Convenient constructor to take a single model object.
* @param viewName name of the View to render, to be resolved
* by the DispatcherServlet's ViewResolver
* @param modelName name of the single entry in the model
* @param modelObject the single model object
*/
public ModelAndView(String viewName, String modelName, Object modelObject) {
this.view = viewName;
addObject(modelName, modelObject);
}
/**
* Convenient constructor to take a single model object.
* @param view View object to render
* @param modelName name of the single entry in the model
* @param modelObject the single model object
*/
public ModelAndView(View view, String modelName, Object modelObject) {
this.view = view;
addObject(modelName, modelObject);
}
/**
* Set a view name for this ModelAndView, to be resolved by the
* DispatcherServlet via a ViewResolver. Will override any
* pre-existing view name or View.
*/
public void setViewName(String viewName) {
this.view = viewName;
}
/**
* Return the view name to be resolved by the DispatcherServlet
* via a ViewResolver, or null
if we are using a View object.
*/
public String getViewName() {
return (this.view instanceof String ? (String) this.view : null);
}
/**
* Set a View object for this ModelAndView. Will override any
* pre-existing view name or View.
*/
public void setView(View view) {
this.view = view;
}
/**
* Return the View object, or null
if we are using a view name
* to be resolved by the DispatcherServlet via a ViewResolver.
*/
public View getView() {
return (this.view instanceof View ? (View) this.view : null);
}
/**
* Indicate whether or not this ModelAndView
has a view, either
* as a view name or as a direct {@link View} instance.
*/
public boolean hasView() {
return (this.view != null);
}
/**
* Return whether we use a view reference, i.e. true
* if the view has been specified via a name to be resolved by the
* DispatcherServlet via a ViewResolver.
*/
public boolean isReference() {
return (this.view instanceof String);
}
/**
* Return the model map. May return null
.
* Called by DispatcherServlet for evaluation of the model.
*/
protected Map getModelInternal() {
return this.model;
}
/**
* Return the underlying ModelMap
instance (never null
).
*/
public ModelMap getModelMap() {
if (this.model == null) {
this.model = new ModelMap();
}
return this.model;
}
/**
* Return the model map. Never returns null
.
* To be called by application code for modifying the model.
*/
public Map getModel() {
return getModelMap();
}
/**
* Add an attribute to the model.
* @param attributeName name of the object to add to the model
* @param attributeValue object to add to the model (never null
)
* @see ModelMap#addAttribute(String, Object)
* @see #getModelMap()
*/
public ModelAndView addObject(String attributeName, Object attributeValue) {
getModelMap().addAttribute(attributeName, attributeValue);
return this;
}
/**
* Add an attribute to the model using parameter name generation.
* @param attributeValue the object to add to the model (never null
)
* @see ModelMap#addAttribute(Object)
* @see #getModelMap()
*/
public ModelAndView addObject(Object attributeValue) {
getModelMap().addAttribute(attributeValue);
return this;
}
/**
* Add all attributes contained in the provided Map to the model.
* @param modelMap a Map of attributeName -> attributeValue pairs
* @see ModelMap#addAllAttributes(Map)
* @see #getModelMap()
*/
public ModelAndView addAllObjects(Map modelMap) {
getModelMap().addAllAttributes(modelMap);
return this;
}
/**
* Clear the state of this ModelAndView object.
* The object will be empty afterwards.
* Can be used to suppress rendering of a given ModelAndView object
* in the postHandle
method of a HandlerInterceptor.
* @see #isEmpty()
* @see HandlerInterceptor#postHandle
*/
public void clear() {
this.view = null;
this.model = null;
this.cleared = true;
}
/**
* Return whether this ModelAndView object is empty,
* i.e. whether it does not hold any view and does not contain a model.
*/
public boolean isEmpty() {
return (this.view == null && CollectionUtils.isEmpty(this.model));
}
/**
* Return whether this ModelAndView object is empty as a result of a call to {@link #clear}
* i.e. whether it does not hold any view and does not contain a model.
* Returns false
if any additional state was added to the instance
* after the call to {@link #clear}.
* @see #clear()
*/
public boolean wasCleared() {
return (this.cleared && isEmpty());
}
/**
* Return diagnostic information about this model and view.
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder("ModelAndView: ");
if (isReference()) {
sb.append("reference to view with name '").append(this.view).append("'");
}
else {
sb.append("materialized View is [").append(this.view).append(']');
}
sb.append("; model is ").append(this.model);
return sb.toString();
}
a 它有很多构造方法,对应会有很多使用方法:
例子:
(1)当你只有一个模型属性要返回时,可以在构造器中指定该属性来构造ModelAndView对象:
package com.apress.springrecipes.court.web;
...
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;
public class WelcomeController extends AbstractController{
public ModelAndView handleRequestInternal(HttpServletRequest request,
HttpServletResponse response)throws Exception{
Date today = new Date();
return new ModelAndView("welcome","today",today);
}
}
(2)如果有不止一个属性要返回,可以先将它们传递到一个Map中再来构造ModelAndView对象。
package com.apress.springrecipes.court.web;
...
import org.springframework.web.servlet.ModelAndView;
import org. springframework.web.servlet.mvc.AbstractController;
public class ReservationQueryController extends AbstractController{
...
public ModelAndView handleRequestInternal(HttpServletRequest request,
HttpServletResponse response)throws Exception{
...
Map model = new HashMap();
if(courtName != null){
model.put("courtName",courtName);
model.put("reservations",reservationService.query(courtName));
}
return new ModelAndView("reservationQuery",model);
}
}
当然也可以使用spring提供的Model或者ModelMap,这是java.util.Map实现;
package com.apress.springrecipes.court.web;
...
import org.springframework.ui.ModelMap;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;
public class ReservationQueryController extends AbstractController{
...
public ModelAndView handleRequestInternal(HttpServletRequest request,
HttpServletResponse response)throws Exception{
...
ModelMap model = new ModelMap();
if(courtName != null){
model.addAttribute("courtName",courtName);
model.addAttribute("reservations",reservationService.query(courtName));
}
return new ModelAndView("reservationQuery",model);
}
}
在页面上可以通过el变量方式${key}或者bboss的一系列数据展示标签获取并展示modelmap中的数据;上面主要讲了ModelAndView的使用,其实Model与ModelMap使用方法都是一致;
下面我通过一个下例子展示一下:
在spring项目中,在配置文件中配置好 视图解析器:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
bean>
在后台写:
@RequestMapping("/test1")
public ModelAndView test1(ModelMap mm ,HttpServletRequest request){
ModelAndView mv = new ModelAndView("mytest");
mv.addObject("key1","123");
mm.addAttribute("key1","1234");
return mv;
}//前端为 123 123
@RequestMapping("/test2")
public String test2(ModelMap mm ,HttpServletRequest request){
mm.addAttribute("key1", "1234");
return "mytest";
}//前端为 1234 1234
@RequestMapping("/test3")
public String test3(Model mm ,HttpServletRequest request){
mm.addAttribute("key1", "12345");
return "mytest";
}
@RequestMapping("/test4")
public String test4(Model mm ,HttpServletRequest request){
mm.addAttribute("key1", "12345");
request.setAttribute("key1", "123456");
return "mytest";
}//前端为 12345 12345
@RequestMapping("/test5")
public String test5(Model mm ,ModelMap mmp,HttpServletRequest request){
request.setAttribute("key1", "123456");
mm.addAttribute("key1", "12345");
mmp.addAttribute("key1", "1234567");
return "mytest";
}//前端为 1234567 1234567
对应前端:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title heretitle>
<%
String test = String.valueOf(request.getAttribute("key1"));
%>
head>
<body>
<%=test %>
${key1 }
body>
html>
从上面测试可以看到Model,ModelMap与ModelAndView的具体使用;同时可以看出Model与ModelMap的值是能够替换的;并且两者赋值后作用比request.setAttribute()要大;这点在使用时需要注意;具体为啥,估计是最终解析时还是放在request上,最终还是替换了;我们知道
request.setAttribute("key1", "123456");
request.setAttribute("key1", "123457");
这样的代码最终得到的是“123457”;具体原因这里不看了,先学会熟练使用;
终上所述,我们知道了Model与ModelMap其实都是实现了hashMap,并且用法都是一样的;两者都是spring在请求时自动生成,拿来用便可;ModelAndView就是在两者的基础上可以指定返回页面;
赋值能力 ModelAndView > Model/ModelMap>request ;
附加: 转发与重定向:
a 原始servlet:
转发时可以将页面的资源传给另一个页面,使用相对路径!
重定向只是单纯的进行页面跳转。使用绝对路径
if(username.equals("123")&&password.equals("123")){
request.getRequestDispatcher("/success.html").forward(request, response);
}else{
//在sendRedict中url前必须加上当前web程序的路径名.....
response.sendRedirect(request.getContextPath()+"/fail.html");
}
springMVC中,默认转发,重定向的话使用如下:
@RequestMapping("filesUpload")
public String filesUpload(@RequestParam("files") MultipartFile[] files) {
//判断file数组不能为空并且长度大于0
if(files!=null&&files.length>0){
//循环获取file数组中得文件
for(int i = 0;i//保存文件
saveFile(file);
}
}
// 重定向
return "redirect:/list.html";
}