一、EnableRedisHttpSession使用

  1. 添加依赖
<!-- spring session的依赖 -->
<dependency><groupId>org.springframework.session</groupId><artifactId>spring-session-data-redis</artifactId>
</dependency>
  1. 添加注解 @EnableRedisHttpSession
/*** session托管到redis*/
@Configuration
@EnableRedisHttpSession(maxInactiveIntervalInSeconds= 3600*24, redisFlushMode = RedisFlushMode.ON_SAVE, redisNamespace = "aurora-web")
public class RedisSessionConfig {}

maxInactiveIntervalInSeconds: 设置 Session 失效时间,使用 Redis Session 之后,原 Spring Boot 的 server.session.timeout 属性不再生效

  1. 经过上面的配置后,Session调用就会自动去Redis存取。另外,想要达到Session共享的目的,只需要在其他的系统上做同样的配置即可。开启spring session的功能就是这么简单,根据定律:使用越简单,源码

二、@EnableRedisHttpSession源码

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({RedisHttpSessionConfiguration.class})
@Configuration
public @interface EnableRedisHttpSession {//Session默认过期时间,单位秒,默认1800秒int maxInactiveIntervalInSeconds() default 1800;//配置key的namespace,默认的是spring:session,如果不同的应用共用一个redis,应该为应用配置不同的namespace,这样才能区分这个Session是来自哪个应用的String redisNamespace() default "spring:session";//配置刷新Redis中Session方式,默认是ON_SAVE模式,只有当Response提交后才会将Session提交到Redis,也可以配置成IMMEDIATE模式,即所有对Session的更改会立即更新到RedisRedisFlushMode redisFlushMode() default RedisFlushMode.ON_SAVE;//清理过期Session的定时任务String cleanupCron() default "0 * * * * *";
}
  1. 其中,@Import({RedisHttpSessionConfiguration.class}) 这个注解的主要作用是注册一个SessionRepositoryFilter,这个Filter会拦截到所有的请求,对Session进行操作,具体的操作细节会在后面讲解,这边主要了解这个注解的作用是注册SessionRepositoryFilter就行了。注入SessionRepositoryFilter的代码在RedisHttpSessionConfiguration这个类中。
@Configuration
@EnableScheduling
public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguration implements BeanClassLoaderAware, EmbeddedValueResolverAware, ImportAware, SchedulingConfigurer {......
}
  1. RedisHttpSessionConfiguration 继承了 SpringHttpSessionConfiguration,SpringHttpSessionConfiguration 中注册了 SessionRepositoryFilter。见下面代码。
@Configuration
public class SpringHttpSessionConfiguration implements ApplicationContextAware {...@Beanpublic <S extends Session> SessionRepositoryFilter<? extends Session> springSessionRepositoryFilter(SessionRepository<S> sessionRepository) {SessionRepositoryFilter<S> sessionRepositoryFilter = new SessionRepositoryFilter<>(sessionRepository);sessionRepositoryFilter.setServletContext(this.servletContext);sessionRepositoryFilter.setHttpSessionIdResolver(this.httpSessionIdResolver);return sessionRepositoryFilter;}...
}
  1. 我们发现注册 SessionRepositoryFilter 时需要一个 SessionRepository 参数,这个参数是在 RedisHttpSessionConfiguration 中被注入进入的。
@Configuration
@EnableScheduling
public class RedisHttpSessionConfiguration extends SpringHttpSessionConfigurationimplements BeanClassLoaderAware, EmbeddedValueResolverAware, ImportAware,SchedulingConfigurer {@Beanpublic RedisOperationsSessionRepository sessionRepository() {RedisTemplate<Object, Object> redisTemplate = createRedisTemplate();RedisOperationsSessionRepository sessionRepository = new RedisOperationsSessionRepository(redisTemplate);sessionRepository.setApplicationEventPublisher(this.applicationEventPublisher);if (this.defaultRedisSerializer != null) {sessionRepository.setDefaultSerializer(this.defaultRedisSerializer);}sessionRepository.setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds);if (StringUtils.hasText(this.redisNamespace)) {sessionRepository.setRedisKeyNamespace(this.redisNamespace);}sessionRepository.setRedisFlushMode(this.redisFlushMode);int database = resolveDatabase();sessionRepository.setDatabase(database);return sessionRepository;}
}

上面主要讲的就是 Spring-Session 会自动注册一个 SessionRepositoryFilter ,这个过滤器会拦截所有的请求。下面就具体看下这个过滤器对拦截下来的请求做了哪些操作。SessionRepositoryFilter 拦截到请求后,会先将 request 和 response 对象转换成 Spring 内部的包装类 SessionRepositoryRequestWrapper 和 SessionRepositoryResponseWrapper 对象。SessionRepositoryRequestWrapper 类重写了原生的getSession方法

@Override
public HttpSessionWrapper getSession(boolean create) {//通过request的getAttribue方法查找CURRENT_SESSION属性,有直接返回HttpSessionWrapper currentSession = getCurrentSession();if (currentSession != null) {return currentSession;}//查找客户端中一个叫SESSION的cookie,通过sessionRepository对象根据SESSIONID去Redis中查找SessionS requestedSession = getRequestedSession();if (requestedSession != null) {if (getAttribute(INVALID_SESSION_ID_ATTR) == null) {requestedSession.setLastAccessedTime(Instant.now());this.requestedSessionIdValid = true;currentSession = new HttpSessionWrapper(requestedSession, getServletContext());currentSession.setNew(false);//将Session设置到request属性中setCurrentSession(currentSession);//返回Sessionreturn currentSession;}}else {// This is an invalid session id. No need to ask again if// request.getSession is invoked for the duration of this requestif (SESSION_LOGGER.isDebugEnabled()) {SESSION_LOGGER.debug("No session found by id: Caching result for getSession(false) for this HttpServletRequest.");}setAttribute(INVALID_SESSION_ID_ATTR, "true");}//不创建Session就直接返回nullif (!create) {return null;}if (SESSION_LOGGER.isDebugEnabled()) {SESSION_LOGGER.debug("A new session was created. To help you troubleshoot where the session was created we provided a StackTrace (this is not an error). You can prevent this from appearing by disabling DEBUG logging for "+ SESSION_LOGGER_NAME,new RuntimeException("For debugging purposes only (not an error)"));}//通过sessionRepository创建RedisSession这个对象,可以看下这个类的源代码,如果//@EnableRedisHttpSession这个注解中的redisFlushMode模式配置为IMMEDIATE模式,会立即将创建的RedisSession同步到Redis中去。默认是不会立即同步的。S session = SessionRepositoryFilter.this.sessionRepository.createSession();session.setLastAccessedTime(Instant.now());currentSession = new HttpSessionWrapper(session, getServletContext());setCurrentSession(currentSession);return currentSession;
}

这里关于springSession 的redis存储模式,可以参考文章

当调用 SessionRepositoryRequestWrapper 对象的getSession方法拿 Session 的时候,会先从当前请求的属性中查找CURRENT_SESSION属性,如果能拿到直接返回,这样操作能减少Redis操作,提升性能。到现在为止我们发现如果redisFlushMode配置为 ON_SAVE 模式的话,Session 信息还没被保存到 Redis 中,那么这个同步操作到底是在哪里执行的呢?仔细看代码,我们`发现 SessionRepositoryFilter 的doFilterInternal方法最后有一个 finally 代码块,这个代码块的功能就是将 Session同步到 Redis。

@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);try {filterChain.doFilter(wrappedRequest, wrappedResponse);}finally {//将Session同步到Redis,同时这个方法还会将当前的SESSIONID写到cookie中去,同时还会发布一//SESSION创建事件到队列里面去wrappedRequest.commitSession();}
}

三、简单总结

  1. 主要的核心类有
  • @EnableRedisHttpSession:开启 Session 共享功能;
  • RedisHttpSessionConfiguration:配置类,一般不需要我们自己配置,主要功能是配置
  • SessionRepositoryFilter 和 RedisOperationsSessionRepository 这两个Bean;
  • SessionRepositoryFilter:拦截器,Spring-Session 框架的核心;
  • RedisOperationsSessionRepository:可以认为是一个 Redis 操作的客户端,有在 Redis 中进行增删改查Session 的功能
  • SessionRepositoryRequestWrapper:Request的包装类,主要是重写了getSession方法
  • SessionRepositoryResponseWrapper:Response的包装类。
  1. 原理简要总结:

当请求进来的时候,SessionRepositoryFilter 会先拦截到请求,将 request 和 response 对象转换成 SessionRepositoryRequestWrapper 和 SessionRepositoryResponseWrapper。后续当第一次调用 request 的getSession方法时,会调用到 SessionRepositoryRequestWrapper的getSession方法。这个方法是被重写过的,逻辑是先从 request的属性中查找,如果找不到;再查找一个key值是"SESSION"的 Cookie,通过这个 Cookie 拿到 SessionId 去Redis 中查找,如果查不到,就直接创建一个RedisSession 对象,同步到 Redis 中。

说的简单点就是:拦截请求,将之前在服务器内存中进行 Session 创建销毁的动作,改成在 Redis 中创建。

参考文章
参考文章
我见过的最详细的文章

从@EnableRedisHttpSession谈谈Spring Session实现原理相关推荐

  1. Spring Session实战3

    我们先把nginx的节点只配置一个tomcat,首先来到我们的命令行,修改我们的happymall.com.conf 现在负载均衡两个节点,我们把第二个节点先注释上 然后reload一下./nginx ...

  2. Spring Data JPA 原理与实战第十一天 Session相关、CompletableFuture、LazyInitializationException

    22 Session 的 open-in-view 对事务的影响是什么? 你好,欢迎来到第 22 讲,今天我们来学习 Session 的相关内容. 当我们使用 Spring Boot 加 JPA 的时 ...

  3. 利用spring session解决共享Session问题

    https://blog.csdn.net/patrickyoung6625/article/details/45694157 1.共享Session问题 HttpSession是通过Servlet容 ...

  4. Spring-Session实现session共享原理及解析

    写在前面 Session简介 是什么? Session在网络中表示"会话控制",用于存储特定用户所需的属性和其他的配置信息:Session表示一个特定的时间间隔,可以指用户从登陆系 ...

  5. 面试官:说说Spring Cloud底层原理?

    点击上方"蓝字", 右上角选择"设为星标" 周一至周五上午11:45!精品文章准时送上! 本文转载自公众号:石杉的架构笔记 目录 一.业务场景介绍 二.Spri ...

  6. Java程序员进阶——Spring依赖注入原理分析

    Spring依赖注入原理分析 下面谈谈Spring是如何实现反转模式IOC或依赖注入模式DI: 平时,我们需要生成一个对象,使用new语法,如一个类为A public class A{public v ...

  7. 面试请不要再问我Spring Cloud底层原理

    目录 一.业务场景介绍 二.Spring Cloud核心组件:Eureka 三.Spring Cloud核心组件:Feign 四.Spring Cloud核心组件:Ribbon 五.Spring Cl ...

  8. [译]Spring Session 与 Spring Security

    原文:http://docs.spring.io/spring-session/docs/current-SNAPSHOT/reference/html5/guides/security.html 本 ...

  9. Spring Session - 使用Spring Session从零到一构建分布式session

    文章目录 快速入门 Spring Session + Redis 官网指导 Demo pom 依赖 配置文件 配置类RedisHttpSessionConfiguration Redis中的sessi ...

最新文章

  1. java 中对象引用,以及对象赋值
  2. 查看登录oracle信息,记录Oracle用户的登录信息
  3. Linux线程(六)
  4. param.requires_grad = False的作用
  5. Lable 换行动态计算高度(2种)
  6. Graphics.DrawRectangle Method(矩形)
  7. CrossOver 12 发布,Windows 模拟器
  8. 读者福利:复盘2018上半年精选文章,还有礼品等着你!
  9. 舆情监控系统python开源_开源舆情监控系统
  10. Echarts官网突然不能登录了?
  11. Java 中的三大特性(超详细篇)
  12. Mac环境下安装、配置liteide
  13. Windows10自带应用的卸载和恢复
  14. 计算摄影: 高动态范围成像
  15. 百度账号 不用手机号注册
  16. 报名啦!第四届滴滴-IEEE未来精英论坛今夏来袭
  17. 菜鸟实战UML——包图
  18. 机器学习期末考试满分试卷答案
  19. 原创 | 使用JUnit、AssertJ和Mockito编写单元测试和实践TDD (六)测试哪些内容:Right-BICEP
  20. 计算机三级信息安全技术知识点总结(6)

热门文章

  1. 如何在RCP程序中添加一个banner栏
  2. Unity—AssetBundle的打包及四种加载资源方式
  3. html js绑定键盘按键触发事件(按回车键登陆)
  4. Yii Model中添加默认搜索条件
  5. 掌握 Ajax,第 8 部分: 在请求和响应中使用 XML
  6. c++学习笔记之运算符的重载
  7. Matlab:精度控制
  8. Python学习笔记:Day15 部署Web App
  9. python调用可执行文件
  10. linux中系统调用和库函数的区别