zoukankan      html  css  js  c++  java
  • SpringMVC源码解析

    使用SpringMVC开发时,可以使用@SessionAttributes注解缓存信息.这样业务开发时,就不需要一次次手动操作session保存,读数据.

    1 @Controller
    2 @RequestMapping("telephones")
    3 @SessionAttributes(value={"name","degree"},types={Double.class})
    4 public class AttributeController {
    5     // ...
    6 }

    SpringMVC实际处理时这部分时,主要涉及3个概念:

      @SessionAttributes注解定义

      注解信息初始化与容器SessionAttributesHanlder

      session操作类SessionAttributeStore,虽然叫做store其实叫utils更贴切

    SessionAttributesHanlder在初始化时扫描类里的方法,找出@SessionAttributes注解,并解析,然后直接保存到attributeNames和attributeTypes中,再更新knownAttributeNames.

    保存的话,也可以在后期storeAttributes和isHandlerSessionAttribute进行.

    在读取,清除时,都是以knownAttributeNames为索引,然后委托SessionAttributeStore处理.

     SessionAttributeStore具体的session操作是委托WebRequest处理的,他主要是封装了一个属性前缀.

    具体分析目录:

      1. 各类定义科普:@SessionAttributes,SessionAttributesHandler,SessionAttributeStore

      2. session属性的保存

      3. session属性的读取

      4. session属性的清除

    1. 各类定义科普

      1.1 先看@SessionAttributes注解的定义吧,这边就两种配置方式,一种是value定义属性的name,一种是types定义属性的类型,如Date

     1 package org.springframework.web.bind.annotation;
     2 
     3 @Target({ElementType.TYPE})
     4 @Retention(RetentionPolicy.RUNTIME)
     5 @Inherited
     6 @Documented
     7 public @interface SessionAttributes {
     8     
     9     String[] value() default {};
    10     Class[] types() default {};
    11 }

      1.2 SessionAttributesHandler

      其实有点容器的味道,这个可以从attributeNames和attributeTypes属性可以看出.

      同时也维护着对应属性的保存,读取与清除,这个看看构造发方法,retrieveAttributes,cleanupAttributes api就很清楚

      当然具体对session的操作,是通过SessionAttributeStore处理的.

      其中knownAttributeNames使用了ConcurrentHashMap,说明这边是线程安全的.

     1 package org.springframework.web.method.annotation;
     2 
     3 public class SessionAttributesHandler {
     4     // 属性名称,对应注解的value
     5     private final Set<String> attributeNames = new HashSet<String>();
     6     // 属性的数据类型,对应注解的types
     7     private final Set<Class<?>> attributeTypes = new HashSet<Class<?>>();
     8     // 缓存配置的attribute,包括根据类型扫描得到的属性,这样清除的时候,可以直接以这个为索引操作
     9     // using a ConcurrentHashMap as a Set
    10     private final Map<String, Boolean> knownAttributeNames = new ConcurrentHashMap<String, Boolean>(4);
    11     // 具体操作session的utils,个人感觉名字起的有点古怪
    12     private final SessionAttributeStore sessionAttributeStore;
    13 
    14 
    15     /**
    16      * 实例化的时候,直接解析注解
    17      */
    18     public SessionAttributesHandler(Class<?> handlerType, SessionAttributeStore sessionAttributeStore) {
    19         Assert.notNull(sessionAttributeStore, "SessionAttributeStore may not be null.");
    20         this.sessionAttributeStore = sessionAttributeStore;
    21 
    22         SessionAttributes annotation = AnnotationUtils.findAnnotation(handlerType, SessionAttributes.class);
    23         if (annotation != null) {
    24             this.attributeNames.addAll(Arrays.asList(annotation.value()));
    25             this.attributeTypes.addAll(Arrays.<Class<?>>asList(annotation.types()));
    26         }
    27 
    28         for (String attributeName : this.attributeNames) {
    29             this.knownAttributeNames.put(attributeName, Boolean.TRUE);
    30         }
    31     }
    32 
    33     public boolean hasSessionAttributes() {
    34         return ((this.attributeNames.size() > 0) || (this.attributeTypes.size() > 0));
    35     }
    36 
    37     /**
    38      * 判断是否支持的同时直接缓存attributeName
    39      */
    40     public boolean isHandlerSessionAttribute(String attributeName, Class<?> attributeType) {
    41         if (this.attributeNames.contains(attributeName) || this.attributeTypes.contains(attributeType)) {
    42             this.knownAttributeNames.put(attributeName, Boolean.TRUE);
    43             return true;
    44         }
    45         else {
    46             return false;
    47         }
    48     }
    49     // 保存
    50     public void storeAttributes(WebRequest request, Map<String, ?> attributes) {}
    51     // 读取
    52     public Map<String, Object> retrieveAttributes(WebRequest request) {}
    53     Object retrieveAttribute(WebRequest request, String attributeName) {}
    54     // 清除
    55     public void cleanupAttributes(WebRequest request) {}
    56 
    57 }

      1.3 SessionAttributeStore用于session中属性的操作

      这边定义了一个接口,并提供默认实现.

        接口很简单,直接定义了保存,读取,清除的接口

        默认实现DefaultSessionAttributeStore,在实现接口的基础上,添加了一个前缀的概念用于区分,并委托WebRequest处理

    SessionAttributeStore接口定义

    1 package org.springframework.web.bind.support;
    2 public interface SessionAttributeStore {
    3     void storeAttribute(WebRequest request, String attributeName, Object attributeValue);
    4     Object retrieveAttribute(WebRequest request, String attributeName);
    5     void cleanupAttribute(WebRequest request, String attributeName);
    6 
    7 }

    DefaultSessionAttributeStore其实主要是一个attributeNamePrefix的定义,并封装属性名称getAttributeNameInSession,其他的都是直接委托

     1 package org.springframework.web.bind.support;
     2 public class DefaultSessionAttributeStore implements SessionAttributeStore {
     3     // 属性名称前缀
     4     private String attributeNamePrefix = "";
     5     // 封装属性名称
     6     protected String getAttributeNameInSession(WebRequest request, String attributeName) {
     7         return this.attributeNamePrefix + attributeName;
     8     }
     9     // ...
    10 }

    2. session属性的保存

    保存可以分为两类操作,一个是实际保存属性,一个是标记是否已经处理.

    实际保存的属性是:Set<String> attributeNames和Set<Class<?>> attributeTypes,在保存时,可以使用构造方法和storeAttributes.

    标记是否已经处理是Map<String, Boolean> knownAttributeNames,保存时使用构造方法或者isHandlerSessionAttribute.storeAttributes在保存是也会调用isHandlerSessionAttribute.

    knownAttributeNames在读取,清除时,都是作为索引使用的,特别是使用types进行注解时,没有这个做索引还真不方便.

     1 package org.springframework.web.method.annotation;
     2 
     3 public class SessionAttributesHandler {
     4     /**
     5      * 实例化的时候,直接解析注解
     6      */
     7     public SessionAttributesHandler(Class<?> handlerType, SessionAttributeStore sessionAttributeStore) {
     8         Assert.notNull(sessionAttributeStore, "SessionAttributeStore may not be null.");
     9         this.sessionAttributeStore = sessionAttributeStore;
    10 
    11         SessionAttributes annotation = AnnotationUtils.findAnnotation(handlerType, SessionAttributes.class);
    12         if (annotation != null) {
    13             this.attributeNames.addAll(Arrays.asList(annotation.value()));
    14             this.attributeTypes.addAll(Arrays.<Class<?>>asList(annotation.types()));
    15         }
    16 
    17         for (String attributeName : this.attributeNames) {
    18             this.knownAttributeNames.put(attributeName, Boolean.TRUE);
    19         }
    20     }
    21 
    22     public boolean hasSessionAttributes() {
    23         return ((this.attributeNames.size() > 0) || (this.attributeTypes.size() > 0));
    24     }
    25 
    26     /**
    27      * 判断是否支持的同时直接缓存attributeName
    28      */
    29     public boolean isHandlerSessionAttribute(String attributeName, Class<?> attributeType) {
    30         if (this.attributeNames.contains(attributeName) || this.attributeTypes.contains(attributeType)) {
    31             this.knownAttributeNames.put(attributeName, Boolean.TRUE);
    32             return true;
    33         }
    34         else {
    35             return false;
    36         }
    37     }
    38     /**
    39      * Store a subset of the given attributes in the session. Attributes not
    40      * declared as session attributes via {@code @SessionAttributes} are ignored.
    41      * @param request the current request
    42      * @param attributes candidate attributes for session storage
    43      */
    44     public void storeAttributes(WebRequest request, Map<String, ?> attributes) {
    45         for (String name : attributes.keySet()) {
    46             Object value = attributes.get(name);
    47             Class<?> attrType = (value != null) ? value.getClass() : null;
    48 
    49             if (isHandlerSessionAttribute(name, attrType)) {
    50                 this.sessionAttributeStore.storeAttribute(request, name, value);
    51             }
    52         }
    53     }
    54 
    55 }
    1 package org.springframework.web.bind.support;
    2 public class DefaultSessionAttributeStore implements SessionAttributeStore {
    3     public void storeAttribute(WebRequest request, String attributeName, Object attributeValue) {
    4         String storeAttributeName = getAttributeNameInSession(request, attributeName);
    5         request.setAttribute(storeAttributeName, attributeValue, WebRequest.SCOPE_SESSION);
    6     }
    7     // ...
    8 }

    3. session属性的读取

    可以根据WebRequest读取对应的属性.

    具体的处理逻辑:

      迭代knownAttributeNames

      委托sessionAttributeStore处理

      而sessionAttributeStore是通过WebRequest.SCOPE_SESSION,委托WebRequest处理的

      同时过滤出null的属性

    这么描述逻辑感觉老是委托委托有那么点烦,但人Spring的确每层都封装了一个概念.

     1 //     SessionAttributesHandler
     2     public Map<String, Object> retrieveAttributes(WebRequest request) {
     3         Map<String, Object> attributes = new HashMap<String, Object>();
     4         for (String name : this.knownAttributeNames.keySet()) {
     5             Object value = this.sessionAttributeStore.retrieveAttribute(request, name);
     6             if (value != null) {
     7                 attributes.put(name, value);
     8             }
     9         }
    10         return attributes;
    11     }
    1 // DefaultSessionAttributeStore
    2     public Object retrieveAttribute(WebRequest request, String attributeName) {
    3         String storeAttributeName = getAttributeNameInSession(request, attributeName);
    4         return request.getAttribute(storeAttributeName, WebRequest.SCOPE_SESSION);
    5     }

    4. session属性的清除

    只有SessionStatus.setComplete后才会清除属性.

    清楚属性的逻辑根据保存差不多,就不解释,直接看代码吧

    1 // SessionAttributesHandler
    2     public void cleanupAttributes(WebRequest request) {
    3         for (String attributeName : this.knownAttributeNames.keySet()) {
    4             this.sessionAttributeStore.cleanupAttribute(request, attributeName);
    5         }
    6     }
    1 // DefaultSessionAttributeStore
    2     public void cleanupAttribute(WebRequest request, String attributeName) {
    3         String storeAttributeName = getAttributeNameInSession(request, attributeName);
    4         request.removeAttribute(storeAttributeName, WebRequest.SCOPE_SESSION);
    5     }
  • 相关阅读:
    DevExpress中的gridControl选择问题
    使用IL DASM来查看接口内的自动属性
    多态的使用 虚方法、抽象类、接口
    匿名类的使用及原理
    JAVA小问题解决办法
    数据库02
    数据库01(验证连接是否成功)
    Java 类库知识总结
    Java多线程知识总结(补充)
    java Thread和Runable的深刻理解
  • 原文地址:https://www.cnblogs.com/leftthen/p/5224533.html
Copyright © 2011-2022 走看看