`

【转】spring session redis 整合

 
阅读更多

【转】http://blog.csdn.net/xiejx618/article/details/42919327

 

参考资料:

http://projects.spring.io/spring-session/#quick-start
http://docs.spring.io/spring-session/docs/current-SNAPSHOT/reference/html5/guides/httpsession.html#httpsession-sample

spring session提供以下功能:
1.API and implementations for managing a user's session
2.HttpSession - allows replacing the HttpSession in an application container (i.e. Tomcat) neutral way
2.1.Clustered Sessions - Spring Session makes it trivial to support clustered sessions without being tied to an application container specific solution.
2.2.Multiple Browser Sessions - Spring Session supports managing multiple users' sessions in a single browser instance (i.e. multiple authenticated accounts similar to Google).
2.3.RESTful APIs - Spring Session allows providing session ids in headers to work with RESTful APIs
3.WebSocket - provides the ability to keep the HttpSession alive when receiving WebSocket messages

仅是集群session功能,都是振奋人心的.spring session是通过filter嵌入去实现的(spring security也是使用这种方式),下面是个例子.

1.主要依赖

 

[html] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. <dependency>  
  2.     <groupId>org.springframework.data</groupId>  
  3.     <artifactId>spring-data-redis</artifactId>  
  4.     <version>1.4.1.RELEASE</version>  
  5. </dependency>  
  6. <dependency>  
  7.     <groupId>redis.clients</groupId>  
  8.     <artifactId>jedis</artifactId>  
  9.     <version>2.5.2</version>  
  10. </dependency>  
  11. <dependency>  
  12.     <groupId>org.springframework.session</groupId>  
  13.     <artifactId>spring-session</artifactId>  
  14.     <version>${spring.session.version}</version>  
  15. </dependency>  

2.写一个configuration来启用RedisHttpSession,在这个配置注册一个redis客户端的连接工厂Bean,供Spring Session用于与redis服务端交互.

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. package org.exam.config;  
  2. import org.springframework.context.annotation.Bean;  
  3. import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;  
  4. import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;  
  5. /** 
  6.  * Created by xin on 15/1/20. 
  7.  */  
  8. @EnableRedisHttpSession  
  9. public class SessionConfig {  
  10.     @Bean  
  11.     public JedisConnectionFactory connectionFactory() {  
  12.         return new JedisConnectionFactory();  
  13.     }  
  14. }  

3.写一个Initializer,主要用于向应用容器添加springSessionRepositoryFilter,顺便注册一下HttpSessionEventPublisher监听,这个监听的作用发布HttpSessionCreatedEvent和HttpSessionDestroyedEvent事件

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. package org.exam.config;  
  2. import org.springframework.session.web.context.AbstractHttpSessionApplicationInitializer;  
  3. /** 
  4.  * Created by xin on 15/1/20. 
  5.  */  
  6. public class SessionApplicationInitializer extends AbstractHttpSessionApplicationInitializer {  
  7.     @Override  
  8.     protected void afterSessionRepositoryFilter(ServletContext servletContext) {  
  9.         servletContext.addListener(new HttpSessionEventPublisher());  
  10.     }  
  11. }  
4.将SessionConfig加入到org.exam.config.DispatcherServletInitializer#getRootConfigClasses,不要加到ServletConfigClasses,至于原因看http://blog.csdn.net/xiejx618/article/details/50603758文末
[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. @Override  
  2. protected Class<?>[] getRootConfigClasses() {  
  3.     return new Class<?>[] {AppConfig.class,SessionConfig.class};  
  4. }  

5.使用例子.

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. package org.exam.web;  
  2. import org.springframework.stereotype.Controller;  
  3. import org.springframework.ui.Model;  
  4. import org.springframework.web.bind.annotation.RequestMapping;  
  5. import javax.servlet.http.HttpServletRequest;  
  6. import javax.servlet.http.HttpSession;  
  7. /** 
  8.  * Created by xin on 15/1/7. 
  9.  */  
  10. @Controller  
  11. public class DefaultController {  
  12.     @RequestMapping("/")  
  13.     public String index(Model model,HttpServletRequest request,String action,String msg){  
  14.         HttpSession session=request.getSession();  
  15.         if ("set".equals(action)){  
  16.             session.setAttribute("msg", msg);  
  17.         }else if ("get".equals(action)){  
  18.             String message=(String)session.getAttribute("msg");  
  19.             model.addAttribute("msgFromRedis",message);  
  20.         }  
  21.         return "index";  
  22.     }  
  23. }  

得到这个被spring session包装过的session,像平常一样直接使用.
6.测试.先启动redis服务端.
请求:localhost:8080/testweb/?action=set&msg=123   把123通过spring session set到redis去.
请求:localhost:8080/testweb/?action=get 从redis取出刚才存入的值.

Redis删除存入去相关的值,再次请求localhost:8080/testweb/?action=get查看结果

 

redis:

a.查询所有key:keys命令,keys *

b.根据某个key删除,使用del命令

源码例子:

http://download.csdn.net/detail/xiejx618/9369518

 

使用redis集群的一个例子:

 

[html] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. <dependency>  
  2.     <groupId>org.springframework.data</groupId>  
  3.     <artifactId>spring-data-redis</artifactId>  
  4.     <version>1.7.1.RELEASE</version>  
  5. </dependency>  
  6. <dependency>  
  7.     <groupId>org.apache.commons</groupId>  
  8.     <artifactId>commons-pool2</artifactId>  
  9.     <version>2.4.2</version>  
  10. </dependency>  
  11. <dependency>  
  12.     <groupId>redis.clients</groupId>  
  13.     <artifactId>jedis</artifactId>  
  14.     <version>2.8.1</version>  
  15. </dependency>  
  16. <dependency>  
  17.     <groupId>org.springframework.session</groupId>  
  18.     <artifactId>spring-session</artifactId>  
  19.     <version>1.1.1.RELEASE</version>  
  20. </dependency>  

 

[plain] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. #REDIS START  
  2. redis.maxRedirections=10  
  3. redis.maxWaitMillis=1500  
  4. redis.maxTotal=2048  
  5. redis.minIdle=20  
  6. redis.maxIdle=200  
  7. redis.jedisClusterNodes=192.168.1.250:6380,192.168.1.250:6381,192.168.1.250:6382  
  8. #REDIS END  

 

 

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. @Configuration  
  2. @EnableRedisHttpSession  
  3. public class HttpSessionConfig implements EnvironmentAware {  
  4.     private Environment env;  
  5.     @Bean  
  6.     public JedisConnectionFactory jedisConnectionFactory() {  
  7.         String[] jedisClusterNodes = env.getProperty("redis.jedisClusterNodes").split(",");  
  8.         RedisClusterConfiguration clusterConfig=new RedisClusterConfiguration(Arrays.asList(jedisClusterNodes));  
  9.         clusterConfig.setMaxRedirects(env.getProperty("redis.maxRedirections",Integer.class));  
  10.   
  11.         JedisPoolConfig poolConfig=new JedisPoolConfig();  
  12.         poolConfig.setMaxWaitMillis(env.getProperty("redis.maxWaitMillis",Integer.class));  
  13.         poolConfig.setMaxTotal(env.getProperty("redis.maxTotal",Integer.class));  
  14.         poolConfig.setMinIdle(env.getProperty("redis.minIdle",Integer.class));  
  15.         poolConfig.setMaxIdle(env.getProperty("redis.maxIdle",Integer.class));  
  16.   
  17.         return new JedisConnectionFactory(clusterConfig,poolConfig);  
  18.     }  
  19.   
  20.     @Override  
  21.     public void setEnvironment(Environment environment) {  
  22.         this.env=environment;  
  23.     }  
  24. }  



 

 

 

下面顺便跟踪下实现吧:

1.注册springSessionRepositoryFilter位置在:org.springframework.session.web.context.AbstractHttpSessionApplicationInitializer#insertSessionRepositoryFilter,从org.springframework.web.filter.DelegatingFilterProxy#initDelegate可以看出会去找名为springSessionRepositoryFilter Bean的实现作为Filter的具体实现.
2.因为使用了@EnableRedisHttpSession,就会使用org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration,这个配置里注册的springSessionRepositoryFilter Bean就是SessionRepositoryFilter.即springSessionRepositoryFilter的实现为org.springframework.session.web.http.SessionRepositoryFilter
3.Filter每一次的请求都会调用doFilter,即调用SessionRepositoryFilter的父类OncePerRequestFilter的doFilter,此方法会调用SessionRepositoryFilter自身的doFilterInternal.这个方法如下:

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {  
  2.     request.setAttribute(SESSION_REPOSITORY_ATTR, sessionRepository);  
  3.     SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper(request, response);  
  4.     SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper(wrappedRequest,response);  
  5.     HttpServletRequest strategyRequest = httpSessionStrategy.wrapRequest(wrappedRequest, wrappedResponse);  
  6.     HttpServletResponse strategyResponse = httpSessionStrategy.wrapResponse(wrappedRequest, wrappedResponse);  
  7.     try {  
  8.         filterChain.doFilter(strategyRequest, strategyResponse);  
  9.     } finally {  
  10.         wrappedRequest.commitSession();  
  11.     }  
  12. }  

4.从这里就知request经过了包装,httpSessionStrategy的默认值是new CookieHttpSessionStrategy(),可以猜测它结合了cookie来实现,当然里面的getSession方法也重写了.org.springframework.session.web.http.SessionRepositoryFilter.SessionRepositoryRequestWrapper#getSession(boolean)方法如下:

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. public HttpSession getSession(boolean create) {  
  2.     if(currentSession != null) {  
  3.         return currentSession;  
  4.     }  
  5.     String requestedSessionId = getRequestedSessionId();  
  6.     if(requestedSessionId != null) {  
  7.     S session = sessionRepository.getSession(requestedSessionId);  
  8.         if(session != null) {  
  9.             this.requestedValidSession = true;  
  10.             currentSession = new HttpSessionWrapper(session, getServletContext());  
  11.             currentSession.setNew(false);  
  12.             return currentSession;  
  13.         }  
  14.     }  
  15.     if(!create) {  
  16.         return null;  
  17.     }  
  18.     S session = sessionRepository.createSession();  
  19.     currentSession = new HttpSessionWrapper(session, getServletContext());  
  20.     return currentSession;  
  21. }  

即上面的例子调用getSession会调用此方法来获取Session.而此Session是通过sessionRepository创建的,此处注入的是org.springframework.session.data.redis.RedisOperationsSessionRepository(sessionRepository的注册也是在org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration),而不是应用服务器本身去创建的.

可以继续看看org.springframework.session.data.redis.RedisOperationsSessionRepository#createSession

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. public RedisSession createSession() {  
  2.     RedisSession redisSession = new RedisSession();  
  3.     if(defaultMaxInactiveInterval != null) {  
  4.         redisSession.setMaxInactiveIntervalInSeconds(defaultMaxInactiveInterval);  
  5.     }  
  6.     return redisSession;  
  7. }  

这里new了一个RedisSession,继续看org.springframework.session.data.redis.RedisOperationsSessionRepository.RedisSession#RedisSession()

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. RedisSession() {  
  2.             this(new MapSession());  
  3.             delta.put(CREATION_TIME_ATTR, getCreationTime());  
  4.             delta.put(MAX_INACTIVE_ATTR, getMaxInactiveIntervalInSeconds());  
  5.             delta.put(LAST_ACCESSED_ATTR, getLastAccessedTime());  
  6.         }  
  7.         RedisSession(MapSession cached) {  
  8.             Assert.notNull("MapSession cannot be null");  
  9.             this.cached = cached;  
  10.         }  
  11.            

这里又new了一个MapSession并赋给了cached变量,再看org.springframework.session.MapSession片段:

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. public static final int DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS = 1800;  
  2.   
  3. private String id = UUID.randomUUID().toString();  
  4. private Map<String, Object> sessionAttrs = new HashMap<String, Object>();  
  5. private long creationTime = System.currentTimeMillis();  
  6. private long lastAccessedTime = creationTime;  
  7.   
  8. /** 
  9.  * Defaults to 30 minutes 
  10.  */  
  11. private int maxInactiveInterval = DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS;  

从这里你可以基本猜测id就是sessionid,这个UUID就是区分不同的客户端的一个唯一标识,它会写入到客户端的cookie,session的有效时间是存在什么地方了,cached和delta都有存.最后就要看它怎么保存到redis里面去了.下面再看看如何保存到redis去:response是经过了SessionRepositoryResponseWrapper包装,SessionRepositoryResponseWrapper是OnCommittedResponseWrapper的子类,服务端一旦调用response.getWriter()就会触发org.springframework.session.web.http.OnCommittedResponseWrapper#getWriter

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. @Override  
  2. public PrintWriter getWriter() throws IOException {  
  3.     return new SaveContextPrintWriter(super.getWriter());  
  4. }  
[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. private class SaveContextPrintWriter extends PrintWriter {  
  2.     private final PrintWriter delegate;  
  3.   
  4.     public SaveContextPrintWriter(PrintWriter delegate) {  
  5.         super(delegate);  
  6.         this.delegate = delegate;  
  7.     }  
  8.   
  9.     public void flush() {  
  10.         doOnResponseCommitted();  
  11.         delegate.flush();  
  12.     }  
  13.   
  14.     public void close() {  
  15.         doOnResponseCommitted();  
  16.         delegate.close();  
  17.     }  

一旦调用out.flush或out.close都会触发doOnResponseCommitted()方法,

 

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. private void doOnResponseCommitted() {  
  2.     if(!disableOnCommitted) {  
  3.         onResponseCommitted();  
  4.         disableOnResponseCommitted();  
  5.     } else if(logger.isDebugEnabled()){  
  6.         logger.debug("Skip invoking on");  
  7.     }  
  8. }  

回来org.springframework.session.web.http.SessionRepositoryFilter.SessionRepositoryResponseWrapper#onResponseCommitted

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. @Override  
  2. protected void onResponseCommitted() {  
  3.     request.commitSession();  
  4. }  

再回来org.springframework.session.web.http.SessionRepositoryFilter.SessionRepositoryRequestWrapper#commitSession

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. private void commitSession() {  
  2.     HttpSessionWrapper wrappedSession = currentSession;  
  3.     if(wrappedSession == null) {  
  4.         if(isInvalidateClientSession()) {  
  5.             httpSessionStrategy.onInvalidateSession(this, response);  
  6.         }  
  7.     } else {  
  8.         S session = wrappedSession.session;  
  9.         sessionRepository.save(session);  
  10.         if(!requestedValidSession) {  
  11.             httpSessionStrategy.onNewSession(session, this, response);  
  12.         }  
  13.     }  
  14. }  

终于看到sessionRepository调用save了

分享到:
评论

相关推荐

    springboot 项目,基于springsession整合redis实现登录拦截功能

    1. **整合Spring Session**: 首先,我们需要在Spring Boot项目中引入Spring Session的依赖。这通常通过在`pom.xml`或`build.gradle`文件中添加对应的Maven或Gradle依赖完成。然后,我们需要配置Spring Boot以使用...

    Spring-session2整合spring5+redis

    标题中的“Spring-session2整合spring5+redis”指的是在Spring框架的第五个主要版本(Spring 5)中,集成Spring Session 2与Redis数据库来管理Web应用的会话(Session)。Spring Session是一个开源项目,旨在提供一...

    spring+redis整合

    这个"spring+redis整合"项目展示了如何利用Spring框架和Redis数据库来实现一个高效的、高可用的登录系统,其中会话(Session)由Redis进行管理,同时支持通过Nginx进行负载均衡。以下是关于这一主题的详细知识讲解。...

    spring-session-redis/spring整合redis管理session依赖jar包

    commons-pool2-2.3.jar,jedis-2.8.0.jar,spring-data-redis-1.6.0.RELEASE.jar,spring-session-1.1.1.RELEASE.jar,Spring-data-redis(Version 1.6.0.RC1)中文版.pdf

    spring+redis整合用例

    在IT行业中,Spring框架与Redis的整合是常见的数据存储与缓存解决方案,尤其适用于高并发、数据读写频繁的应用场景。下面将详细讲解这个主题,包括Spring如何与Redis进行集成,以及它们各自的关键特性。 首先,...

    Springboot+SpringSecurity+SpringSession+Redis+Mybatis-Plus+Swwager.zip

    本项目“Springboot+SpringSecurity+SpringSession+Redis+Mybatis-Plus+Swwager”整合了Spring Boot、Spring Security、Spring Session、Redis、Mybatis-Plus以及Swagger等技术,旨在构建一个强大的、安全的、具有...

    spring-boot mybaits spring security redis整合

    注解redis缓存数据,Spring-session和redis实现分布式session同步(建议按功能模块划分系统)。 6、日志 =========== logback打印日志,业务日志和调试日志分开打印。同时基于时间和文件大小分割日志文件。 9、...

    spring-session+spring+redis的依赖包

    本压缩包包含的“spring-session+spring+redis”组合,是将Spring Session与Redis集成,利用Redis作为会话存储介质,以实现高可用性和可扩展性。 首先,我们要了解Spring Session的核心概念。它通过替换默认的...

    redis整合tomcat8

    标题“redis整合tomcat8”涉及的是在Java Web开发中,如何将Redis缓存系统与Tomcat应用服务器集成,以优化session管理。Redis通常用于缓解服务器内存压力,提高Web应用程序的性能,特别是处理大量并发用户时。以下是...

    spring整合redis

    通过这种方式,Spring MVC+Mybatis+Redis的整合能够构建出一个高可用、高性能的分布式应用系统,满足复杂业务场景的需求。在实际项目中,还需要考虑异常处理、事务管理、性能优化等方面,以确保系统的稳定性和可靠性...

    spring session入门

    本教程将带你入门Spring Session,并重点探讨其与Redis的整合。 首先,我们来理解Spring Session的核心概念。传统的HttpSession在单服务器环境下工作良好,但随着微服务和分布式系统的普及,session共享成为挑战。...

    springboot +shiro+redis实现session共享(方案二)1

    "Spring Boot + Shiro + Redis 实现 Session 共享方案二" 1. 概述 本文档旨在介绍如何使用 Spring Boot、Shiro 和 Redis 实现分布式 session 共享,以解决 Web 应用程序的登录 session 统一问题。 2. 相关依赖 ...

    Spring+Struts2+hibernate+Redis整合

    将SSH与Redis整合,可以提升应用程序的性能和响应速度。下面将详细阐述这个整合过程中的关键知识点。 1. **Spring框架**:Spring是企业级Java应用的核心框架,它提供了依赖注入(DI)和面向切面编程(AOP)等核心特性,...

    spring+mybatis+redis整合.docx

    Spring+MyBatis+Redis整合 Title:Spring+MyBatis+Redis整合 Description:Spring+MyBatis+Redis整合 Tag:MyBatis Spring Redis 内容摘要: 本文主要讲述了如何将Spring、MyBatis和Redis三者进行整合,以解决...

    Spring session整合到Redis过程解析

    Spring Session 整合 Redis 过程解析 Spring Session 简介 在传统的单机 Web 应用中,用户的会话 Session 都是由容器管理的。浏览器使用 Cookie 中记录 SessionId,容器根据 SessionId 判断用户是否存在会话 ...

    38. Spring Boot分布式Session状态保存Redis【从零开始学Spring Boot】

    要实现Spring Boot分布式Session与Redis的整合,我们需要以下几个步骤: 1. **添加依赖**: 首先在`pom.xml`或`build.gradle`文件中添加Spring Session和Redis的依赖。对于Maven,可以添加如下依赖: ```xml ...

    shiro+spring+data+session+redis实现单点登录

    在SSO中,Spring可以帮助整合各种组件,如Shiro和Spring Data,同时管理会话状态。 **Spring Data** Spring Data是Spring的一个模块,它简化了数据访问层的开发,支持多种数据存储技术,如JPA、MongoDB、Redis等。...

    SpringBoot2整合Redis多数据源步骤详解

    SpringBoot2整合Redis多数据源是一个非常重要的知识点, especially 在分布式系统中,REDIS作为一个高性能的Key-Value数据库,广泛应用于缓存、Session管理、消息队列等领域。下面我们将详细介绍SpringBoot2整合...

    整合SRPING 基于REDIS共享SESSION

    本篇文章将深入探讨如何整合Spring与Redis,实现基于Redis的Session共享。 首先,我们来理解Spring Session的核心概念。Spring Session是一个开源项目,它扩展了Spring MVC和Spring WebFlux,提供了在分布式环境中...

    基于Shiro、Spring和Redis的Freemarker整合session cluster设计源码

    该项目为基于Shiro、Spring和Redis的Freemarker整合的session cluster设计源码,包含46个文件,涵盖22个Java源文件、5个属性文件、5个Freemarker模板文件、4个XML配置文件、2个JavaScript文件以及其他相关文件。...

Global site tag (gtag.js) - Google Analytics