SpringMVC @SessionAttributes 使用詳解以及源碼分析

來源:互聯網
上載者:User

標籤:ann   清除   代碼   應用   time   資料   ace   class   ram   

@sessionattributes

@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Inherited@Documentedpublic @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的方法。

 View Code

 

我們看這行代碼

modelFactory.initModel(webRequest, mavContainer, requestMappingMethod);

跟進去看源碼

 View Code

 

其中這兩句是關鍵的地方,把session的值取出來全部放入到ModelAndViewContainer當中去,這樣我就可以再controller當中的方法中擷取了,當然直接從session中拿也是可以的。

Map<String, ?> attributesInSession = this.sessionAttributesHandler.retrieveAttributes(request);
mavContainer.mergeAttributes(attributesInSession);

 

在controller中擷取sessionAttributes的只有兩種方式。

一、public ModelAndView index2(@ModelAttribute("attr1")String attr1, @ModelAttribute("attr2")String attr2) 

二、public ModelAndView index2(ModelMap map)

這倆種方式的區別是,如果使用@ModelAttribute屬性擷取值,並且@SessionAttributes註解當中還設定了該屬性,當屬性為null時會跑出異常,因為這幾行代碼。

for (String name : findSessionAttributeArguments(handlerMethod)) {            if (!mavContainer.containsAttribute(name)) {                Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);                if (value == null) {                    throw new HttpSessionRequiredException("Expected session attribute ‘" + name + "‘");                }                mavContainer.addAttribute(name, value);            }}

 

 

以上分析的都是取值的過程,那麼Spring是如何將Model中的資料同步到session當中的呢。

 

我們看上面invokeHandleMethod方法中最後一句話 return getModelAndView(mavContainer, modelFactory, webRequest); 我們跟進去看源碼。

 View Code

 

有一行代碼是modelFactory.updateModel(webRequest, mavContainer); 我們在跟進去看源碼。

我們看到首先判斷一下ModelAndViewContainer中的SessionStatus是否是完成狀態,如果是TRUE就清除session中的值,反之將ModelAndViewContainer中的model值設定進去。

 View Code

 

 

看到這裡可能還有一個疑問,就是我們的資料時設定到ModelAndView裡面的,那麼這些資料時如何跑到ModelAndViewContainer當中去的呢,其實是在方法執行完成之後Spring幫我們做的,

具體細節查看 ModelAndViewMethodReturnValueHandler方法中的最後一行代碼mavContainer.addAllAttributes(mav.getModel());

 View Code

SpringMVC @SessionAttributes 使用詳解以及源碼分析

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.