SpringMVC 使用 @ModelAttribute 和 @SessionAttributes 在不同的模型(model)和控制器之间共享数据。
@ModelAttribute 主要有两种使用方式,一种是标注在方法上,一种是标注在 Controller 方法参数上。
当 @ModelAttribute 标记在方法上的时候,该方法将在处理器方法执行之前执行,然后把返回的对象存放在 session 或模型属性中,属性名称可以使用 @ModelAttribute("attributeName") 在标记方法的时候指定,若未指定,则使用返回类型的类名称(首字母小写)作为属性名称。
关于 @ModelAttribute 标记在方法上时对应的属性是存放在 session 中还是存放在模型中,我们来做一个实验,看下面一段代码。
@Controller
@RequestMapping("/myTest")
public class MyController
{
@ModelAttribute("world")
public String getModel()
{
System.out.println("-------------world---------");
return "world" ;
}
@ModelAttribute("age")
public int getInteger()
{
System.out.println("-------------age---------------");
return 20;
}
@RequestMapping("sayHello")
public void sayHello(@ModelAttribute("world") String world, @ModelAttribute("age") int age, @ModelAttribute("user") User user, Writer writer, HttpSession session) throws IOException
{
writer.write( "Hello " + world + " ! My name is " + user.getUsername() + ", I am " + age + " years old.");
writer.write( "\r" );
Enumeration enume = session.getAttributeNames();
while (enume.hasMoreElements())
{
writer.write(enume.nextElement() + "\r" );
}
}
@ModelAttribute("user")
public User getUser()
{
System.out.println( "---------getUser-------------" );
return new User("Tom",20 );
}
}
当我们请求 /myTest/sayHello.do 的时候使用 @ModelAttribute 标记的方法会先执行,然后把它们返回的对象存放到模型中。最终访问到 sayHello 方法的时候,使用 @ModelAttribute 标记的方法参数都能被正确的注入值。执行结果如下所示:
Hello world! My name is Tom, I am 20 years old.
由执行结果我们可以看出来,此时 session 中没有包含任何属性,也就是说上面的那些对象都是存放在模型属性中,而不是存放在 session 属性中。那要如何才能存放在 session 属性中呢?这个时候我们先引入一个新的概念 @SessionAttributes 。我们在 MyController 类上加上 @SessionAttributes 属性标记哪些是需要存放到 session 中的。看下面的代码:
@Controller
@RequestMapping("/myTest")
@SessionAttributes(value={"userName" , "age"}, types={User.class})
public class MyController
{
@ModelAttribute("world")
public String getModel()
{
System.out.println("-------------world---------");
return "world" ;
}
@ModelAttribute("age")
public int getInteger()
{
System.out.println( "-------------age---------------" );
return 20;
}
@RequestMapping("sayHello")
public void sayHello(@ModelAttribute( "world") String world, @ModelAttribute("age") int age, @ModelAttribute("user") User user, Writer writer, HttpServletRequest request) throws IOException
{
writer.write( "Hello " + world + " ! My name is " + user.getUsername() + ", I am " + age + " years old.");
writer.write( "\r" );
HttpSession session = request.getSession();
Enumeration enume = session.getAttributeNames();
while (enume.hasMoreElements())
{
writer.write(enume.nextElement() + "\r" );
}
System.out.println(session);
}
@ModelAttribute("user")
public User getUser()
{
System.out.println( "---------getUser-------------" );
return new User("Tom",20 );
}
}
在上面代码中我们指定了属性为 userName 或 age 或者类型为 User 的都会放到 Session中,利用上面的代码当我们访问 /myTest/sayHello.do 的时候,结果如下:
Hello world! My name is Tom, I am 20 years old.
仍然没有打印出任何 session 属性,这是怎么回事呢?怎么定义了把模型中属性名为 age 的对象和类型为 User 的对象存到 session 中,而实际上没有加进去呢?难道我们错啦?我们当然没有错,只是在第一次访问 /myTest/sayHello.do 的时候 @SessionAttributes 定义了需要存放到 session 中的属性,而且这个模型中也有对应的属性,但是这个时候还没有加到 session 中,所以 session 中不会有任何属性,等处理器方法执行完成后 Spring 才会把模型中对应的属性添加到 session 中。所以当请求第二次的时候就会出现如下结果:
Hello world! My name is Tom, I am 20 years old.
user
userName
age
当 @ModelAttribute 标记在处理器方法参数上的时候,表示该参数的值将从模型或者 Session 中取对应名称的属性值,该名称可以通过 @ModelAttribute("attributeName") 来指定,若未指定,则使用参数类型的类名称(首字母小写)作为属性名称。
补充:与 Model 相关的常见面试题
SpringMvc中函数的返回值是什么?
答:返回值可以有很多类型有String、ModelAndView,但一般用String比较好。
SpringMvc用什么对象从后台向前台传递数据的?
答:通过ModelMap对象,可以在这个对象里面用put方法,把对象加到里面,前台就可以通过el表达式拿到。
SpringMvc中有个类把视图和数据都合并的一起的,叫什么?
答:ModelAndView。
怎么样把ModelMap里面的数据放入Session里面?
答:可以在类上面加上@SessionAttributes注解,里面包含的字符串就是要放入session里面的key。