`
king_tt
  • 浏览: 2234034 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

springMVC源码分析--@SessionAttribute用法及原理解析SessionAttributesHandler和SessionAttributeStore

 
阅读更多

@SessionAttribute作用于处理器类上,用于在多个请求之间传递参数,类似于Session的Attribute,但不完全一样,一般来说@SessionAttribute设置的参数只用于暂时的传递,而不是长期的保存,长期保存的数据还是要放到Session中。

通过@SessionAttribute注解设置的参数有3类用法:

(1)在视图中通过request.getAttribute或session.getAttribute获取

(2)在后面请求返回的视图中通过session.getAttribute或者从model中获取

(3)自动将参数设置到后面请求所对应处理器的Model类型参数或者有@ModelAttribute注释的参数里面。

将一个参数设置到SessionAttribute中需要满足两个条件:

(1)在@SessionAttribute注解中设置了参数的名字或者类型

(2)在处理器中将参数设置到了model中。

@SessionAttribute用户后可以调用SessionStatus.setComplete来清除,这个方法只是清除SessionAttribute里的参数,而不会应用Session中的参数。

示例如下:注解@SessionAttribute中设置book、description和types={Double},这样值会被放到@SessionAttribute中,但Redirect跳转时就可以重新获得这些数据了,接下来操作sessionStatus.setComplete(),则会清除掉所有的数据,这样再次跳转时就无法获取数据了。

@Controller
@RequestMapping("/book")
@SessionAttributes(value ={"book","description"},types={Double.class})
public class RedirectController {
	
	@RequestMapping("/index")
	public String index(Model model){
		model.addAttribute("book", "金刚经");
		model.addAttribute("description","不擦擦擦擦擦擦擦车");
		model.addAttribute("price", new Double("1000.00"));
		//跳转之前将数据保存到book、description和price中,因为注解@SessionAttribute中有这几个参数
		return "redirect:get.action";
	}
	
	@RequestMapping("/get")
	public String get(@ModelAttribute ("book") String book,ModelMap model,
			SessionStatus sessionStatus){
		//可以获得book、description和price的参数
		System.out.println(model.get("book")+";"+model.get("description")+";"+model.get("price"));
		sessionStatus.setComplete();
		return "redirect:complete.action";
	}
	
	@RequestMapping("/complete")
	public String complete(ModelMap modelMap){
		//已经被清除,无法获取book的值
		System.out.println(modelMap.get("book"));
		modelMap.addAttribute("book", "妹纸");
		return "sessionAttribute";
	}

}
接下来我们分析一下@SessionAttribute的实现机制

第一步:我们首先要获取注解@SessionAttribute的值的情况,在RequestMappingHandlerAdapter中的getModelFactory中处理。

protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

		//这里面对注解的@SessionAttribute的处理器做处理
		ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

		........
		//会对@SessionAttribute操作的值进行处理
		modelFactory.initModel(webRequest, mavContainer, invocableMethod);
		........

		return getModelAndView(mavContainer, modelFactory, webRequest);
	}
在getModelFactory中会创建@SessionAttribute的处理器SessionAttributeHandler

	
	private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {
		//创建SessionAttribute处理器
		SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);
		
		return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
	}

getSessionAttributesHandler的操作就是获取或者初始化SessionAttributesHandler

//已经获取过的SessionAttribute放到Map中,如果没有则需要初始化
	private SessionAttributesHandler getSessionAttributesHandler(HandlerMethod handlerMethod) {
		Class<?> handlerType = handlerMethod.getBeanType();
		SessionAttributesHandler sessionAttrHandler = this.sessionAttributesHandlerCache.get(handlerType);
		if (sessionAttrHandler == null) {
			synchronized (this.sessionAttributesHandlerCache) {
				sessionAttrHandler = this.sessionAttributesHandlerCache.get(handlerType);
				if (sessionAttrHandler == null) {
					//初始化sessionAttrHandler,并放到map中
					sessionAttrHandler = new SessionAttributesHandler(handlerType, sessionAttributeStore);
					this.sessionAttributesHandlerCache.put(handlerType, sessionAttrHandler);
				}
			}
		}
		return sessionAttrHandler;
	}

SessionAttributesHandler的构造函数中的操作如下,其实就是解析被@SessionAttribute注解的处理器,这样就完成了@SessionAttribute注解中设置的key的解析。

public SessionAttributesHandler(Class<?> handlerType, SessionAttributeStore sessionAttributeStore) {
		Assert.notNull(sessionAttributeStore, "SessionAttributeStore may not be null.");
		this.sessionAttributeStore = sessionAttributeStore;
		//解析被@SessionAttribute注解的处理器
		SessionAttributes annotation = AnnotationUtils.findAnnotation(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);
		}
	}

接下来我们看看springMVC对@SessionAttribute的处理操作,在ModelFactory.initModel会对@SessionAttribute的注解进行处理操作。

protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

		//这里面对注解的@SessionAttribute的处理器做处理
		ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

		........
		//会对@SessionAttribute操作的值进行处理
		modelFactory.initModel(webRequest, mavContainer, invocableMethod);
		........

		return getModelAndView(mavContainer, modelFactory, webRequest);
	}

initModel其实做了两步操作,一是:获取上一次请求保存在SessionAttributeHandler中值,给这次请求的值用,二是将这次请求的处理结果可能会对上次的@SessionAttribute中的值进行改变的值进行保存给下一次请求使用。

public void initModel(NativeWebRequest request, ModelAndViewContainer mavContainer, HandlerMethod handlerMethod)
			throws Exception {
		//获取所有的@SessionAttribute注解设置的key中值
		Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);
		//将获取的值传递给下一个请求使用
		mavContainer.mergeAttributes(sessionAttributes);

		invokeModelAttributeMethods(request, mavContainer);
		
		//请求访问完之后将修改的值重新放到@SessionAttributeStore设置的key中
		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);
			}
		}
	}
sessionAttributesHandler.retrieveAttributes的操作是将request中值,按照注解@SessionAttribute中key取值处理,然后保存到attribute中,作为这次请求的传递值使用。

public Map<String, Object> retrieveAttributes(WebRequest request) {
		Map<String, Object> attributes = new HashMap<String, Object>();
		//获取注解@SessionAttribute中设置的key
		for (String name : this.knownAttributeNames) {
			//如果设置的key有值则把它保存到attribute中,给跳转之后的请求使用
			Object value = this.sessionAttributeStore.retrieveAttribute(request, name);
			if (value != null) {
				attributes.put(name, value);
			}
		}
		return attributes;
	}
这样就完成了将值保存在SessionAttributeHandler中,这样下一次请求过来时依然可以从SessionAttributeHandler中获取上次的结果,完成了类似Session的实现机制,但明显感觉和Session不一样,所有的请求其值是保存在一个同一个SessionAttributeHandler中。


SessionAttributeHandler其实维持了一个Map结构来存取数据,功能主要有解析@SessionAttribute注解,get和set相关值,源码如下:

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.newSetFromMap(new ConcurrentHashMap<String, Boolean>(4));

	private final SessionAttributeStore sessionAttributeStore;


	//构造函数,解析@SessionAttribute注解,将其设置额key等信息保存
	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.names()));
			this.attributeTypes.addAll(Arrays.asList(annotation.types()));
		}

		for (String attributeName : this.attributeNames) {
			this.knownAttributeNames.add(attributeName);
		}
	}

	
	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);
	}

}





分享到:
评论

相关推荐

    SpringMVC ---- HelloWorld ---- 代码

    SpringMVC ---- HelloWorld ---- 代码 SpringMVC ---- HelloWorld ---- 代码 SpringMVC ---- HelloWorld ---- 代码 SpringMVC ---- HelloWorld ---- 代码 SpringMVC ---- HelloWorld ---- 代码 SpringMVC ---- Hello...

    SpringMVC-Mybatis-Shiro-redis-0.2-master

    基于SpringMVC Mybatis Shiro Redis 的权限管理系统,该系统已经部署到线上,线上访问地址:http://shiro.itboy.net,登录账号:admin 密码:sojson,,详细教程参考sojson.com/shiro

    springMVC集成spring-data-redis

    在SpringMVC中集成Spring Data Redis,可以利用Redis的高效特性来提升应用程序的数据处理能力,例如作为session共享的存储、缓存数据或者实现发布/订阅(Pub/Sub)功能。发布/订阅是一种通信模式,允许发送者(pub)将...

    springMvc源码分析

    springMvc源码分析springMvc源码分析springMvc源码分析springMvc源码分析springMvc源码分析springMvc源码分析

    SpringMVC SessionAttribute 使用说明.docx

    2. **处理请求**:在控制器方法执行后,Spring MVC的`SessionAttributesHandler`会将模型中被`@SessionAttribute`注解的属性保存到会话中。 3. **请求结束时**:如果需要从会话中移除特定属性,可以使用`Session...

    springmvc-base-quick.rar

    《Spring MVC 快速入门详解》 Spring MVC 是 Spring 框架的...通过这个 "springmvc-base-quick" 项目,你可以快速搭建一个基本的 Spring MVC 环境,理解其工作原理,并逐步深入学习 Spring MVC 的高级特性和最佳实践。

    easyUI-springmvc-tree-demo

    其基本使用方法是通过HTML元素和JavaScript来创建。在HTML中,我们可以定义一个div作为树的容器,并设置相应的class为"easyui-tree"。然后在JavaScript中,通过$(selector).tree()方法初始化并配置Tree组件。 二、...

    SpringMVC精品资源--JAX-RS &amp; SpringMVC supported maven buil.zip

    SpringMVC和JAX-RS是两种在Java世界中广泛使用的Web开发框架,它们都用于构建RESTful服务,但有着不同的设计哲学和技术实现。这个压缩包"SpringMVC精品资源--JAX-RS & SpringMVC supported maven build.zip"显然是一...

    springMVC学习--基本的几个例子

    SpringMVC是Spring框架的一部分,专门用于构建Web应用程序。它是一个模型-视图-控制器(MVC)架构的实现,提供了强大的数据绑定、验证、国际化等功能,极大地简化了Java Web开发。在“springMVC学习--基本的几个例子...

    springmvc学习笔记-springmvc注解开发.zip

    在"springmvc学习笔记--springmvc注解开发.zip"这个压缩包中,我们可以深入探讨Spring MVC的注解驱动开发方式,这是一种简洁且高效的开发模式。 1. **Spring MVC基础**: Spring MVC的核心组件包括...

    SpringMVC------从HelloWorld开始

    让我们从一个简单的"HelloWorld"示例开始,深入了解SpringMVC的工作原理。 首先,创建一个`HelloWorld`控制器是开始任何SpringMVC项目的第一步。在Java中,我们通常会创建一个继承自`org.springframework....

    SpringMVC4教程-.pptx

    SpringMVC4教程-.pptx SpringMVC4教程-.pptx SpringMVC4教程-.pptx SpringMVC4教程-.pptx SpringMVC4教程-.pptx

    SpringMVC-Mybatis-Shiro-redis-0.2_baichengzhou.tar.gz

    《SpringMVC-Mybatis-Shiro-Redis整合实践详解》 在现代Web开发中,...通过分析源码,开发者可以深入理解每个组件的功能以及它们如何协同工作,从而在自己的项目中更好地应用这些技术,提升项目的质量和效率。

    springmvc-high-other-05.rar

    本资料"springmvc-high-other-05.rar"着重探讨了SpringMVC的一些高级特性和用法,特别是那些不太常见但对提升应用性能和可维护性至关重要的特性。 1. **POM.xml配置解析**:在"pom.xml"文件中,我们可以看到项目...

    springmvc课堂笔记-1

    ### SpringMVC基础知识详解 #### 一、SpringMVC简介 **SpringMVC**是Spring框架中的一个子项目,主要用于构建Web应用程序,并且遵循MVC(Model-View-Controller)设计模式。它提供了灵活的控制层框架,可以轻松地...

    SpringMVC精品资源--JAX-RS &amp; SpringMVC supported gradle bui.zip

    【标题】"SpringMVC精品资源--JAX-RS & SpringMVC supported gradle bui.zip" 提供的是一份关于使用Gradle构建支持JAX-RS和SpringMVC的项目资源。这涉及到两个关键的技术栈:SpringMVC,一个用于构建Web应用程序的...

    springmvc-maven-webapp-helloworld 源码

    【标题】"SpringMVC Maven Webapp HelloWorld 源码解析" 在Web开发领域,SpringMVC是一个广泛应用的Java框架,用于构建可扩展且模块化的Web应用程序。它作为Spring框架的一部分,提供了处理HTTP请求、视图渲染以及...

    1、手写springmvc框架及分析springmvc源码.zip

    1、手写springmvc框架及分析springmvc源码.zip1、手写springmvc框架及分析springmvc源码.zip1、手写springmvc框架及分析springmvc源码.zip1、手写springmvc框架及分析springmvc源码.zip1、手写springmvc框架及分析...

    基于springboot+springmvc+mybatis-plus的微信小程序美容预约平台源码.zip

    基于springboot+springmvc+mybatis-plus的微信小程序美容预约平台源码.zip本资源中的源码都是经过本地编译过可运行的,评审分达到95分以上。资源项目的难度比较适中,内容都是经过助教老师审定过的能够满足学习、...

    SpringMVC精品资源--SpringMVC+Mybatis 脚手架.zip

    SpringMVC和Mybatis是Java开发中非常流行的两个框架,它们在企业级Web应用开发中起着关键作用。SpringMVC作为Spring框架的一部分,主要负责处理HTTP请求和响应,而Mybatis则是一个轻量级的持久层框架,专注于数据库...

Global site tag (gtag.js) - Google Analytics