1 依赖
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session</artifactId>
<version>${version.redis.session}</version>
</dependency>
2 使用注解启动spring基于redis的session管理
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 3600, redisNamespace = "sessionNamespace")
其中:sessionNamespace 会变成redis保存session的前缀;
此注解会导入:@Import({RedisHttpSessionConfiguration.class}),RedisHttpSessionConfiguration类会实现相应配置
3 配置RedisHttpSessionConfiguration类分析:
3.1 注册一个RedisOperationsSessionRepository bean到spring中,实现保存session到redis的功能
@Bean
public RedisOperationsSessionRepository sessionRepository(@Qualifier("sessionRedisTemplate") RedisOperations<Object, Object> sessionRedisTemplate, ApplicationEventPublisher applicationEventPublisher) {
RedisOperationsSessionRepository sessionRepository = new RedisOperationsSessionRepository(sessionRedisTemplate);
。。。。。。
return sessionRepository;
}
当然redisTemplate也是配置了
@Bean
public RedisTemplate<Object, Object> sessionRedisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate();
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
if (this.defaultRedisSerializer != null) {
// 使用默认的序列化器
template.setDefaultSerializer(this.defaultRedisSerializer);
}
template.setConnectionFactory(connectionFactory);
return template;
}
3.2 对SessionRepositoryFilter配置:
这个filter也会加入到ServletContext的过滤器执行器链中
RedisHttpSessionConfiguration的父类:SpringHttpSessionConfiguration注册了SessionRepositoryFilter的Bean到spring容器中
@Bean
public <S extends ExpiringSession> SessionRepositoryFilter<? extends ExpiringSession> springSessionRepositoryFilter(SessionRepository<S> sessionRepository) {
// 向SessionRepositoryFilter设置了sessionRepository(实现类是RedisOperationsSessionRepository )
// 所以最终filter的session操作落到redis上
SessionRepositoryFilter<S> sessionRepositoryFilter = new SessionRepositoryFilter(sessionRepository);
sessionRepositoryFilter.setServletContext(this.servletContext);
。。。。。
return sessionRepositoryFilter;
}
4 SessionRepositoryFilter类
由它来拦截url,实现session的操作
@Order(-2147483598)
public class SessionRepositoryFilter<S extends ExpiringSession> extends OncePerRequestFilter{
// 完成对redis session的操作
private final SessionRepository<S> sessionRepository;
。。。。。。
}
可以看到@Order(-2147483598)指定了一个非常大的负数,所以这个filter会在ServletContext比较前面的位置
filter的内部处理doFilterInternal方法:
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) {
request.setAttribute(SESSION_REPOSITORY_ATTR, this.sessionRepository);
SessionRepositoryFilter<S>.SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryFilter.SessionRepositoryRequestWrapper(request, response, this.servletContext);
SessionRepositoryFilter<S>.SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryFilter.SessionRepositoryResponseWrapper(wrappedRequest, response);
try {
// 使用Wrapper完成正常的请求
filterChain.doFilter(strategyRequest, strategyResponse);
} finally {
// 这个finally块会对session进行保存、更新等操作
wrappedRequest.commitSession();
}
}
父类OncePerRequestFilter实现Filter接口的doFilter
public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) {
。。。。。。
// 调用子类实现doFilterInternal,这里是实现了filter的标准接口
this.doFilterInternal(httpRequest, httpResponse, filterChain);
。。。。。。
}
再来分析session是如何保存的,SessionRepositoryRequestWrapper内部类:
private final class SessionRepositoryRequestWrapper extends HttpServletRequestWrapper {
。。。。。。
private void commitSession() {
SessionRepositoryFilter<S>.SessionRepositoryRequestWrapper.HttpSessionWrapper wrappedSession = this.getCurrentSession();
if (wrappedSession == null) {
if (this.isInvalidateClientSession()) {
SessionRepositoryFilter.this.httpSessionStrategy.onInvalidateSession(this, this.response);
}
} else {
// 保存redis的session中了
S session = wrappedSession.getSession();
SessionRepositoryFilter.this.sessionRepository.save(session);// 关注save方法
。。。。。。
}
}
}
第一请进来,就会走else分支,保存或者更新session
下面分析sessionRepository.save(session)方法:
RedisOperationsSessionRepository类
public void save(RedisOperationsSessionRepository.RedisSession session) {
session.saveDelta();
if (session.isNew()) {
String sessionCreatedKey = this.getSessionCreatedChannel(session.getId());
this.sessionRedisOperations.convertAndSend(sessionCreatedKey, session.delta);
session.setNew(false);
}
}
private void saveDelta() {
。。。。。。
String sessionId = this.getId();
// 这里把session写到了redis中
RedisOperationsSessionRepository.this.getSessionBoundHashOperations(sessionId).putAll(this.delta);
。。。。。。
}
获取session方法
public RedisOperationsSessionRepository.RedisSession getSession(String id) {
return this.getSession(id, false);
}
private RedisOperationsSessionRepository.RedisSession getSession(String id, boolean allowExpired) {
// 底层就是从redis中读取的session
Map<Object, Object> entries = this.getSessionBoundHashOperations(id).entries();
。。。。。。
}