Spring Web学习-- Spring Session实现原理

       这篇博客我们简单介绍一下对Java web 实现Session共享的几种实现方式,及通过分析Spring Session来看看Spring 对Session共享是如何实现的。

一、Session 共享实现方式

1、通过tomcat的实现机制来实现,简单来说在tomcat容器中它完成了对Session的创建和管理等功能,如果能修改这部分代码就可以实现基于tomcat容器的session共享,算是在服务端实现了,tomcat Session管理的博客可以看《Tomcat源码学习--Session创建销毁》,tomcat实现Session共享在github中有开源项目tomcat-redis-session-manager 简单来说tomcat已经提供了扩展机制,让开发人员可以改造其Session管理的模块

2、自己实现HttpSession的实现机制,所有要保存到HttpSession中的数据都自己进行处理,之前所在公司有个项目就是如此实现的。

3、Spring Session提供的Session共享机制,其不再容器服务中进行改造,实在web应用中进行处理的,和第二种实现方式有点类似。

二、Spring Session 共享实现原理

1、引入  Spring Session相关jar

<dependency>
  <groupId>org.springframework.session</groupId>
  <artifactId>spring-session-data-redis</artifactId>
  <version>1.2.1.RELEASE</version>
</dependency>
<dependency>
  <groupId>redis.clients</groupId>
  <artifactId>jedis</artifactId>
  <version>2.8.1</version>
</dependency>

2、注入Spring Session运行需要的Bean

<bean id="redisHttpSessionConfiguration"
      class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration">
    <property name="maxInactiveIntervalInSeconds" value="600"/>
</bean>

<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
    <property name="maxTotal" value="100" />
    <property name="maxIdle" value="10" />
</bean>

<bean id="jedisConnectionFactory"
      class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" destroy-method="destroy">
    <property name="hostName" value="${redis_hostname}"/>
    <property name="port" value="${redis_port}"/>
    <property name="password" value="${redis_pwd}" />
    <property name="timeout" value="3000"/>
    <property name="usePool" value="true"/>
    <property name="poolConfig" ref="jedisPoolConfig"/>
</bean>

3、在web.xml中配置拦截器,为什么要将此Filter在web.xml中配置为第一个Filter的,因为由于Session共享的机制,你不确定其他Filter中是否有使用Session相关的操作,如果存在可能会出现问题。

<filter>
    <filter-name>springSessionRepositoryFilter</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
    <filter-name>springSessionRepositoryFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

4、实现原理

(1)在注入RedisHttpSessionConfiguration时会将SessionRepositoryFilter注入,这个就是Spring Session实现session共享的Filter

@Bean
	public <S extends ExpiringSession> SessionRepositoryFilter<? extends ExpiringSession> springSessionRepositoryFilter(
			SessionRepository<S> sessionRepository) {
		SessionRepositoryFilter<S> sessionRepositoryFilter = new SessionRepositoryFilter<S>(
				sessionRepository);
		sessionRepositoryFilter.setServletContext(this.servletContext);
		if (this.httpSessionStrategy instanceof MultiHttpSessionStrategy) {
			sessionRepositoryFilter.setHttpSessionStrategy(
					(MultiHttpSessionStrategy) this.httpSessionStrategy);
		}
		else {
			sessionRepositoryFilter.setHttpSessionStrategy(this.httpSessionStrategy);
		}
		return sessionRepositoryFilter;
	}

(2)接下来我们看看SessionRepositoryFilter中做了什么处理操作来实现Session共享的,接下来我们看看其doInternalFilter方法,在这个方法中我们看到request和response都做了包装处理,那样我们看看包装类SessionRepositoryRequestWrapper中有关Session获取的方法中做了什么处理。

@Override
	protected void doFilterInternal(HttpServletRequest request,
			HttpServletResponse response, FilterChain filterChain)
					throws ServletException, IOException {
		request.setAttribute(SESSION_REPOSITORY_ATTR, this.sessionRepository);

		SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper(
				request, response, this.servletContext);
		SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper(
				wrappedRequest, response);

		HttpServletRequest strategyRequest = this.httpSessionStrategy
				.wrapRequest(wrappedRequest, wrappedResponse);
		HttpServletResponse strategyResponse = this.httpSessionStrategy
				.wrapResponse(wrappedRequest, wrappedResponse);

		try {
			filterChain.doFilter(strategyRequest, strategyResponse);
		}
		finally {
			wrappedRequest.commitSession();
		}
	}

(3)在SessionRepositoryRequestWrapper中根据sessionId获取session方法中看到了从sessionRepository中根据sessionId获取Session,sessionRepository存在实现类RedisOperationsSessionRepository实现了将Session保存到redis的操作(当然还有其他实现类)

private S getSession(String sessionId) {
			S session = SessionRepositoryFilter.this.sessionRepository
					.getSession(sessionId);
			if (session == null) {
				return null;
			}
			session.setLastAccessedTime(System.currentTimeMillis());
			return session;
		}

总结:Spring Session通过创建一个Filter,然后在Filter中对request和response进行包装,在包装的request中重写session保存和获取的操作,完成了将session保存到redis的操作,实现了session共享。

猜你喜欢

转载自blog.csdn.net/qq924862077/article/details/81586695