强烈建议阅读Spring-session原理的"2.spring-session重写servlet request 及 redis实现存储相关问题"
另外在写作中,适当参考了下面的文章。它们的分析并不完全符合本文的场景,但都有值得参考之处:
- https://blog.csdn.net/u010648555/article/details/79491988#2SessionRepositoryFilterFilterChain_201
- https://juejin.im/post/5cb4a34f6fb9a0688360fd91#heading-3
1. redis session的作用来源:SessionRepositoryFilter
我们知道,引入spring-data-redis后,request.getSession()的行为将会从redis中寻找,为什么会这样呢?简单来说,这是因为SessionRepositoryFilter被添加到Servlet拦截链,将request和response替换了。
它是这样起作用的:
- 该类通过建立Filter,继承SessionRepositoryRequestWrapper和SessionRepositoryResponseWrapper得到两个自定义类
- 在拦截链调用前就把Request和Reponse替换为自己的两个自定义类。
- 之后的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
上面两张图暴露两个信息:
- RedisHttpSessionConfiguration是SpringHttpSessionConfiguration的子类,后者也会在类里定义一些Bean。
- 该类会注册一个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 extends Session> 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自动加载原理)。
我们看下该类的定义:
@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
,我们分析下这个类:
- 该类有这
@ConditionalOnBean(SessionRepositoryFilter.class)
标记,说明该类会在SessionRepositoryFilter准备好时才被处理 -
sessionRepositoryFilterRegistration
方法会接受SessionRepositoryFilter类型的自动注入。这个类型的Bean在@EnableRedisHttpSession的作用下已经生成。 -
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也是如此)。
- 代码注册通过ServletRegistrationBean、 FilterRegistrationBean 和 ServletListenerRegistrationBean 获得控制。
- 也可以通过实现 ServletContextInitializer 接口直接注册。
在 SpringBootApplication 上使用@ServletComponentScan 注解后,Servlet、Filter、Listener 可以直接通过 @WebServlet、@WebFilter、@WebListener 注解自动注册,无需其他代码。
之后,ServletRegistrationBean::onStartup到底是如何被调用的呢?它内部又是如何注册filter的呢?这就不是本文需要关注的了。我们只需要知道,SessionRepositoryFilter在此会被加入拦截链,而它之后又会在拦截链中替换Request、Response,从而更改Session的行为是查看redis。