Redis存储Session启动顺序
@EnableRedisHttpSession-注解 → 进入RedisHttpSessionConfiguration类
主要看RedisHttpSessionConfiguration类
image.png- 该类实现ImportAware接口,所以先执行 setImportMetadata 方法
- 注入 RedisTemplate
当bean中需要注入其他参数或者引用时,将其作为方法的参数即可,Spring会帮你注入这些引用。这里RedisConnectionFactory被作为参数被注入进来
- 注入 RedisOperationsSessionRepository
此方法的返回值是RedisOperationsSessionRepository,有关于session持久化到redis的相关操作都在此类中;
注:持久化到redis只是spring-session的一种方式,也支持持久化到其他数据库中(jdbc,Mongo,Hazelcast等)
3.1 注入父类的 SessionRepositoryFilter过滤器
所有的请求都会先经过SessionRepositoryFilter过滤器,doFilter方法如下:
request被包装成了SessionRepositoryRequestWrapper对象,response被包装成了SessionRepositoryResponseWrapper对象,SessionRepositoryRequestWrapper中重写了getSession等方法;finally中执行了commitSession方法,将session进行持久化操作;
3.2 注入父类的 SessionRepositoryRequestWrapper包装类
重点看一下重写的getSession方法,代码如下:
@Override
public HttpSessionWrapper getSession(boolean create) {
// 从当前请求的attribute中获取session,如果有直接返回
HttpSessionWrapper currentSession = getCurrentSession();
if (currentSession != null) {
return currentSession;
}
// 获取当前request的sessionId,这里使用了HttpSessionStrategy
// 决定怎样将Request映射至Session,默认使用Cookie策略,即从cookies中解析sessionId
String requestedSessionId = getRequestedSessionId();
// 请求的如果sessionId存在且当前request的attribute中的没有session失效属性
// 则根据sessionId获取spring session
if (requestedSessionId != null
&& getAttribute(INVALID_SESSION_ID_ATTR) == null) {
S session = getSession(requestedSessionId);
// 如果spring session不为空,则将spring session包装成HttpSession并
// 设置到当前Request的attribute中,防止同一个request getsession时频繁的到存储器
//中获取session,提高性能
if (session != null) {
this.requestedSessionIdValid = true;
currentSession = new HttpSessionWrapper(session, getServletContext());
currentSession.setNew(false);
setCurrentSession(currentSession);
return currentSession;
}
// 如果根据sessionId,没有获取到session,则设置当前request属性,此sessionId无效
// 同一个请求中获取session,直接返回无效
else {
// This is an invalid session id. No need to ask again if
// request.getSession is invoked for the duration of this request
if (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
if (!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创建spring session
S session = SessionRepositoryFilter.this.sessionRepository.createSession();
// 设置session的最新访问时间
session.setLastAccessedTime(System.currentTimeMillis());
// 包装成HttpSession透明化集成
currentSession = new HttpSessionWrapper(session, getServletContext());
// 设置session至Requset的attribute中,提高同一个request访问session的性能
setCurrentSession(currentSession);
return currentSession;
}
大致分为三步,① 首先去本地内存中获取session ② 如果获取不到去指定的数据库中获取,这里其实就是去redis里面获取,sessionRepository就是上面定义的RedisOperationsSessionRepository对象;③ 如果redis里面也没有则创建一个新的session;
- 注入 RedisMessageListenerContainer
- 注入 EnableRedisKeyspaceNotificationsInitializer
- 注入父类的 SessionEventHttpSessionListenerAdapter
RedisOperationsSessionRepository类,关于session的保存,更新,删除,获取操作都在此类中;