spring-session-redis

本文详细介绍了Spring基于Redis的Session管理。首先展示了依赖配置,接着说明使用注解启动管理,分析了RedisHttpSessionConfiguration类的配置,包括注册保存session到Redis的bean和对SessionRepositoryFilter的配置。还介绍了SessionRepositoryFilter类拦截url实现session操作及session保存、获取的具体方法。

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();
        。。。。。。
}

### spring-boot-starter-data-redis #### 介绍 `spring-boot-starter-data-redis` 是 Spring Boot 提供的一个用于简化 Redis 开发的启动器。它集成了 Spring Data Redis,提供了对 Redis 数据库的访问支持。借助该启动器,开发者能轻松地与 Redis 进行交互,实现缓存、消息队列等功能,避免了复杂的配置过程,提供了默认配置和可插拔设计,追求开箱即用的效果,就像 Spring Boot 把常用基础框架组合起来的特性一样 [^1]。 #### 使用方法 1. **添加依赖**:在 `pom.xml` 中添加以下依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> ``` 2. **配置 Redis 连接信息**:在 `application.properties` 或 `application.yml` 中配置 Redis 服务的相关信息,例如: ```properties spring.redis.host=127.0.0.1 spring.redis.port=6379 spring.redis.password= ``` 3. **使用 RedisTemplate 或 StringRedisTemplate 进行操作**: ```java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service; @Service public class RedisService { @Autowired private StringRedisTemplate stringRedisTemplate; public void setValue(String key, String value) { stringRedisTemplate.opsForValue().set(key, value); } public String getValue(String key) { return stringRedisTemplate.opsForValue().get(key); } } ``` ### spring-session-redis #### 介绍 `spring-session-redis` 是 Spring SessionRedis 集成的模块,主要用于解决分布式系统中的会话管理问题。它将用户的会话信息存储在 Redis 中,实现了会话的共享和持久化,使得在不同的服务节点之间可以方便地共享用户会话信息,提高了系统的可扩展性和可靠性。 #### 使用方法 1. **添加依赖**:在 `pom.xml` 中添加以下依赖: ```xml <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> </dependency> ``` 2. **配置 Redis 连接信息**:同样在 `application.properties` 或 `application.yml` 中配置 Redis 服务的相关信息,与 `spring-boot-starter-data-redis` 的配置相同。 3. **启用 Spring Session**:在 Spring Boot 主类上添加 `@EnableRedisHttpSession` 注解: ```java import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession; @SpringBootApplication @EnableRedisHttpSession public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ``` 4. **使用会话信息**:在控制器中可以像使用普通会话一样使用 Spring Session: ```java import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpSession; @RestController public class SessionController { @GetMapping("/setSession") public String setSession(HttpSession session) { session.setAttribute("username", "testUser"); return "Session set"; } @GetMapping("/getSession") public String getSession(HttpSession session) { String username = (String) session.getAttribute("username"); return "Username in session: " + username; } } ``` ### 区别 - **功能侧重点**: - `spring-boot-starter-data-redis` 主要侧重于提供与 Redis 数据库的交互功能,用于实现缓存、消息队列、分布式锁等功能。 - `spring-session-redis` 主要用于解决分布式系统中的会话管理问题,将用户的会话信息存储在 Redis 中,实现会话的共享和持久化。 - **使用场景**: - 当需要在项目中使用 Redis 进行数据存储、缓存、消息处理等操作时,使用 `spring-boot-starter-data-redis`。 - 当项目是分布式系统,需要在不同的服务节点之间共享用户会话信息时,使用 `spring-session-redis`。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值