@sessionattributes
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface SessionAttributes { String[] value() default {}; Class[] types() default {}; }
@sessionattributes注解应用到Controller上面,可以将Model中的属性同步到session当中。
先看一个最基本的方法
@Controller @RequestMapping("/Demo.do") @SessionAttributes(value={"attr1","attr2"}) public class Demo { @RequestMapping(params="method=index") public ModelAndView index() { ModelAndView mav = new ModelAndView("index.jsp"); mav.addObject("attr1", "attr1Value"); mav.addObject("attr2", "attr2Value"); return mav; } @RequestMapping(params="method=index2") public ModelAndView index2(@ModelAttribute("attr1")String attr1, @ModelAttribute("attr2")String attr2) { ModelAndView mav = new ModelAndView("success.jsp"); return mav; }
}
index方法返回一个ModelAndView 其中包括视图index.jsp 和 两个键值放入model当中,在没有加入@sessionattributes注解的时候,放入model当中的键值是request级别的。
现在因为在Controller上面标记了@SessionAttributes(value={"attr1","attr2"}) 那么model中的attr1,attr2会同步到session中,这样当你访问index 然后在去访问index2的时候也会获取这俩个属性的值。
当需要清除session当中的值得时候,我们只需要在controller的方法中传入一个SessionStatus的类型对象 通过调用setComplete方法就可以清除了。
@RequestMapping(params="method=index3") public ModelAndView index4(SessionStatus status) { ModelAndView mav = new ModelAndView("success.jsp"); status.setComplete(); return mav; }
下面就直接分析代码来看Spring是如可封装的。
首先我们需要看2个类 DefaultSessionAttributeStore和SessionAttributesHandler
DefaultSessionAttributeStore这个类是用来往WebRequest存取数据的工具类,WebRequest是Spring包装的HttpServletRequest,大家理解为普通的HttpServletRequest就行了。
public class DefaultSessionAttributeStore implements SessionAttributeStore { private String attributeNamePrefix = ""; public void setAttributeNamePrefix(String attributeNamePrefix) { this.attributeNamePrefix = (attributeNamePrefix != null ? attributeNamePrefix : ""); } public void storeAttribute(WebRequest request, String attributeName, Object attributeValue) { Assert.notNull(request, "WebRequest must not be null"); Assert.notNull(attributeName, "Attribute name must not be null"); Assert.notNull(attributeValue, "Attribute value must not be null"); String storeAttributeName = getAttributeNameInSession(request, attributeName); request.setAttribute(storeAttributeName, attributeValue, WebRequest.SCOPE_SESSION); } public Object retrieveAttribute(WebRequest request, String attributeName) { Assert.notNull(request, "WebRequest must not be null"); Assert.notNull(attributeName, "Attribute name must not be null"); String storeAttributeName = getAttributeNameInSession(request, attributeName); return request.getAttribute(storeAttributeName, WebRequest.SCOPE_SESSION); } public void cleanupAttribute(WebRequest request, String attributeName) { Assert.notNull(request, "WebRequest must not be null"); Assert.notNull(attributeName, "Attribute name must not be null"); String storeAttributeName = getAttributeNameInSession(request, attributeName); request.removeAttribute(storeAttributeName, WebRequest.SCOPE_SESSION); } protected String getAttributeNameInSession(WebRequest request, String attributeName) { return this.attributeNamePrefix + attributeName; } }
Spring会为每一个Controller初始化一个SessionAttributesHandler实例,用来记录@SessionAttributes(value={"attr1","attr2"})里面的属性信息,当需要同步model的值时,会先判断是否在SessionAttributes当中定义。
public class SessionAttributesHandler { private final Set<String> attributeNames = new HashSet<String>(); private final Set<Class<?>> attributeTypes = new HashSet<Class<?>>(); private final Set<String> knownAttributeNames = Collections.synchronizedSet(new HashSet<String>(4)); private final SessionAttributeStore sessionAttributeStore; public SessionAttributesHandler(Class<?> handlerType, SessionAttributeStore sessionAttributeStore) { Assert.notNull(sessionAttributeStore, "SessionAttributeStore may not be null."); this.sessionAttributeStore = sessionAttributeStore; SessionAttributes annotation = AnnotationUtils.findAnnotation(handlerType, SessionAttributes.class); if (annotation != null) { this.attributeNames.addAll(Arrays.asList(annotation.value())); this.attributeTypes.addAll(Arrays.<Class<?>>asList(annotation.types())); } this.knownAttributeNames.addAll(this.attributeNames); } public boolean hasSessionAttributes() { return ((this.attributeNames.size() > 0) || (this.attributeTypes.size() > 0)); } public boolean isHandlerSessionAttribute(String attributeName, Class<?> attributeType) { Assert.notNull(attributeName, "Attribute name must not be null"); if (this.attributeNames.contains(attributeName) || this.attributeTypes.contains(attributeType)) { this.knownAttributeNames.add(attributeName); return true; } else { return false; } } public void storeAttributes(WebRequest request, Map<String, ?> attributes) { for (String name : attributes.keySet()) { Object value = attributes.get(name); Class<?> attrType = (value != null) ? value.getClass() : null; if (isHandlerSessionAttribute(name, attrType)) { this.sessionAttributeStore.storeAttribute(request, name, value); } } } public Map<String, Object> retrieveAttributes(WebRequest request) { Map<String, Object> attributes = new HashMap<String, Object>(); for (String name : this.knownAttributeNames) { Object value = this.sessionAttributeStore.retrieveAttribute(request, name); if (value != null) { attributes.put(name, value); } } return attributes; } public void cleanupAttributes(WebRequest request) { for (String attributeName : this.knownAttributeNames) { this.sessionAttributeStore.cleanupAttribute(request, attributeName); } } Object retrieveAttribute(WebRequest request, String attributeName) { return this.sessionAttributeStore.retrieveAttribute(request, attributeName); } }
当我们访问controller中的一个方法时,会调用RequestMappingHandlerAdapter类当中的invokeHandleMethod的方法。
private ModelAndView invokeHandleMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { ServletWebRequest webRequest = new ServletWebRequest(request, response); WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod); ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory); ServletInvocableHandlerMethod requestMappingMethod = createRequestMappingMethod(handlerMethod, binderFactory); ModelAndViewContainer mavContainer = new ModelAndViewContainer(); mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request)); modelFactory.initModel(webRequest, mavContainer, requestMappingMethod); mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect); AsyncWebRequest asyncWebRequest = AsyncWebUtils.createAsyncWebRequest(request, response); asyncWebRequest.setTimeout(this.asyncRequestTimeout); final WebAsyncManager asyncManager = AsyncWebUtils.getAsyncManager(request); asyncManager.setTaskExecutor(this.taskExecutor); asyncManager.setAsyncWebRequest(asyncWebRequest); if (asyncManager.hasConcurrentResult()) { Object result = asyncManager.getConcurrentResult(); mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0]; asyncManager.clearConcurrentResult(); if (logger.isDebugEnabled()) { logger.debug("Found concurrent result value [" + result + "]"); } requestMappingMethod = requestMappingMethod.wrapConcurrentResult(result); } requestMappingMethod.invokeAndHandle(webRequest, mavContainer); if (asyncManager.isConcurrentHandlingStarted()) { return null; } return getModelAndView(mavContainer, modelFactory, webRequest); }