前言
SpringMVC的请求处理器 会调用Service层,Service层调用数据访问层,最终页面上要得到的结果一般就是数据访问层的结果。那么SpringMVC是如何将数据模型加入到请求域和session域的呢?
一. 处理数据模型之ModelAndView
请求处理器方法的返回结果如果为ModelAndView,则既包含数据模型,也包含视图信息。ModelAndView类中添加数据模型的方法:
- addObject(String,Object) 添加单个数据,第一个参数表示数据模型名称,第二个参数就是数据模型
- addAllObjects(Map<String,Object>) 表示将Map键值对中的所有的数据加入数据模型,map的key就是数据模型名称,map的value就是数据模型
ModelAndView中设置视图的方法:
- setView(View view) 设置视图
- setViewName(String viewName) 设置视图名称
/**
* ModelAndView 中的数据模型 会被SpringMVC自动的放入到请求域
* ModelAndView 中的视图信息会被SpringMVC的视图解析器解析,并进行转发
* @return
*/
@RequestMapping("testModelAndView")
public ModelAndView testModelAndView() {
ModelAndView modelAndView = new ModelAndView();//实例化ModelAndView对象
//添加数据模型
modelAndView.addObject("company","阿里爸爸");
modelAndView.addObject("address","杭州");
//设置视图信息
modelAndView.setViewName(SUCCESS);
return modelAndView;
}
二. 处理数据模型之Map,Model,ModelMap
不管目标方法返回的是字符串,还是ModelAndView,SpringMVC最终都会以ModelAndView的方法去接收目标方法的返回结果。对于Map,Model,ModelMap都是用来处理数据模型的,可以将Map,Model,ModelMap作为处理器目标方法的入参,Map,Model,ModelMap中的数据最终都会被SpringMVC处理,并且放入到ModelAndVIew的数据模型中去。
/**
* Map作为目标方法的入参,Map中的数据会自动的被SpringMVC放到ModelAndView的数据模型中,最终加入到请求域
* @param map
* @return
*/
@RequestMapping("testMap")
public String testMap(Map<String,Object> map) {
map.put("classname", "17级XXX班");
map.put("classleader", "波波老师");
return SUCCESS;
}
以上是Map的处理,那么Model,ModelMap与Map又是什么关系呢?
通过类的关系,发现Model,ModelMap与Map是同类的,所以处理也是相同的。代码如下:
/**
* ModelMap作为目标方法的入参,ModelMap中的数据会自动的被SpringMVC放到ModelAndView的数据模型中,最终加入到请求域
* @param map
* @return
*/
@RequestMapping("testModelMap")
public String testModel(ModelMap modelMap) {
modelMap.addAttribute("java", "Java框架学习");
modelMap.addAttribute("mysql", "MySQL是一个关系型数据库");
return SUCCESS;
}
/**
* Model作为目标方法的入参,Model中的数据会自动的被SpringMVC放到ModelAndView的数据模型中,最终加入到请求域
* @param map
* @return
*/
@RequestMapping("testModel")
public String testModel(Model model) {
model.addAttribute("name", "中华传统文化园");
model.addAttribute("tel", "0792-1234556");
return SUCCESS;
}
三. 处理数据模型之@SessionAttributes注解
在我们的登录程序中,用户登录成功之后,需要将用户的信息放入到session域,那么我们上面使用ModelAndView,Model,ModelMap,Map都是将信息加入到请求域中,怎么讲数据模型加入到session域呢?SpringMVC提供了@SessionAttribute注解,这个注解可以将数据模型加入到session域中。
- @SessionAttribute注解是注解在处理器类上
- @SessionAttribute注解语法有两种:
- @SessionAttribute(value={“name1”,”name2”}):表示如果有数据模型名称为name1和name2 ,并且将此数据模型加入到请求域,那么同时会加入到session域
- @SessionAttribute(types={String.class,User.class}):表示如果有数据模型类型为String和User,并且将此数据模型加入到请求域,那么同时会加入到session域
@SessionAttributes(value= {"java","mysql"},types= {Student.class}) //表示数据模型名称为Java和mysql 放入到请求域的时候,同时放入到session域
//@SessionAttributes(value= {"java","mysql"}) //表示数据模型名称为Java和mysql 放入到请求域的时候,同时放入到session域
//@SessionAttributes(types= {Student.class}) //表示数据模型名称类型为Student 放入到请求域的时候,同时放入到session域
@Controller //表示是一个控制器
@RequestMapping("hello")
public class HelloWorld {
四. 处理数据模型之@ModelAttribute注解
需求:在修改某个对象的时候,其中的一部分属性不能被修改,而部分属性则能被修改,我们能够想到的第一方案是使用隐藏域将不能修改的属性也进行传递,但是隐藏域存在的问题是什么?
- 如果不需要修改的字段是密码
- 如果不需要被修改的字段比较多
SpringMVC给我们提供了另外一种解决方案:
SpringMVC提供的解决方案是要修改的对象不是new出来的,而是从数据库中来,并且需要修改的字段只针对从数据库中来的对象进行修改。需要在方法上面使用@ModelAttribute注解,但是这个方法并不是普通的请求处理器的目标方法,也就是说这个方法没有@RequestMapping注解。
@ModelAttribute
public void testModelAttribute() {
System.out.println("ModelAttribute....");
}
发现所有的处理器的目标方法执行之前,都会执行@ModelAttribute注解的方法。
public static final String SUCCESS = "success";
@RequestMapping("testStudentUpdate")
public String testStudentUpdate(Student student) {
System.out.println(student);
return SUCCESS;
}
@ModelAttribute
public void testModelAttribute() {
System.out.println("ModelAttribute....");
}
以上程序就是模拟stuName不被修改,但是在请求处理器的目标方法中获取的Student对应的stuName属性为null,所以存在问题,这个时候@ModelAttribute注解的方法就可以起作用了。
@RequestMapping("testStudentUpdate")
public String testStudentUpdate(Student student) {
System.out.println(student);
return SUCCESS;
}
@ModelAttribute
public void testModelAttribute(Map<String,Object> map) {
System.out.println("ModelAttribute....");
//1 从数据库中查询 需要修改的Student
Student student = new Student("张三丰","male",130);
map.put("student", student);
}
以上就实现了需求所描述的功能。
五. @ModelAttribute注解修饰目标方法POJO类型入参
现在在@ModelAttribute注解的方法中加入到请求域的数据Student对象,名称是student,如果名称改成其他的,又会有什么样的效果呢?
@RequestMapping("testStudentUpdate")
public String testStudentUpdate(Student stu) {
System.out.println(stu);
return SUCCESS;
}
@ModelAttribute
public void testModelAttribute(Map<String,Object> map) {
System.out.println("ModelAttribute....");
//1 从数据库中查询 需要修改的Student
Student student = new Student("张三丰","male",130);
map.put("stu", student);
}
发现在目标方法中入参的student的stuName属性又不在了,这是为什么?因为请求处理器目标方法的入参,默认的会找@ModelAttribute放入到请求域的数据,默认会通过类名首字母小写的名称进行查找,如果找不到,达不到需求所述的功能,如果找到,则达到需求所述功能。在找不到的情况下,我们通过注解,让目标方法的入参不再以默认的名称查找,而是通过注解以指定的名称查找,这个时候需要在目标方法的入参前使用@ModelAttribute注解指定查找的属性模型名称。
/**
* public String testStudentUpdate(Student stu) : 默认查找@ModelAttribute注解方法放入到请求域中的数据模型,通过默认student名称查找
* public String testStudentUpdate(@ModelAttribute("stu")Student stu) :默认查找@ModelAttribute注解方法放入到请求域中的数据模型,通过指定stu名称查找
* @param stu
* @return
*/
@RequestMapping("testStudentUpdate")
public String testStudentUpdate(@ModelAttribute("stu")Student stu) {
System.out.println(stu);
return SUCCESS;
}
@ModelAttribute
public void testModelAttribute(Map<String,Object> map) {
System.out.println("ModelAttribute....");
//1 从数据库中查询 需要修改的Student
Student student = new Student("张三丰","male",130);
map.put("stu", student);
六. @SessionAttribute引发的异常
如果有@SessionAttribute注解,而且存在@ModelAttribute注解的方法,而且处理器的目标方法的入参类型存在于@SessionAttribute注解的types属性中,而且@ModelAttribute注解的方法没有将此类型的对象加入到请求域中,那么会报异常。我们得分析流程:
- 执行@ModelAttribute注解的方法
- 执行目标方法,目标方法入参存在Student
- 目标方法到请求域中寻找@ModelAttribute注解方法加入到请求域中的Student
- 寻找不到,则看类中是否存在@SessionAttribute,并且注解的types中有Student类型,如果有Student类型,则到Session域中寻找student,寻找不到,报错
小结
对于源码来说,四种处理数据模型的操作最后都会返回一个ModelAndView;
开发中对于四种处理数据模型的操作,我们最常用的时候后三种,根据不同的业务来使用这三种数据模型的操作:
- Map:我们在开发中最常用的一种
- @SessionAttributes注解一般用于登陆
- @ModelAttribute注解来说,一般在修改的时候都会使用