SpringMVC提供了以下几种途径输出模型数据:
ModelAndView:处理方法返回值类型为ModelAndView时,方法体即可通过该对象添加模型数据。
Map及Model:入参为org.springframework.ui.Model、org.springframework.ui.ModelMap或java.util.map时,处理方法返回时,Map中的数据会自动添加到模型中。
@SessionAttributes:将模型中的某个属性暂存到HttpSession中,以便多个请求之间可以共享这个属性。
@ModelAttribute:方法入参标注该注解后,入参的对象将会放到数据模型中。
ModelAndView:
控制器处理方法的返回值如果为ModelAndView,则既包含视图信息,也包含模型数据信息。
/**
* 目标方法返回类型可以是ModelAndView
* 其中可以包含视图和模型信息
* Spring MVC会把ModelAndView中model的数据放入到request域对象中
* @return
*/
@RequestMapping("testModelAndView")
public ModelAndView testModelAndView() {
ModelAndView model = new ModelAndView("success");
model.addObject("time", new Date());
return model;
}
在jsp页面使用${requestScope.time }获取
Map及Model:
SpringMVC在内部使用了一个org.springframework.ui.Model接口存储模型数据。
具体步骤
SpringMVC在调用方法前会创建一个隐含的模型对象作为模型数据的存储容器。如果方法的入参为Map或Model类型,Spring MVC会将隐含模型的引用传递给这些入参。在方法体内,开发者可以通过这个入参对象访问到模型中的所有数据,也可以向模型中添加新的属性数据。
/**
* 目标方法可以传入map,也可以是Model或者ModelMap类型的参数
* @param maps
* @return
*/
@RequestMapping("testMap")
public String testMap(Map<String,Object> maps) {
maps.put("names", Arrays.asList("zhangsan","lisi","wangwu"));
return SUCCESS;
}
@SessionAttributes(该注解只能放在类上,不能放在方法上)
如果希望在多个请求之间共用某个模型属性数据,则可以在控制器类上标注一个@SessionAttributes,SpringMVC将在模型中对应的属性暂存到HttpSession中。@SessionAttributes除了可以通过属性名指定需要放到绘画中的属性外(通过注解中的value属性值设置),还可以通过模型属性的对象类型指定哪些模型属性需要放到会话中(通过注解中types属性值设置)。
@SessionAttributes(types = User.class)会将隐含模型中所有类型为User.class的属性添加到会话中。
@SessionAttributes(value= {“user1”,“user2”},types = User.class)
//在控制器上添加@SessionAttributes注解,下面注解表示模型中的names属性
//不仅会存放在Request作用域中,也会存放在Session中,User类型的属性也是一样
@SessionAttributes(value= {"names"},types = User.class)
//必须是模型数据中存在的属性。
@RequestMapping("testMap")
public String testMap(Map<String,Object> maps) {
User user = new User();
user.setUsername("zhangsan");
user.setAge("20");
maps.put("testUser", user);
maps.put("names",Arrays.asList("wangwu","lisi"));
return SUCCESS;
}
jsp页面中访问
request names,${requestScope.names }
<br>
request User,${requestScope.testUser }
<br>
<br>
session names,${sessionScope.names }
<br>
session User,${sessionScope.testUser }
@ModelAttribute
有ModelAttribute标记的方法,会在每个目标方法执行之前被MVC调用,比如控制器类中定义了多个方法,且被@RequestMapping注解修饰,那么当访问方法时,被modelAttribute标记的方法会先执行。
//在修改对象时,有些属性不想修改,例如对象的创建时间,密码等,但是如果界面上没有传入值,
//那么在后台赋值的时候对应的属性会是null,如果执行更新操作,那么对应属性将会修改为null,所以可以使用
//@ModelAttribute标注方法,让其从数据库中先查找数据,然后手动赋值界面传入的,再放入模型中,以供调用方法使用
private static final String SUCCESS="success_test";
@ModelAttribute
private void getUser(@RequestParam(value="id",required = false)String id,Map<String,User> map) {
if(id.equals("1")) {
User user = new User();
user.setUsername("test1");
user.setAge("20");
user.setPassword("123456");
map.put("user", user);
System.out.println("模拟从数据库中查找User :"+user.toString());
}
}
@RequestMapping("/testModelAttribute")
public String testModelAttribute(User user) {
System.out.println("修改User : "+ user);
return SUCCESS;
}
上述代码运行流程:
执行@ModelAttribute注解修饰的方法:从数据库中取出对象,把对象放入map中,键为user
SpringMVC从map中取出user对象,并把表单的请求参数赋值给该user对象的对应属性
SpringMVC把上述对象传入目标方法的参数。
注意:在@ModelAttribute修饰的方法中,放入到Map时的键需要和目标方法入参类型的第一个字母小写的字符串一致,如上述代码map.put(“user”, user),而目标方法的入参类型为User。除了默认使用默认设置,也可以在目标方法参数中使用@ModelAttribute注解来映射,如下:map.put(“user111”, user),key为user111,在目标方法中使用@ModelAttribute(“user111”)来映射。
@ModelAttribute
private void getUser(@RequestParam(value="id",required = false)String id,Map<String,User> map) {
if(id.equals("1")) {
User user = new User();
user.setUsername("test1");
user.setAge("20");
user.setPassword("123456");
map.put("user111", user);
System.out.println("模拟从数据库中查找User :"+user.toString());
}
}
@RequestMapping("/testModelAttribute")
public String testModelAttribute(@ModelAttribute("user111")User user) {
System.out.println("修改User : "+ user);
return SUCCESS;
}
SpringMVC 确定目标方法POJO类型入参过程
确定一个Key
若目标方法的POJO类型参数没有使用@ModelAttribute作为修饰,则key为POJO类名第一个字母的小写的名字。
若使用@ModelAttribute来修饰,则key为@ModelAttribute注解的value属性值。
在implicitModel中查找key对应的对象,若存在,则作为参数入参传入。(若使用了@ModelAttribute标记的方法中在Map中保存过,且key是上述描述中确定的key一致,就会获取到)
若implicitModel中不存在key对应的对象,则检查当前Handler是否使用了@SessionAttrbutes注解修饰,若使用了该注解,且@SessionAttributes注解的value属性值中包含了key,则会从HttpSession中来获取key所对应的value值,若存在则直接传入到目标方法的入参中,若不存在则抛出异常。
若Handler没有标识@SessionAttributes注解或@SessionAttributes注解的value值中不包含key,则会通过反射来创建POJO类型的参数,传入为目标方法的参数。
SpringMVC会把key 和POJO类型对象保存在implicitModel中,今儿会保存到Request中。
源码中:
1、调用@ModelAttribute注解修饰的方法,实际上把ModelAttribute方法中Map中的数据放在了implicitModel中。
2、解析请求处理器的目标参数,实际上该目标参数来自于WebDataBinder对象的target属性。
创建WebDataBinder对象;
确定ObjectName属性(若传入的attrName属性值为"",则objectName为类名第一个字母小写,注意:attrName,若目标方法的POJO属性使用了@ModelAttribute来修饰,则attrName 的值几位@ModelAttribute的value属性值,即上面代码所示);
确定target属性(在implicitModel中查找attrName对应的属性值,若不存在,则验证当前Handler是否使用率@SessionAttributes进行修饰,若使用了,则尝试从Session中获取attrName所对应的属性值。若Session中没有对应的属性值,则抛出异常,若Handler没有使用@SessionAttributes进行修饰,或@SessionAttributes中没有使用value值指定的key和attrName相匹配,则通过反射创建POJO类对象)
3、SpringMVC把表单的请求参数赋给了WebDataBinder的target对应的属性。
4、SpringMVC会把WebDataBinder的attrName和target给到implicitModel,从而传到Request域中。
5、把WebDataBinder的target作为参数传递给目标方法入参。
有@ModelAttribute标记的方法,会在每个目标方法执行之前被MVC调用。
@ModelAttribute注解也可以来修饰目标方法POJO类型的入参,其value属性值有如下作用
SpringMVC会使用value属性值在implicitModel中查找对应的对象,若存在则会直接传入到目标方法的入参中。
SpringMVC会把value属性值作为key,POJO类型的对象作为value存入到Request中。如下目标方法中,会将user111作为key,User对象作为value存入Request域中,如果没有设置,则是默认为类名第一个字母小写的名字作为key,即user作为key.
@RequestMapping("/testModelAttribute")
public String testModelAttribute(@ModelAttribute("user111")User user) {
System.out.println("修改User : "+ user);
return SUCCESS;
}