重要的结论要先说出来,千万不要用@SessionAttributes来保存http session的属性!!!
在Spring MVC中,@ModelAttribute注解作用在方法或者方法参数上,表示将被注解的方法的返回值或者是被注解的参数作为Model的属性加入到Model中,然后Spring框架自会将这个Model传递给ViewResolver。Model的生命周期只有一个http请求的处理过程,请求处理完后,Model就销毁了。
@SessionAttributes(attrName)注解的意思就是将Model中的attrName属性作为session的属性保存下来。
但关键是它并不仅仅是将这个属性放到HttpSession中这么简单!它的做法大概可以理解为将Model中的被注解的attrName属性保存在一个SessionAttributesHandler中,在每个RequestMapping的方法执行后,这个SessionAttributesHandler都会将它自己管理的“属性”从Model中写入到真正的HttpSession;同样,在每个RequestMapping的方法执行前,SessionAttributesHandler会将HttpSession中的被@SessionAttributes注解的属性写入到新的Model中。注意!它的生命周期很别扭哦!
所以如果在方法中用HttpSession.removeAttribute()来删除被@SessionAttributes注解的属性,你会发现根本没有效果,因为方法执行结束后,它又被SessionAttributesHandler从Model中写回HttpSession了。可以用一个SessionStatus.setComplete()方法来让SessionAttributesHandler在方法结束后不接手工作,从而达到不写入HttpSession的目的,但这方法太鸡巴扯了我感觉。
所以,最重要的是,千万不要使用@SessionAttributes注解来管理session的属性!!!
下面贴一个简单的例子,自己试一下就知道了
/**
* 被@SessionAttributes注解的类,会有一个SessionAttributesHandler加入它的HandlerExecutionChain中
* 在每个RequestMapping的方法执行前,SessionAttributesHandler都会将testSession从HttpSession写入Model
* 在每个RequestMapping的方法执行后,SessionAttributesHandler都会将testSession从Model写入HttpSession
*/
@Controller
@RequestMapping("/test")
@SessionAttributes("testSession")
public class TestController {
// 每次调用Controller的其他方法前,会先调用这个被@ModelAttribute注解的方法
// 并将返回值作为model的属性保存在model中
@ModelAttribute("time")
public String testModelAttribute() {
System.out.println("被@ModelAttribute注解的方法会在该Controller的其他方法执行前先执行一次,"
+ "然后将返回值放入model");
return new Date().toString();
}
@RequestMapping("/page1")
public String page1(Model model, HttpSession session) {
// 现在,model中已经有了time属性
// 另外,由于在类上注解了@SessionAttributes("testSession"),所以方法被执行前,SessionAttributesHandler会去HttpSession中查找testSession属性写入model
model.addAttribute("testSession", "what the fuck!");
model.addAttribute("test1", "11111");
session.setAttribute("realSession", "我才是真的http session属性");
System.out.println("test1:");
System.out.println(model.toString());
printSession(session);
return "test";
// 由于在类上注解了@SessionAttributes("testSession"),所以方法结束后,SessionAttributesHandler会将model中的testSession属性写入model
}
@RequestMapping("/page2")
public String page2(Model model, HttpSession session) {
model.addAttribute("test2", "22222");
System.out.println("test2:");
System.out.println(model.toString());
printSession(session);
session.removeAttribute("testSession");
// 由于有@SessionAttributes("testSession")在,这个话根本就无法删除testSession属性
System.out.println("remove httpsession attr:");
printSession(session); // 现在打印出来的httpsession中确实没有testSession属性,但方法结束后又被SessionAttributesHandler写回去了
return "test";
}
@RequestMapping("/page3")
public String page3(Model model, HttpSession session, SessionStatus sessionStatus) {
model.addAttribute("test3", "33333");
System.out.println("test3:");
System.out.println(model.toString());
printSession(session);
session.removeAttribute("testSession");
sessionStatus.setComplete(); // 告诉SessionAttributesHandler你可以暂时下班了,本方法结束后不用处理@SessionAttributes了
System.out.println("remove httpsession attr:");
printSession(session);
return "test";
}
public void printSession(HttpSession session) {
StringBuffer sb = new StringBuffer("httpsession: [");
Enumeration<String> names = session.getAttributeNames();
while (names.hasMoreElements()) {
String name = (String) names.nextElement();
sb.append(name + "=" + session.getAttribute(name) + ", ");
}
sb.append("]");
System.out.println(sb.toString());
}
}
<html>
<head></head>
<body>
time:${time!} <br />
realSession: ${realSession!} <br />
testSession: ${testSession!} <br />
test1: ${test1!} <br />
test2:${test2!} <br />
test3:${test3!}
</body>
</html>
重要的事情说第三遍!前往不要用@SessionAttributes注解!要自己手动来操作HttpSession!
我一个同事在项目里就大笔一挥用了这个@SessionAttributes,我这几天接手他的代码琢磨来琢磨去头发都要琢磨没了*滑稽
看了你这篇顿时明白多了