@SessionAttributes
若希望在多个请求之间公用某个模型属性数据,则可以在控制器类上标注一个@SessionAttributes,SpringMVC将在模型中对应的属性暂存到HttpSession中。@SessionAttributes 除了可以通过属性名指定需要放到会话中的属性外,还可以通过模型属性的对象类型指定哪些模型属性需要放到会话中。
首先介绍一下SpringMVC中的隐含模型
Spring MVC 在调用方法前会创建一个隐含的模型对象作为模型数据的存储容器。
如果方法的入参为 Map 或 Model 类型,Spring MVC 会将隐含模型的引用传递给这些入参。在方法体内,开发者可以通过这个入参对象访问到模型中的所有数据,也可以向模型中添加新的属性数据。通俗来说就是如果使用Map、Model、ModelMap设置一些参数,这些key-value其实是放进了SpringMVC的隐含模型中了,以前博客讲过Map、Model、ModelMap三者在处理器方法中的类型都是BindingAwareModelMap。
代码实例1:
/*
* 向session中放数据
* value:使用Model,Map,ModelMap,ModelAndView向页面传输数据,
* 只要放置的数据的key和value指定的key一样,则会在session中同样放置一份,
* value的值为key。
* value="msg"指定凡是key为msg的数据都在session中放置一份
*
* types:只要使用Model,Map,ModelMap,ModelAndView向页面放置的数据的类型是types指定的类型,
* 则会在session中放置一份
* types= {String.class}:凡是值为String类型的,都在session中放置一份
*/
@SessionAttributes(value= {"a","b","c","d"},types={String.class})
@Controller
public class TestSessionAndModelAttribute {
@RequestMapping("/testSession")
public String testSession(Model model) {
model.addAttribute("a", "model中放置了一个数据");
return "outData";
}
@RequestMapping("/testSession2")
public String testSession2(Map<String,Object> map) {
map.put("b", "map中放置了数据");
return "outData";
}
@RequestMapping("/testSession3")
public String testSession3(ModelMap modelMap) {
modelMap.addAttribute("c","ModelMap中放置数据");
return "outData";
}}
如上述代码,分别使用Map、Model、ModelMap设置了key为a,b,c的参数值,此时应为@SessionAttributes中指定了value的值为a,b,c,所以会在session域中放置一份,则前段页面中可以在session中取出指定的key的值。但是指定的key为d的,由于没有放置key为d的参数,则在session中无法取出。上述代码中的注释中也有对@SessionAttributes的说明。
重点介绍一下@ModelAttribute注解。
@ModelAttribute注解可以使用在两个地方,一个是处理器类的适配器方法的头上,一个是方法的参数的前面。
1、当注解在处理器类的适配器方法的头上时,则这个方法会先适配器方法执行,当这个方法执行完以后,才会到匹配请求的方法那执行。
实例代码2:
@RequestMapping("/testModelAttribute")
public String testModelAttribute(Book book) {
System.out.println(book);
return "success";
}
@ModelAttribute
public void testModelAttribute(Map<String,Object> map) {
System.out.println("testModelAttribute...");
Book book = new Book("awd",213, "dwad",123);
map.put("bookinfor", book);
}
结果如下:
可以看出来被@ModelAttribute标注的方法会先执行。
2、放在参数的前面
首先先上代码
实例代码3:
@RequestMapping("/testModelAttribute")
public String testModelAttribute(@ModelAttribute("bookinfor")Book book) {
System.out.println(book);
return "success";
}
@ModelAttribute
public void testModelAttribute(Map<String,Object> map) {
System.out.println("testModelAttribute...");
Book book = new Book("awd",213, "dwad",123);
map.put("bookinfor", book);
}
结果如下图:
通过实例代码我们可以看出,如果我们在被@ModelAttribute注解标注的方法中向SpringMVC的隐形模型中放置一个Book类的实例,我们在适配器方法中的参数前使用@ModelAttribute注解,并通过value属性指定隐含模型中的那个值将被赋值给参数。上述代码中将隐含模型中的Book的实例赋值给了方法中的book参数,打印出的book,其属性的值和在@ModelAttribute标注的方法中设置的一样。
如果使用@ModelAttribute标注在方法参数的前面是,不指定value,如何给方法参数赋值?
解:当没有指定value的值时,就取被标注的方法参数的类型名首字母小写作为在隐含模型中匹配的key,如果有则将其值赋值给方法的参数。
实例代码4:
@RequestMapping("/testModelAttribute")
public String testModelAttribute(Book book) {
System.out.println(book);
return "success";
}
@ModelAttribute
public void testModelAttribute(Map<String,Object> map) {
System.out.println("testModelAttribute...");
Book book = new Book("awd",213, "dwad",123);
map.put("book", book);
}
结果如下:
值得注意的是,@ModelAttribute标注的参数大多是自定义参数
SpringMVC确认方法的每个参数值的流程大致如下图所说:
当自定义类型参数被@ModelAttribute标注是,会从隐形模型中取出以@ModelAttribute的value值作为key的值赋值给方法的参数,若@ModelAttribute的value值是空串(即没有配置@ModelAttribute的value属性),则会将当前用@ModelAttribute修饰的方法的Bean参数或直接用@ModelAttribute修饰的Bean 的首字母小写Bean的名字作为attrName。这也是实例代码4为什么也可以给参数赋值的原因。
同时使用@ModelAttribute和@SessionAttributes可能会出现错误。
代码示例如下:
@SessionAttributes(value= {"d"})
@Controller
public class TestSessionAndModelAttribute {
@RequestMapping("/testModelAttribute2")
public String testModelAttribute2(@ModelAttribute("d")Book book) {
return "success";
}
@ModelAttribute
public void testModelAttribute(Map<String,Object> map) {
System.out.println("testModelAttribute...");
Book book = new Book("awd",213, "dwad",123);
map.put("book", book);
}
}
页面中出现的错误如下:
解释:当使用@ModelAttribute标注自定义类型的参数Book并指定value为d时,SpringMVC会首先在隐含模型中找与value值对应的值,如果则看@SessionAttributes是否声明了session中有这个与value对应的值,即是否有@SessionAttributes(value=“d”),如果有则去session中拿,拿不到则报错,拿到了则赋值。上述实例代码中只有声明,但并没有真的往session中放置d对应的值,所以会报错。
所以向session域中添加数据时,并不推荐使用@SessionAttributes注解,因为可能出现错误,可以使用原生API向session域中赋值。