要了解SpringSession首先要知道什么事HttpSession以及其存在的问题:
什么是 HttpSession
是 JavaWeb 服务端提供的用来建立与客户端会话状态的对象。
什么是 Session 共享
是指在一个浏览器访问多个 Web 服务时,服务端的 Session 数据需要共享。正常情况下同一页面多次访问不同的服务器,不同的session是不同的
Session 共享常见的解决方案
Session 复制
通过对应用服务器的配置开启服务器的 Session 复制功能,在集群中的几台服务器之间同步 Session 对象,使得每台服务器上都保存所有的 Session 信息,这样任何一台宕机都不会导致 Session 的数据丢失,服务器使用 Session 时,直接从本地获取。这种方式的缺点也比较明显。因为 Session 需要时时同步,并且同步过程是有应用服务器来完成,由此对服务器的性能损耗也比较大。
Session 绑定
利用 hash 算法,比如 nginx 的 ip_hash,使得同一个 Ip 的请求分发到同一台服务器上。 这种方式不符合对系统的高可用要求,因为一旦某台服务器宕机,那么该机器上的 Session 也就不复存在了,用户请求切换到其他机器后么有 Session,无法完成业务处理。
利用 Cookie 记录 Session
Session 记录在客户端,每次请求服务器的时候,将 Session 放在请求中发送给服务器, 服务器处理完请求后再将修改后的 Session 响应给客户端。这里的客户端就是 cookie。 利用 cookie 记录 Session 的也有缺点,比如受 cookie 大小的限制,能记录的信息有限, 安全性低,每次请求响应都需要传递 cookie,影响性能,如果用户关闭 cookie,访问就不正常。
Session 服务器
Session 服务器可以解决上面的所有的问题,利用独立部署的 Session 服务器统一管理 Session,服务器每次读写 Session 时,都访问 Session 服务器。 对于 Session 服务器,我们可以使用 Redis 或者 MongoDB 等内存数据库来保存 Session 中的数据,以此替换掉服务中的 HttpSession。达到 Session 共享的效果。
SpringSession采用的是独立部署Session服务器的方式;
SPringMvc如何实现SpringSession
· 1.首先,要在Web.xml中配置相应的拦截器,拦截器的名称必须为springSessionRepositoryFilter,并根据实际情况配置对应的路径,图中国的DelegatingFilterProxy是fileter的代理类,具体的作用和实现逻辑请自行百度,本文不做详细说明
2.在对应的spring配置文件中声明SpringHttpSessionConfiguration或者其子类,在该类中声明了SessionRepositoryFilter类,该类就是DelegatingFilterProxy所代理的实际对象,下面的redisHttpSessionConfiguration是SpringHttpSessionConfiguration的子类,该类初始化返回的是RedisOperationsSessionRepository,从名字就可以看出,其返回的是redis的操作类,内部封装了redis操作的常用方法
<bean id="redisHttpSessionConfiguration" class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration"> <property name="maxInactiveIntervalInSeconds" value="1800" /> <property name="cookieSerializer" ref="defaultCookieSerializer"/> </bean>
defaultCookieSerializer配置了Sesion的序列化方式,包括domainName,DefaultCookieSerializer仅支持单个域名,如果需要拓展多域名需要重写器writeCookie方法
<bean id="defaultCookieSerializer" class="org.springframework.session.web.http.DefaultCookieSerializer"> <property name="domainName" value="**.com"/> </bean>
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig"> </bean> <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"> <property name="hostName" value="${redis.host}" /> <property name="port" value="${redis.port}" /> <property name="password" value="${redis.pass}" /> <property name="timeout" value="${redis.timeout}" /> <property name="poolConfig" ref="jedisPoolConfig" /> <property name="usePool" value="true" /> </bean> <bean id="redisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate"> <property name="connectionFactory" ref="jedisConnectionFactory" /> </bean>
不知你发现没有,在redisHttpSessionConfiguration,没有配置任何redis的链接信息,那么redisHttpSessionConfiguration是如何读取到redis信息的呢?还有我们配置了jedisConnectionFactory和redisTemplate, redisHttpSessionConfiguration使用的是哪个bean的信息呢?
下图是redisHttpSessionConfiguration中的方法,该方方法动态初始化一个bean 叫RedisTemplate,入参是RedisConnectionFactory接口,而我们配置的jedisConnectionFactory实现了该接口,spring会自动的判断容器中接口的实现类,进行注入, 如果存在多个实现类,那么启动时系统会报错
springBoot中实现SpringSession将变得更简单,因为SpringBoot将上述过程进行了包装,我们需要做的包括
1.添加spring-session依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> </dependency>
2.在springBoot的配置文件中配置SpringSession
server.port=8090 server.servlet.context-path=/sbe spring.redis.host=localhost
spring.session.store-type=redis
拓展:
在实际的应用中,我们有时需要将RedisHttpSessionConfiguration,替换成自己的配置类,从而将Session管理交给我们自定义的session服务,了解原理,其实一切都不再复杂,合理的利用继承,用自己写的类继承原始的类或者实现某些接口,重写方法来达到自己的目的。