Spring-data-redis 的实现原理

强烈建议阅读Spring-session原理的"2.spring-session重写servlet request 及 redis实现存储相关问题"

另外在写作中,适当参考了下面的文章。它们的分析并不完全符合本文的场景,但都有值得参考之处

  1. https://blog.csdn.net/u010648555/article/details/79491988#2SessionRepositoryFilterFilterChain_201
  2. https://juejin.im/post/5cb4a34f6fb9a0688360fd91#heading-3

1. redis session的作用来源:SessionRepositoryFilter

我们知道,引入spring-data-redis后,request.getSession()的行为将会从redis中寻找,为什么会这样呢?简单来说,这是因为SessionRepositoryFilter被添加到Servlet拦截链,将request和response替换了。
它是这样起作用的:

  1. 该类通过建立Filter,继承SessionRepositoryRequestWrapper和SessionRepositoryResponseWrapper得到两个自定义类
  2. 在拦截链调用前就把Request和Reponse替换为自己的两个自定义类。
  3. 之后的request和response的调用,其行为就会改为在redis中查找

2. SessionRepositoryFilter的生成

SessionRepositoryFilter是如何被生成、加入拦截链的呢?
参考https://zhuanlan.zhihu.com/p/75776430,一切得从@EnableRedisHttpSession讲起:

@Import(RedisHttpSessionConfiguration.class)
@Configuration
public @interface EnableRedisHttpSession {

@EnableRedisHttpSession直接引入了RedisHttpSessionConfiguration。

2.1 RedisHttpSessionConfiguration

看下该类的定义:

@Configuration
@EnableScheduling
public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguration ...{
    @Bean
    public RedisOperationsSessionRepository sessionRepository() {
        RedisTemplate redisTemplate = createRedisTemplate();
        RedisOperationsSessionRepository sessionRepository = new RedisOperationsSessionRepository(
                redisTemplate);
        ...
        return sessionRepository;
    }

上面两张图暴露两个信息:

  1. RedisHttpSessionConfiguration是SpringHttpSessionConfiguration的子类,后者也会在类里定义一些Bean。
  2. 该类会注册一个RedisOperationsSessionRepository类型的Bean,而根据上图2,该类也是一个SessionRepository。注意,因为这一点,这个Bean将会在之后被注入到其它Bean中。

跟着第一点的思路,我们看下父类SpringHttpSessionConfiguration。生成了SessionRepositoryFilter,我们看下父类:

/**
 * Configures the basics for setting up Spring Session in a web environment. In order to
 * use it, you must provide a {@link SessionRepository}. For example:
  ...
 */
@Configuration
public class SpringHttpSessionConfiguration implements ApplicationContextAware {

  @Bean
  public  SessionRepositoryFilter springSessionRepositoryFilter(
        SessionRepository sessionRepository) {
      SessionRepositoryFilter sessionRepositoryFilter = new SessionRepositoryFilter<>(
              sessionRepository);
      ...
      return sessionRepositoryFilter;
  }
...

可知,该方法会接受springboot自动注入的SessionRepository为参数,并以此构建SessionRepositoryFilter。而回忆前面提到的,这个参数就是RedisHttpSessionConfiguration中提供的RedisOperationsSessionRepository。

这一章的总结了包含了RedisOperationsSessionRepository的SessionRepositoryFilter,是如何被生成为Bean的。我们之后会提到,这个Bean是如何加入拦截链的

3. SessionRepositoryFilter加入拦截链

首先,spring-boot-autoconfigure里的spring.factories里有提到SessionAutoConfiguration。springboot就会自动加载该类(不知道为什么会自动加载的话,建议先百度springboot自动加载原理)。


image.png

我们看下该类的定义:

@Configuration
...
public class SessionAutoConfiguration {
    ...
    @Import({ ServletSessionRepositoryValidator.class, SessionRepositoryFilterConfiguration.class })
    static class ServletSessionConfiguration {

其中的@Import...导入了SessionRepositoryFilterConfiguration。看下定义:

@Configuration
@ConditionalOnBean(SessionRepositoryFilter.class)
@EnableConfigurationProperties(SessionProperties.class)
class SessionRepositoryFilterConfiguration {

    @Bean
    public FilterRegistrationBean> sessionRepositoryFilterRegistration(
            SessionProperties sessionProperties, SessionRepositoryFilter filter) {  // 接受springboot的注入
        FilterRegistrationBean> registration = new FilterRegistrationBean<>(filter);  // 以filter为参数
        ...
        return registration;
    }

SessionRepositoryFilterConfiguration是一个@Configuration,我们分析下这个类:

  1. 该类有这@ConditionalOnBean(SessionRepositoryFilter.class)标记,说明该类会在SessionRepositoryFilter准备好时才被处理
  2. sessionRepositoryFilterRegistration方法会接受SessionRepositoryFilter类型的自动注入。这个类型的Bean在@EnableRedisHttpSession的作用下已经生成。
  3. sessionRepositoryFilterRegistration方法会生成FilterRegistrationBean这个Bean。这个Bean值得研究。

在此处debug,会得到以下信息,供读者参考:


接下来我们要分析FilterRegistrationBean。

3.1 FilterRegistrationBean

根据Servlet3.0规范,这种Bean会被自动调用onStartup方法。这个方法的行为是把内部的filter注册,而上文提到这里的filter就是SessionRepositoryFilter。

关于Servlet3.0,可参考https://www.cnblogs.com/duanxz/archive/2012/10/25/2738173.html

在spring boot中添加自己的Servlet有两种方法,代码注册Servlet和注解自动注册(Filter和Listener也是如此)。

  1. 代码注册通过ServletRegistrationBean、 FilterRegistrationBean 和 ServletListenerRegistrationBean 获得控制。
  2. 也可以通过实现 ServletContextInitializer 接口直接注册。
    在 SpringBootApplication 上使用@ServletComponentScan 注解后,Servlet、Filter、Listener 可以直接通过 @WebServlet、@WebFilter、@WebListener 注解自动注册,无需其他代码。

之后,ServletRegistrationBean::onStartup到底是如何被调用的呢?它内部又是如何注册filter的呢?这就不是本文需要关注的了。我们只需要知道,SessionRepositoryFilter在此会被加入拦截链,而它之后又会在拦截链中替换Request、Response,从而更改Session的行为是查看redis

你可能感兴趣的:(Spring-data-redis 的实现原理)