SpringSession——集成SpringBoot

本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

原文链接:blog.ouyangsihai.cn >> SpringSession——集成SpringBoot

springSession spring 旗下的一个项目,把 servlet 容器实现的 httpSession替换为 springSession,专注于解决 session管理问题。可简单快速且无缝的集成到我们的应用中。本文通过一个案例,使用 SpringBoot来集成 SpringSession,并且使用 Redis作为存储来实践下 SpringSession 的使用。

环境准备

因为需要使用 Redis作为底层 Session的存储介质,实现分布式 session,因此需要安装 Redis

Redis 安装

1、从官网下载最新版的 Redis

2、解压


tar zxvf redis-5.0.0.tar.gz

3、编译测试


 sudo make test

4、编译安装


sudo make install

5、安装问题

如果您之前安装过,重复安装且没有卸载干净的话,会报下面的错


make[1]: *** [test] Error 1 
make: *** [test] Error 2

解决这个错误,执行下面的语句即可:


make distclean 
make 
make test

正确安装姿势如下:

6、启动 Redis
在您的 Redis安装目录下,有 redis-server ,执行该脚本命令:

OK,到这里, Redis的安装工作完毕。

SpringBoot 工程准备

这里我们直接通过 Idea来构建我们的 SpringBoot工程。


File-New-Project : Spring Initializr

OK, SpringBoot 工程准备完毕,这里选择创建的是一个 Web工程。

集成

集成主要是依赖引入,这里需要 redis session的依赖

依赖引入


dependencies
    !--redis 依赖--
    dependency
        groupIdorg.springframework.boot/groupId
        artifactIdspring-boot-starter-data-redis/artifactId
    /dependency
    !--sessions 依赖--
    dependency
        groupIdorg.springframework.session/groupId
        artifactIdspring-session-data-redis/artifactId
    /dependency
/dependencies

配置application.properties


#服务端口
server.port=8080
#redi主机地址
spring.redis.host=localhost
#redis服务端口
spring.redis.port=6379

# spring session使用存储类型,spirngboot默认就是使用redis方式,如果不想用可以填none。
spring.session.store-type=redis

在启动类中加入@EnableRedisHttpSession  注解


@SpringBootApplication
@EnableRedisHttpSession
public class SpringBootSessionApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringBootSessionApplication.class, args);
    }
}

测试

先来编写一个 Controller


/**
 * SessionController
 * 
 * @author: glmapper@leishu
 * @since: 18/11/3 下午3:16
 * @version 1.0
 **/
@Controller
@RequestMapping(value = "/")
public class SessionController {

    @ResponseBody
    @RequestMapping(value = "/session")
    public MapString, Object getSession(HttpServletRequest request) {
        request.getSession().setAttribute("userName", "glmapper");
        MapString, Object map = new HashMap();
        map.put("sessionId", request.getSession().getId());
        return map;
    }

    @ResponseBody
    @RequestMapping(value = "/get")
    public String get(HttpServletRequest request) {
        String userName = (String) request.getSession().getAttribute("userName");
        return userName;
    }
}

测试结果

启动 SpringBoot 工程;然后浏览器中输入地址 http://localhost:8080/session;

这里对应执行的是我们上面 Controller中的第一个方法 getSession,这个方法向 session中设置了一个值。

下面我们执行:http://localhost:8080/get  这里是从 session中取值:

到此, SpringBoot 整合 SpringSession 的过程就完成了。这里我们只是引入了依赖,然后做了简单的配置,那么我们的请求是如何被 SpringSession 处理的呢?从我们一贯的认知来看,对于基于 Servlet规范的容器( SpringBoot 使用的是嵌入式 Tomcat)的应用,请求最先被处理的是 Filter。我们在基于 Spring+SpringMvc这套技术栈开发时,如果我们需要做权限管理,通过会基于 Filter或者拦截器。但是这里貌似我们什么也没做,但是请求确实被 SpringSession处理了。OK,我们来扒一扒。

SpringSession 是如何处理请求的?

上面这张截图想必大家都不陌生,是 SpringBoot的启动日志;上图红色框内的是当前应用注册是 Filter信息,从这里可以看到有个和 session 有关的 Filter:sessionRepositoryFilter;这个 bean对应的类是:


org.springframework.boot.autoconfigure.session.SessionRepositoryFilterConfiguration.ConditionalOnBean=
org.springframework.session.web.http.SessionRepositoryFilter

在这里找到了

这里涉及到 SpringBoot的自动配置,从 spring-boot-autoconfig包下加载 spring-autoconfigure-metadata.properties 配置文件,然后获取所有支持自动配置的信息; SpringSession 也在其中。关于如何加载并且注册不在本文的范畴之内,我们继续来分析 SpringSession的处理过程。

SpringSession 的处理过程

从上面 SpringBoot的启动过程我们找到了处理 session Filter,然后知道了它是通过自动配置的方式被注册到当前的容器并且来处理请求。


@Order(SessionRepositoryFilter.DEFAULT_ORDER)
public class SessionRepositoryFilterS extends Session 
extends OncePerRequestFilter {

SessionRepositoryFilter的定义来看:

  • 1、使用了`Order`,并且配置了一个很小的值(`Integer.MIN_VALUE + 50`),以此来确保`session`的`Filter`在`Filter`链中被优先执行。
  • 2、集成了`OncePerRequestFilter`,确保在一次请求只通过一次`filter`,而不需要重复执行
  • 2、集成了 OncePerRequestFilter,确保在一次请求只通过一次 filter,而不需要重复执行

    为什么 session Filter 要被优先执行呢?因为我们的请求被包装了,如果 SessionRepositoryFilter不优先处理请求,可能会导致后续的请求行为不一致,这里涉及到 springSession无缝替换应用服务器的 request的原理:

  • 1.自定义个`Filter`,实现`doFilter`方法
  • 2.继承 `HttpServletRequestWrapper` 、`HttpServletResponseWrapper` 类,重写`getSession`等相关方法(在这些方法里调用相关的 `session`存储容器操作类)。
  • 3.自定义`request`和`response`类;并把它们分别传递到过滤器链
  • 4.把该`filter`配置到过滤器链的第一个位置上
  • 2.继承 HttpServletRequestWrapper HttpServletResponseWrapper 类,重写 getSession等相关方法(在这些方法里调用相关的 session存储容器操作类)。

    4.把该 filter配置到过滤器链的第一个位置上

    OK,了解了这些背景,我们来跟踪下整个处理流程。

    1、断点到 doFilterInternal

    从这里可以看到 request response类被包装了。

    2、断点到 getSession

    这里是从 Redis中拿我们 session数据的地方

  • 先从我们当前`servlet`容器中去拿,如果拿到则直接返回
  • 去`Redis`中取
    SpringSession:集成SpringBoot

    这里会有一个缓存处理,并非是每次都到`Reids`中去查一次,避免一次与`Reids`的交互。

    • 如果缓存当前应用容器缓存中有,则直接返回当前被缓存的`session`
    • 如果没有,则从请求中获取`sessionId`,并且根据当前`sessionId`去`Reids`中查找`session`数据
    • 更新缓存`session,sessionId,requestedSessionCached`等数据状态
    • 如果缓存当前应用容器缓存中有,则直接返回当前被缓存的 session

      更新缓存 session,sessionId,requestedSessionCached等数据状态

      如果 Redis中有,则更新 session相关信息并返回

      如果 Reids中没有找到,则根据 create 来判断是否创建新的 session

      断点到 readCookieValues

      SpringSession提供了两种保存和传递 SessionId的方式,一种是基于 Cookie的,一种是基于 Header的。 SpringSession中默认使用的是基于 Cookie的方式。 readCookieValues 就是实现如何从 Cookie中获取 sessionId的。

      这个过程其实很简单,先是从 request中获取当前请求携带的所以的 Cookie信息,然后将匹配到的 cookieName “SESSION” Cookie进行解析。

      断点到 RedisOperationsSessionRepository - getSession

      这里是从 Redis中取 session数据的地方

    • 根据`sessionId`从 `Redis`中取到 `entries` 数据
    • 构建 `RedisSession` 并返回
    • 构建 RedisSession 并返回

      断点到 commitSession

      commitSession作用是通过 HttpSessionIdResolver sessionId写到 response,并且进行持久化。

      这里的 session 其实是已经更新过状态的,比如重新设置了 session 的过期时间等。 session 提交实际上就意味着当前请求已经处理完毕了。

      小结

      本文先介绍了如何使用 SpringBoot 集成 SpringSession,并且以 Redis 作为存储。然后简单分析了 SpringSession 的处理过程,本文对 SpringSession 的原理部分没有进行深入分析,下一篇分析下 SpringSession的原理。

      原文始发于微信公众号(glmapper工作室):

    本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

    本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

    原文链接:blog.ouyangsihai.cn >> SpringSession——集成SpringBoot


     上一篇
    Tomcat在SpringBoot中是如何启动的 Tomcat在SpringBoot中是如何启动的
    点击上方“Java知音”,选择“置顶公众号” 技术文章第一时间送达! 出处:木木匠 juejin.im/post/5d3f95ebf265da039e12959e 前言我们知道SpringBoot给我们带来了一个全新的开发体
    下一篇 
    SpringSession系列-sessionId解析和Cookie读写策略 SpringSession系列-sessionId解析和Cookie读写策略
    首先需求在这里说明下,SpringSession的版本迭代的过程中肯定会伴随着一些类的移除和一些类的加入,目前本系列使用的版本是github上对象的master的代码流版本。如果有同学对其他版本中的一些类或者处理有疑惑,欢迎交流。 本篇