处理模型数据
Spring MVC 提供了以下几种途径输出模型数据:
1.1.ModelAndView: 处理方法返回值类型为 ModelAndView时, 方法体即可通过该对象添加模型数据
/*
* 处理方法的返回值可以是ModelAndView类型,其中可包含视图和模型信息
* SpringMVC会把ModelAndView的model中的数据放到request域对象
*/
@RequestMapping("/ModelAndView")
public ModelAndView testModelAndView() {
ModelAndView modelAndView = new ModelAndView("hello");
modelAndView.addObject("time",new Date());
return modelAndView;
}
如果想要在JSP页面上获取对应的值可以通过如下的方法:
time now: ${requestScope.time}<br>
- @ModelAttribute: 方法入参标注该注解后, 入参的对象就会放到数据模型中
1.2.Handler 方法可以接受Map类型的参数
Map 及 Model: 入参为org.springframework.ui.Model、org.springframework.ui. ModelMap 或 java.uti.Map 时,处理方法返回时,Map 中的数据会自动添加到模型中。
Spring MVC 在调用方法前会创建一个隐含的模型对象作为模型数据的存储容器。
如果方法的入参为 Map 或 Model 类型,Spring MVC 会将隐含模型的引用传递给这些入参。在方法体内,开发者可以通过这个入参对象访问到模型中的所有数据,也可以向模型中添加新的属性数据。
/*
* 目标方法可以添加Map类型(也可以是Model/ModelMap类型)
*/
@RequestMapping("/testMap")
public String testMap(Map<String,Object> map) throws IOException {
map.put("names", Arrays.asList("Tom","Lisa","Allen"));
return "hello";
}
jsp页面中添加:
names list: ${requestScope.names}
1.3. 控制类上的@SessionAttributes
该注解只能标识在类上面
若希望在多个请求之间共用某个模型属性数据,则可以在控制器类上标注一个 @SessionAttributes, Spring MVC 将在模型中对应的属性暂存到 HttpSession 中。
/*
* @SessionAttributes 除了可以通过属性名指定需要放到会话中的属性外,(value)
* value={"user"}含义是把模型中属性为"user"的,(map中的键值)
* 还可以通过模型属性的对象类型指定哪些模型属性需要放到会话中(types)
* types指定的是map中的值类型符合就保存在session中
* (requestScope一样含有这些模型属性)
*/
@SessionAttributes(value={"user"},types ={String.class})
@RequestMapping("/TestRequest")
@Controller
public class HelloWorld {
@RequestMapping("/Session")
public String testSessionAttribute(Map<String,Object> map){
User user = new User("kaixin","123456",new Address("nanchang","AlongRiver"),19);
map.put("user",user);
map.put("school","hust");
return "hello";
}
}
request user:${requestScope.user}<br>
session user:${sessionScope.user}<br>
request school:${requestScope.school}<br>
session school:${sessionScope.school}<br>
localhost:8080/SpringMVC01/TestRequest/Session上显示
request user:User{username='kaixin', password='123456', address=Address{city='nanchang', street='AlongRiver'}, age=19}
session user:User{username='kaixin', password='123456', address=Address{city='nanchang', street='AlongRiver'}, age=19}
request school:hust
session school:hust
注:
传入数据到Map中时,一般情况下对于键的名称没有做特别的要求,但如果是在@ModelAttribute中,因为map的数据是作为参数传给处理器方法的,所以要求键的名称对应bean类的首字母小写。
1.4.@ModelAttribute
参考
https://blog.csdn.net/xiangwanpeng/article/details/53069533
讨论@ModelAttribute的作用及工作流程。即:有一个Graduate类,有id、studentname、email、password四个属性。现在要完成一个更新操作,但是其中有一项属性不能被修改,例如id,那么只能修改三项属性,password、studentname和email,所以从form表单传递的信息就只能有这三项,我们是通过@ModelAttribute注解标记方法来实现的:(其中数据库相关的操作仅采用模拟的方式)
1.4.1 注:
若目标方法的 POJO 类型的参数没有使用 @ModelAttribute 作为修饰, 则 key 为 POJO 类名第一个字母的小写;
若目标方法的 POJO 类型的参数使用了@ModelAttribute 来修饰, 则 key 为 @ModelAttribute 注解的 value 属性值。
1.4.2
SpringMVC确定目标方法POJO入参的全过程
在这个例子中,SpringMVC通过映射请求调用目标处理方法testModelAttribute方法之前,做了下面三件事情:
第1步: 执行 @ModelAttribute 注解修饰的方法: 从数据库中取出对象,把对象放入到了 Graduate中,键为:undergraduate。(见1.4.1)
第2步: SpringMVC 从 Map 中取出 Graduate 对象 graduate1, 并把表单的请求参数赋给 graduate1的对应属性。
第3步: SpringMVC 把上述对象作为参数传入目标方法testModelAttribute(Graduate id1)。
/*
* 源码分析:
* 1.调用@ModelAttribute修饰的方法,实际上@ModelAttribute修饰的方法中的Map的数据放到implicitModel
* (这是一个BindingAwareModelMap类型的对象,BindingAwareModelMap类型实现了Map接口)中
* 2.解析请求处理器的目标参数,实际上该目标参数来源于WebDataBinder的target属性
* 1)创建WebDataBinder对象
* ①确定Attribute属性:(确定key)
* 若传入的属性值为"",则ObjectName为类名首字母小写
* 注:若目标方法的POJO属性使用了@ModelAttribute修饰,则ObjectName值即为
* @ModelAttribute的value值
* ②确定target属性:(确定value)
* Ⅰ 在implicitModel中查找attributeName对应的属性值.找到ok;
* Ⅱ 若 implicitModel 中不存在 key 对应的对象,
* 则检查当前的控制器类是否被@SessionAttributes注解修饰 ,如果使用了@SessionAttributes注解修饰,
* 且@SessionAttributes注解的value属性值中包含了key,则尝试从HttpSession中获取key所对应的value值,
* 如果value值存在则获取到,若存在则直接传入到目标方法的入参中。
* 如果value值不存在则抛出异常。
* 如果没有使用@SessionAttributes注解修饰该控制器类,或者使用了,
* 但是@SessionAttributes注解中的value值不包含key,
* 则SpringMVC会通过反射来创建一个POJO类型的对象。
* 2)SpringMVC将表单参数赋给WebDataBinder对象的target属性
* 3)SpringMVC将WebDataBinder的Attribute 、target属性给implicitModel
* 4)把target作为参数传给目标方法的入参
*/
@ModelAttribute
public void getStudentInfo(@RequestParam(value = "id",required = false)Integer id,
Map<String,Object> map){
if(id!=null){
//模拟从数据库中获取对象
Graduate graduate1 =new Graduate("Tom","123456789",1,"hust@edu.cn");
System.out.println("从数据库中获取一个对象");
//把对象放入到map中
map.put("undergraduate", graduate1);//map中的键和目标方法的入参类型的名首字母小写
}
}
@RequestMapping("/testModelAttribute")
//在传入目标对象前,SpringMVC会从Map中取出Graduate对象,并把表单请求参数赋给对应的属性值
public String testModelAttribute(@ModelAttribute("undergraduate") Graduate id1){
System.out.println("修改:"+id1);
return "hello";
}
}
源码分析有一丢丢复杂,总之就是到处找key,找完key找对应的value,可以没有key(也就没有value),但不可以有key没value(抛异常)。