spring session

14 篇文章 0 订阅
12 篇文章 2 订阅
本文内容参考spring-session(一)揭秘,因为2.X新版本的源码差异较大,所以在这里重新进行整理

一、spring session的使用

利用spring boot可以很容易地实现spring session,传送门:
Spring Boot入门教程(五十六): Spring Session


二、总体介绍和文章结构

注:阅读本篇之前需要对java web体系有着基本的了解,例如servlet,HttpServletRequest,HttpServletResponse,filter,HttpSession等。
Spring session主要用于解决多web应用共享session的问题。它主要支持以下三个功能:

  1. 把servlet容器实现的httpSession替换为spring-session,把session存储到第三方存储容器
  2. 支持多个session问题。
  3. 不依赖于cookie。可通过header来传递JSessionId

本文将spring session的源码解析按照四个部分来介绍:

  1. session
  2. SessionRepositoryFilter
  3. request&response
  4. repository

同时本文的源码版本为2.2.x


三、Session

Java Servlet 规范中定义了HttpSession、HttpServletRequest、HttpServletResponse等接口,诸如tomcat等标准web容器都会遵循这个规约,都是基于这些接口进行开发使用。
而spring为了给自身使用,单独抽象出了一个Session接口,并设计了一套实现类。
但是,为了不影响tomcat等标准web容器的正常使用,spring最后输出的必须得是HttpSession类型的接口,而spring的session接口与HttpSession接口是两个完全独立的接口,就好比usb接口和type c接口,因此需要设计一个适配器来进行适配(适配器模式)。在拥有

session相关的类图如下所示:
在这里插入图片描述

HttpSession接口

在这里插入图片描述

Session接口

在这里插入图片描述

HttpSessionAdapter

在这里插入图片描述

作为适配器的具体原理有以下两部分组成:

  1. 实现了HttpSession接口的同时,内置了Spring的Session。
class HttpSessionAdapter<S extends Session> implements HttpSession {
	...
	private S session;
	...
}
  1. 重写了HttpSession接口的所有方法,方法都是通过Spring的Session来实现的,这样就相当于USB接口的具体实现全部换成了Type C的具体实现。
    以getAtribute()方法为例:
	@Override
	public Object getAttribute(String name) {
		checkState();
		return this.session.getAttribute(name);
	}
HttpSessionWrapper

HttpSessionWrapper相对HttpSessionAdapter而言仅仅多了一个invalidate()方法,用于将session失效。
HttpSessionWrapper是spring session体系里,session的使用单位,它从表面上看是HttpSession,但其内部实现其实是spring session
因此将该类作为输出,便可以满足”在遵循java servlet规范的同时,实现spring session功能“这一条件。

MapSession

spring的session接口根据存储工具的不同分为很多种类,有MapSession、RedisSession、JdbcSession、MongoSession等。
在这里插入图片描述

private Map<String, Object> sessionAttrs = new HashMap<>();
RedisSession

在这里插入图片描述


四、SessionRepositoryFilter

Filter过滤器是java web的三大组件之一,在Filter定义范围内的request或response都会被filter拦截并执行内部的方法。
SessionRepositoryFilter会将拦截的请求或响应进行以下操作:

  1. 将HttpServletRequest包装成SessionRepositoryRequestWrapper
  2. 将HttpServletResponse包装成SessionRepositoryResponseWrapper
  3. commitSession()提交session
	@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {
		request.setAttribute(SESSION_REPOSITORY_ATTR, this.sessionRepository);
		//1.将HttpServletRequest包装成SessionRepositoryRequestWrapper
		SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper(request, response);
		//2.将HttpServletResponse包装成SessionRepositoryResponseWrapper
		SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper(wrappedRequest,
				response);

		try {
			filterChain.doFilter(wrappedRequest, wrappedResponse);
		}
		finally {
		//3. commitSession()提交session
			wrappedRequest.commitSession();
		}
	}

五、request&response

类的关系图:
在这里插入图片描述

HttpServletRequest

在这里插入图片描述

HttpServletRequestWrapper

(该类并不需要学会使用,清楚为什么要用这个即可)
HttpServletRequestWrapper 往往与拦截器Filter一共使用,java web的filter主要负责两个功能:

  1. 检查用户的输入
  2. 压缩web内容

而实际上,当我们在使用filter的时候却会发现至少有一半的时间我们都想改变HttpServletRequest对象的参数。
例如:用filter在HttpServletRequest对象到达Servlet之前将用户输入的空格去掉。但是java.util.Map包装的HttpServletRequest对象的参数是不可改变的。因此需要通过HttpServletRequestWrapper来改变HttpServletRequest对象。

具体使用方法:自定义一个装饰器,并继承HttpServletRequestWrapper。
Spring session便是使用这个机制,自定义了SessionRepositoryRequestWrapper和SessionRepositoryResponseWrapper。
其中SessionRepositoryRequestWrapper的作用就是改变HttpServletRequest里的session。

SessionRepositoryRequestWrapper

SessionRepositoryRequestWrapper的主要功能就是改变HttpServletRequest,将其内部的HttpSession切换至HttpSessionWrapper,这么做的原因是HttpSessionWrapper从表面上看是HttpSession,但其内部实现其实是spring session。

该类的主要方法有getSession()和commitSession(),具体流程图如下所示:
在这里插入图片描述

SessionRepositoryResponseWrapper

该装饰器的作用只有一个:确保response被commit时,session已被保存下来。
实现原理:

  1. 类里有一个成员变量,
private final SessionRepositoryRequestWrapper request;
  1. 重写了onResponseCommitted()方法
@Override
protected void onResponseCommitted() {
	this.request.commitSession();
}

问题:Filter时已经commitSession过,这里为什么还要再commitSession一次?
答案可以去spring-session(一)揭秘上看看,该文的作者大神对这个问题在git上找spring session作者进行了提问并得到了回答。
个人总结的答案:
为了确保response被提交之前seesion已经被创建,这样在提交之前才能将session的一些信息写入到response中(例如在cookie中写入sessionId)。
调用onResponseCommitted()后,会接着调用flushBuffer()方法,将缓存区内的所有数据发送给客户端,因此要确保在这一步签名session已经被提交。


六、repository

类关系图:
在这里插入图片描述

RedisSessionRepository

该类用于在redis中对session进行操作。
在这里插入图片描述

七、总结流程

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值