@SessionAttributes 只能作用在类上,作用是将指定的Model中的键值对添加至session中,方便在下一次请求中使用。
简单示例
- 目标是通过 @SessionAttributes 注解将Model中attrName为 "user","age","name" 的值添加至 session 中
1 @Controller 2 @RequestMapping("/testSessionAttribute") 3 @SessionAttributes(value = {"user","age","name"}) 4 public class TestSessionAttributeController { 5 6 @ModelAttribute("user") 7 public User addUser(){ 8 User user = new User(); 9 user.setName("James"); 10 user.setAge(29); 11 return user; 12 } 13 14 @RequestMapping("/testHandler") 15 public String testHandler(Model model, String age, String name){ 16 model.addAttribute("age",age); 17 model.addAttribute("name",name); 18 System.out.println(age); 19 System.out.println(name); 20 return "result"; 21 } 22 }
对应的jsp代码如下:
1 <body> 2 ${sessionScope.user.age} 3 ${sessionScope.user.name} 4 <br/> 5 ${sessionScope.age} 6 ${sessionScope.name} 7 </body>
通过实例可以得出一下结论:
- 不论是利用@ModelAttribute添加至model的数据,还是手动添加至model的数据,均遵循 @SessionAttributes 的规则
清除@SessionAttributes向session中添加的值
如果需要清除通过@SessionAttribute添加至 session 中的数据,则需要在controller 的 handler method中添加 SessionStatus参数,在方法体中调用SessionStatus#setComplete。
需要注意的是,此时清除的只是该Controller通过@SessionAttribute添加至session的数据(当然,如果不同controller的@SessionAttribute拥有相同的值,则也会清除)
@Controller @RequestMapping("/testSessionAttribute") @SessionAttributes(value = {"user","age","name"}) public class TestSessionAttributeController { .......................... @RequestMapping("/removeSessionAttributes") public String removeSessionAttributes(SessionStatus sessionStatus){ sessionStatus.setComplete(); return "result"; } }
通过@ModelAttributes和@SessionAttributes共同添加至session中的数据,只会添加一次
在没用使用SessionStatus清除过之前,通过@ModelAttributes和@SessionAttributes共同添加至session中的数据并不会更新,如下例:
1 @Controller 2 @RequestMapping("/testSessionAttribute") 3 @SessionAttributes(value = {"user","age","name"}) 4 public class TestSessionAttributeController { 5 6 public static int age = 29; 7 @ModelAttribute("user") 8 public User addUser(){ 9 User user = new User(); 10 user.setName("James"); 11 user.setAge(age++); 12 return user; 13 } 14 15 @RequestMapping("/testHandler") 16 public String testHandler(Model model, String age, String name){ 17 model.addAttribute("age",age); 18 model.addAttribute("name",name); 19 System.out.println(age); 20 System.out.println(name); 21 return "result"; 22 } 23 24 }
1 <body> 2 ${sessionScope.user.age} 3 ${sessionScope.user.name} 4 <br/> 5 ${sessionScope.age} 6 ${sessionScope.name} 7 </body>
第一次请求:http://localhost:8080/testSessionAttribute/testHandler.action?name=James&age=29
结果:
29 James
29 James
第二次请求:http://localhost:8080/testSessionAttribute/testHandler.action?name=James&age=30
29 James
30 James
原因是,在org.springframework.web.bind.annotation.support.HandlerMethodInvoker#invokeHandlerMethod中进行了如下操作:
1 public final Object invokeHandlerMethod(Method handlerMethod, Object handler, 2 NativeWebRequest webRequest, ExtendedModelMap implicitModel) throws Exception { 3 4 Method handlerMethodToInvoke = BridgeMethodResolver.findBridgedMethod(handlerMethod); 5 try { 6 boolean debug = logger.isDebugEnabled(); 7 for (String attrName : this.methodResolver.getActualSessionAttributeNames()) { 8 Object attrValue = this.sessionAttributeStore.retrieveAttribute(webRequest, attrName); 9 if (attrValue != null) { 10 implicitModel.addAttribute(attrName, attrValue); 11 } 12 } 13 for (Method attributeMethod : this.methodResolver.getModelAttributeMethods()) { 14 Method attributeMethodToInvoke = BridgeMethodResolver.findBridgedMethod(attributeMethod); 15 Object[] args = resolveHandlerArguments(attributeMethodToInvoke, handler, webRequest, implicitModel); 16 if (debug) { 17 logger.debug("Invoking model attribute method: " + attributeMethodToInvoke); 18 } 19 String attrName = AnnotationUtils.findAnnotation(attributeMethod, ModelAttribute.class).value(); 20 if (!"".equals(attrName) && implicitModel.containsAttribute(attrName)) { 21 continue; 22 } 23 ReflectionUtils.makeAccessible(attributeMethodToInvoke); 24 Object attrValue = attributeMethodToInvoke.invoke(handler, args); 25 if ("".equals(attrName)) { 26 Class<?> resolvedType = GenericTypeResolver.resolveReturnType(attributeMethodToInvoke, handler.getClass()); 27 attrName = Conventions.getVariableNameForReturnType(attributeMethodToInvoke, resolvedType, attrValue); 28 } 29 if (!implicitModel.containsAttribute(attrName)) { 30 implicitModel.addAttribute(attrName, attrValue); 31 } 32 } 33 Object[] args = resolveHandlerArguments(handlerMethodToInvoke, handler, webRequest, implicitModel); 34 if (debug) { 35 logger.debug("Invoking request handler method: " + handlerMethodToInvoke); 36 } 37 ReflectionUtils.makeAccessible(handlerMethodToInvoke); 38 return handlerMethodToInvoke.invoke(handler, args); 39 } 40 catch (IllegalStateException ex) { 41 // Internal assertion failed (e.g. invalid signature): 42 // throw exception with full handler method context... 43 throw new HandlerMethodInvocationException(handlerMethodToInvoke, ex); 44 } 45 catch (InvocationTargetException ex) { 46 // User-defined @ModelAttribute/@InitBinder/@RequestMapping method threw an exception... 47 ReflectionUtils.rethrowException(ex.getTargetException()); 48 return null; 49 } 50 }
如8-11行所示,在执行被@ModelAttributes注解的方法前,会将上一次通过@SessionAttributes添加至session中的数据添加添加至model中;
第20-22行,在执行被@ModelAttributes注解的方法前,springMVC会判断model中是否已经包含了@ModelAttributes给出的attrName,如果包含,则被@ModelAttributes注解的方法则不再执行