重要的结论要先说出来,千万不要用@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,我这几天接手他的代码琢磨来琢磨去头发都要琢磨没了*滑稽
看了你这篇顿时明白多了