写点什么

SpringMVC 源码分析 -HandlerAdapter(5)-SessionAttributesHandler 组件分析

用户头像
Brave
关注
发布于: 3 小时前

SessionAttributesHandler 组件分析


1,SessionAttributesHandler 介绍


用于处理@SessionAttributes注释的参数具体存储工由又SessionAttributeStore完成SessionAttributeStore并不是保存数据的容器,而是保存数据的一个工具类保存数据的容器默认使用Session,也可以使用其他容器,重写SessionAttributeStore设置到RequestMappngHandlerAdapter即可就会在SessionAttributeStore中保存到其他容器
复制代码


SessionAttributesHandler 的属性

// 用于存储@SessionAttributes注解中的参数名value值private final Set<String> attributeNames = new HashSet<String>();
// 用于存储@SessionAttributes注解中的参数类型types值private final Set<Class<?>> attributeTypes = new HashSet<Class<?>>();
// 用于存储所有已知可以被当前处理器处理的属性名// 1,构造方法里会将所有attributeNames设置到knownAttributeNames// 2,当调用isHandlerSessionAttribute方法检查,// 并且是当前Handler所管理的SessionAttributesprivate final Set<String> knownAttributeNames = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(4));
// 具体执行Attribute的存储工作private final SessionAttributeStore sessionAttributeStore;
复制代码


1,attributeNames  用于存储@SessionAttributes注解中的参数名value值  2,attributeTypes  用于存储@SessionAttributes注解中的参数类型types值  3,knownAttributeNames  用于存储所有已知可以被当前处理器处理的属性名  保存了使用value配置的名称和通过types配置的已经保存过的属性名    knownAttributeNames来自两个地方:  1,构造方法里会将所有attributeNames设置到knownAttributeNames  2,当调用isHandlerSessionAttribute方法检查,  并且是当前Handler所管理的SessionAttributes时,也会添加到knownAttributeNames中  保存属性的storeAttributes方法会在每个属性保存前调用isHandlerSessionAttribute  判断是否支持要保存的属性,所以,所有保存过得属性名称都会保存在knownAttributeNames中  4,sessionAttributeStore  具体执行Attribute的存储工作
复制代码


SessionAttributesHandler 源码:

public SessionAttributesHandler(Class<?> handlerType, SessionAttributeStore sessionAttributeStore) {  Assert.notNull(sessionAttributeStore, "SessionAttributeStore may not be null");  this.sessionAttributeStore = sessionAttributeStore;
SessionAttributes annotation = AnnotatedElementUtils.findMergedAnnotation(handlerType, SessionAttributes.class); if (annotation != null) { this.attributeNames.addAll(Arrays.asList(annotation.names())); this.attributeTypes.addAll(Arrays.asList(annotation.types())); }
for (String attributeName : this.attributeNames) { this.knownAttributeNames.add(attributeName); }}
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; }}
复制代码


SessionAttributesHandler 提供了 Attribute 操作:

// 保存属性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);}
复制代码


取出全部属性retrieveAttributes和清除属性cleanupAttributes都遍历了knownAttributeNamesknownAttributeNames中只保存了当前Handler注释里所有使用过的属性名所以这两个方法的操作值针对当前处理器类的@SessionAttributes注释里的配置起作用
按名称取属性的方法可以在整个SessionAttributes中查找,没有knownAttributeNames的限制
需要注意: 如果在不同的Handler中使用SessionAttributes保存的属性使用了相同名称,它们会相互影响
复制代码




2,SessionAttributeStore


SessionAttributeHandler 中对每个参数的保存、取回、删除工作都是由 SessionAttributeStore 完成的;

SessionAttributeStore:是一个接口,里面的三个方法对应以上的三个功能;


SessionAttributeStore 源码:

public interface SessionAttributeStore {
void storeAttribute(WebRequest request, String attributeName, Object attributeValue);
Object retrieveAttribute(WebRequest request, String attributeName);
void cleanupAttribute(WebRequest request, String attributeName);
}
复制代码


SessionAttributeStore 有一个默认实现类:DefaultSessionAttributeStore;

RequestMappingHandlerAdapter 初始化 sessionAttributeStore 就使用了该默认实现类;

private SessionAttributeStore sessionAttributeStore = new DefaultSessionAttributeStore();
复制代码



DefaultSessionAttributeStore


DefaultSessionAttributeStore:将参数保存到 Session 中;


DefaultSessionAttributeStore 源码:

public class DefaultSessionAttributeStore implements SessionAttributeStore {    private String attributeNamePrefix = "";
public DefaultSessionAttributeStore() { }
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 = this.getAttributeNameInSession(request, attributeName); request.setAttribute(storeAttributeName, attributeValue, 1); }
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 = this.getAttributeNameInSession(request, attributeName); return request.getAttribute(storeAttributeName, 1); }
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 = this.getAttributeNameInSession(request, attributeName); request.removeAttribute(storeAttributeName, 1); }
protected String getAttributeNameInSession(WebRequest request, String attributeName) { return this.attributeNamePrefix + attributeName; }}
复制代码


需要注意:这里虽Session的操作使用的是request的setAttribute和getAttribute以及removeAttribute虽然是request调用的,但并不是设置到了request上面,通过最后一个参数制定了设置范围
这里使用的request实际是ServletWebRequest,这三个方法是在其父类ServletRequestAttributes中定义的最后一个参数指定了操作的范围
复制代码


3,ServletRequestAttributes#setAttribute


@Overridepublic void setAttribute(String name, Object value, int scope) {  // request  if (scope == SCOPE_REQUEST) {    if (!isRequestActive()) {      throw new IllegalStateException(          "Cannot set request attribute - request is not active anymore!");    }    this.request.setAttribute(name, value);  }  // session  else {    HttpSession session = getSession(true);    this.sessionAttributesToUpdate.remove(name);    session.setAttribute(name, value);  }}
复制代码


如果第三个参数传入的是SCOPE_REQUEST会存储到request,否则存到Session
sessionAttributesToUpdate属性: 用于保存从Session中获取过的值,request使用完后再同步给Session 因为Session中的值可能已经使用别的方式修改过代码中使用了SCOPE_SESSION,会对Session操作,而不是对request操作
复制代码



4,总结


SessionAttributesHandler与@SessionAttributes注释相对应,用于对SessionAttributes操作SessionAttributesHandler包含判断参数是否可被处理及批量操作多个参数等功能具体对单个参数的操由SessionAttributeStore完成,默认实现为DefaultSessionAttributeStoreDefaultSessionAttributeStore使用ServletWebRequest将参数设置到Session中SessionAttributesHandler是在ModelFactory中使用的
下一篇,ModelFactory...
复制代码


用户头像

Brave

关注

还未添加个人签名 2018.12.13 加入

还未添加个人简介

评论

发布
暂无评论
SpringMVC源码分析-HandlerAdapter(5)-SessionAttributesHandler组件分析