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

 @Controller
@RequestMapping("telephones")
@SessionAttributes(value={"name","degree"},types={Double.class})
public class AttributeController {
// ...
}

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

 package org.springframework.web.bind.annotation;

 @Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface SessionAttributes { String[] value() default {};
Class[] types() default {};
}

  1.2 SessionAttributesHandler

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

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

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

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

 package org.springframework.web.method.annotation;

 public class SessionAttributesHandler {
// 属性名称,对应注解的value
private final Set<String> attributeNames = new HashSet<String>();
// 属性的数据类型,对应注解的types
private final Set<Class<?>> attributeTypes = new HashSet<Class<?>>();
// 缓存配置的attribute,包括根据类型扫描得到的属性,这样清除的时候,可以直接以这个为索引操作
// using a ConcurrentHashMap as a Set
private final Map<String, Boolean> knownAttributeNames = new ConcurrentHashMap<String, Boolean>(4);
// 具体操作session的utils,个人感觉名字起的有点古怪
private final SessionAttributeStore sessionAttributeStore; /**
* 实例化的时候,直接解析注解
*/
public SessionAttributesHandler(Class<?> handlerType, SessionAttributeStore sessionAttributeStore) {
Assert.notNull(sessionAttributeStore, "SessionAttributeStore may not be null.");
this.sessionAttributeStore = sessionAttributeStore; 22 SessionAttributes annotation = AnnotationUtils.findAnnotation(handlerType, SessionAttributes.class);
if (annotation != null) {
24 this.attributeNames.addAll(Arrays.asList(annotation.value()));
25 this.attributeTypes.addAll(Arrays.<Class<?>>asList(annotation.types()));
} for (String attributeName : this.attributeNames) {
29 this.knownAttributeNames.put(attributeName, Boolean.TRUE);
}
} public boolean hasSessionAttributes() {
return ((this.attributeNames.size() > 0) || (this.attributeTypes.size() > 0));
} /**
* 判断是否支持的同时直接缓存attributeName
*/
public boolean isHandlerSessionAttribute(String attributeName, Class<?> attributeType) {
if (this.attributeNames.contains(attributeName) || this.attributeTypes.contains(attributeType)) {
this.knownAttributeNames.put(attributeName, Boolean.TRUE);
return true;
}
else {
return false;
}
}
// 保存
public void storeAttributes(WebRequest request, Map<String, ?> attributes) {}
// 读取
public Map<String, Object> retrieveAttributes(WebRequest request) {}
Object retrieveAttribute(WebRequest request, String attributeName) {}
// 清除
public void cleanupAttributes(WebRequest request) {} }

  1.3 SessionAttributeStore用于session中属性的操作

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

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

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

SessionAttributeStore接口定义

 package org.springframework.web.bind.support;
public interface SessionAttributeStore {
void storeAttribute(WebRequest request, String attributeName, Object attributeValue);
Object retrieveAttribute(WebRequest request, String attributeName);
void cleanupAttribute(WebRequest request, String attributeName); }

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

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

2. session属性的保存

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

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

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

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

 package org.springframework.web.method.annotation;

 public class SessionAttributesHandler {
/**
* 实例化的时候,直接解析注解
*/
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) {
13 this.attributeNames.addAll(Arrays.asList(annotation.value()));
14 this.attributeTypes.addAll(Arrays.<Class<?>>asList(annotation.types()));
} for (String attributeName : this.attributeNames) {
this.knownAttributeNames.put(attributeName, Boolean.TRUE);
}
} public boolean hasSessionAttributes() {
return ((this.attributeNames.size() > 0) || (this.attributeTypes.size() > 0));
} /**
* 判断是否支持的同时直接缓存attributeName
*/
public boolean isHandlerSessionAttribute(String attributeName, Class<?> attributeType) {
30 if (this.attributeNames.contains(attributeName) || this.attributeTypes.contains(attributeType)) {
31 this.knownAttributeNames.put(attributeName, Boolean.TRUE);
return true;
}
else {
return false;
}
}
/**
* Store a subset of the given attributes in the session. Attributes not
* declared as session attributes via {@code @SessionAttributes} are ignored.
* @param request the current request
* @param attributes candidate attributes for session storage
*/
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);
}
}
} }
 package org.springframework.web.bind.support;
public class DefaultSessionAttributeStore implements SessionAttributeStore {
public void storeAttribute(WebRequest request, String attributeName, Object attributeValue) {
String storeAttributeName = getAttributeNameInSession(request, attributeName);
5 request.setAttribute(storeAttributeName, attributeValue, WebRequest.SCOPE_SESSION);
}
// ...
}

3. session属性的读取

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

具体的处理逻辑:

  迭代knownAttributeNames

  委托sessionAttributeStore处理

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

  同时过滤出null的属性

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

 //     SessionAttributesHandler
public Map<String, Object> retrieveAttributes(WebRequest request) {
Map<String, Object> attributes = new HashMap<String, Object>();
for (String name : this.knownAttributeNames.keySet()) {
Object value = this.sessionAttributeStore.retrieveAttribute(request, name);
if (value != null) {
attributes.put(name, value);
}
}
return attributes;
}
 // DefaultSessionAttributeStore
public Object retrieveAttribute(WebRequest request, String attributeName) {
String storeAttributeName = getAttributeNameInSession(request, attributeName);
return request.getAttribute(storeAttributeName, WebRequest.SCOPE_SESSION);
}

4. session属性的清除

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

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

 // SessionAttributesHandler
public void cleanupAttributes(WebRequest request) {
for (String attributeName : this.knownAttributeNames.keySet()) {
this.sessionAttributeStore.cleanupAttribute(request, attributeName);
}
}
 // DefaultSessionAttributeStore
public void cleanupAttribute(WebRequest request, String attributeName) {
String storeAttributeName = getAttributeNameInSession(request, attributeName);
request.removeAttribute(storeAttributeName, WebRequest.SCOPE_SESSION);
}

SpringMVC源码解析 - HandlerAdapter - @SessionAttributes注解处理的更多相关文章

  1. SpringMVC源码解析- HandlerAdapter - ModelFactory(转)

    ModelFactory主要是两个职责: 1. 初始化model 2. 处理器执行后将modle中相应参数设置到SessionAttributes中 我们来看看具体的处理逻辑(直接充当分析目录): 1 ...

  2. SpringMVC源码解析- HandlerAdapter - ModelFactory

    ModelFactory主要是两个职责: 1. 初始化model 2. 处理器执行后将modle中相应参数设置到SessionAttributes中 我们来看看具体的处理逻辑(直接充当分析目录): 1 ...

  3. SpringMVC源码解析- HandlerAdapter初始化

    HandlerAdapter初始化时,主要是进行注解解析器初始化注册;返回值处理类初始化;全局注解@ControllerAdvice内容读取并缓存. 目录: 注解解析器初始化注册:@ModelAttr ...

  4. SpringMVC源码解析 - HandlerAdapter - HandlerMethodArgumentResolver

    HandlerMethodArgumentResolver主要负责执行handler前参数准备工作. 看个例子,红色部分的id初始化,填充值就是它干的活: @RequestMapping(value ...

  5. springMVC源码解析--ViewResolver视图解析器执行(三)

    之前两篇博客springMVC源码分析--ViewResolver视图解析器(一)和springMVC源码解析--ViewResolverComposite视图解析器集合(二)中我们已经简单介绍了一些 ...

  6. SpringMVC源码情操陶冶-AnnotationDrivenBeanDefinitionParser注解解析器

    mvc:annotation-driven节点的解析器,是springmvc的核心解析器 官方注释 Open Declaration org.springframework.web.servlet.c ...

  7. SpringMVC源码解析

    一:springmvc运行过程: 1. dispatcherServlet 通过 HandlerMapping 找到controller2. controller经过后台逻辑处理得到结果集modela ...

  8. 深入了解SpringMVC源码解析

    Spring MVC源码解析 Spring MVC的使用原理其实是通过配置一个Servlet来接管所有的请求,所有的请求由这个Servlet来进行分发处理. 我们可以从web.xml里面看出这一点 & ...

  9. springMVC源码分析--HandlerAdapter(一)

    HandlerAdapter的功能实际就是执行我们的具体的Controller.Servlet或者HttpRequestHandler中的方法. 类结构如下:

随机推荐

  1. 写写Django中DRF框架概述以及序列化器对象serializer的构造方法以及使用

    写写Django中DRF框架概述以及序列化器对象serializer的构造方法以及使用 一.了解什么是DRF DRF: Django REST framework Django REST framew ...

  2. vmware克隆linux网络配置

    一.配置Linux网络 在安装Linux的时候,一定要保证你的物理网络的IP是手动设置的,要不然会在Linux设置IP连通网络的时候会报network is unreachable 并且怎么也找不到问 ...

  3. vagrant 相关记录

    最近安装vagrant 出错的最大的可能性是BOX 路径不太对, 好像和目录的大小写有关系,请检查 $ vagrant init # 初始化$ vagrant up # 启动虚拟机$ vagrant ...

  4. 黄聪:如何配置Emeditor实现代码智能识别自动完成功能

    设置方法如图所示: 效果如下图所示:

  5. HDU 1043 八数码问题的多种解法

    一.思路很简单,搜索.对于每一种状态,利用康托展开编码成一个整数.于是,状态就可以记忆了. 二.在搜索之前,可以先做个优化,对于逆序数为奇数的序列,一定无解. 三.搜索方法有很多. 1.最普通的:深搜 ...

  6. 第2课 GUI程序实例分析

    1. GUI程序开发概述 (1)现代操作系统提供原生SDK支持GUI程序开发 (2)GUI程序开发是现代操作系统上的主流技术 (3)不同操作系统上的GUI开发原理相同 (4)不同操作系统上的GUI S ...

  7. PHP中的=>,->,@,&,::,%

    在php中数组默认键名是整数,也可以自己定义任意字符键名(最好是有实际意义).如: $css=array('style'=>'0',‘color’=>‘green‘), 则$css['st ...

  8. 笔记本制作centos qcow2格式文件

    笔记本win7先通过vbox安装好centos6.5 然后打开cmd命令行在c:\Program Files\Oracle\VirtualBox下执行 vboxmanage clonehd --for ...

  9. Selenium2+python自动化64-100(大结局)[已出书]

    前言 小编曾经说过要写100篇关于selenium的博客文章,前面的64篇已经免费放到博客园供小伙伴们学习,后面的内容就不放出来了,高阶内容直接更新到百度阅读了. 一.百度阅读地址: 1.本书是在线阅 ...

  10. java 在MySQL中存储文件,读取文件(包括图片,word文档,excel表格,ppt,zip文件等)

    转自:https://blog.csdn.net/u014475796/article/details/49893261 在设计到数据库的开发中,难免要将图片或文档文件(如word)插入到数据库中的情 ...