Redis06_SpringBoot整合Redis源码解析

Spring Boot 2.0后默认Redis客户端从Jedis切换为Lettuce,因其非阻塞I/O、线程安全、高并发性能更优,支持响应式编程且资源利用率高。RedisTemplate是Spring Data Redis的核心类,提供操作接口和序列化功能,常用自定义配置如StringRedisSerializer和Jackson2JsonRedisSerializer以提升可读性和安全性。

作品集: Redis学习
作者头像
LumiBee
22 天前 · 47 0
分享

SpringBoot整合

默认客户端

Spring Boot在2.0版本后,将默认的Redis客户端从Jedis切换到了Lettuce

以下是主要原因:

  1. 线程模型和连接管理 (最核心的区别):
  • Jedis: 是一个阻塞式 I/O (BIO) 的客户端。每个 Jedis 连接实例不是线程安全的。这意味着在多线程环境下,你需要为每个线程(或每个请求)从连接池中获取和释放连接。如果连接池耗尽或管理不当,很容易成为性能瓶颈。在高并发下,大量线程等待连接会消耗过多资源。
  • Lettuce: 是一个基于 Netty 的非阻塞式 I/O (NIO) 的客户端。它的连接实例是线程安全的,可以被多个线程共享。这意味着你可以用少量的连接来处理大量的并发请求。Lettuce 利用 Netty 的事件循环模型,可以用较少的线程处理更多的并发,从而提高吞吐量和资源利用率。
  1. 响应式编程支持:
  • Jedis: 本身并不直接支持响应式编程模型(如 Project Reactor 或 RxJava)。虽然可以进行一些封装,但不是其核心设计。
  • Lettuce: 原生支持响应式编程。它提供了与 Project Reactor(Spring WebFlux 使用的核心响应式库)完美集成的 API。这使得在构建完全响应式的 Spring Boot 应用时,Lettuce 成为自然的选择。
  1. 性能和可伸缩性:
  • Jedis: 由于其阻塞特性和连接池依赖,在高并发场景下,性能可能会受到连接获取和线程阻塞的限制,可伸缩性相对较差。
  • Lettuce: 其异步和非阻塞的设计,以及高效的连接共享,通常在高并发下能提供更好的性能和可伸缩性。它能更有效地利用服务器资源,减少上下文切换和线程开销。
  1. 资源利用率:
  • Jedis: 需要维护一个连接池,每个活跃的并发操作可能都需要一个独立的连接,这会消耗客户端和 Redis 服务器两端的资源(如文件描述符、内存)。
  • Lettuce: 由于连接可以共享,通常只需要很少的连接就能满足需求,从而降低了资源消耗。
  1. API 设计和功能:
  • Jedis: 主要提供同步API。
  • Lettuce: 提供了同步、异步和响应式三种API模式,更加灵活。其API设计也更现代。

总而言之,Lettuce相比Jedis的优点:

  • 更适合高并发场景: 非阻塞I/O和线程安全的连接使其在高负载下表现更优。

  • 更好地支持响应式编程: 这与 Spring 框架向响应式模型发展的趋势相吻合 (Spring WebFlux)。

  • 资源效率更高: 使用更少的线程和连接处理更多请求。

  • 可伸缩性更好: 更容易构建能够水平扩展的应用。

RedisTemplate

RedisTemplate是Spring Data Redis提供的用于执行Redis操作的核心模块类,RedisTemplate是一个高层抽象,它提供了一系列方便的方法(如 opsForValue().set(), opsForList().leftPush() 等)来操作 Redis 数据结构,并且负责处理 对象的序列化和反序列化。它本身 不管理 Redis 的底层网络连接。

连接处理

RedisTemplate 必须配置一个 RedisConnectionFactory 实例,用于获取到底层 Redis 的连接 。其核心的交互方式是通过 execute(RedisCallback<T> action) 方法。此方法负责从连接工厂获取一个 RedisConnection,执行用户提供的 RedisCallback 中的 doInRedis(RedisConnection connection) 逻辑,并确保在操作完成后(无论成功与否)连接被正确关闭或归还到池中 。在 execute 方法内部,通常会使用 RedisConnectionUtils.getConnection(factory) 来获取连接,并通过 RedisConnectionUtils.releaseConnection(connection, factory) 来释放连接。

序列化

RedisTemplate 的一个关键特性是自动进行Java对象与 Redis 二进制存储格式之间的序列化和反序列化 。它提供了多个序列化器属性,可以针对不同部分的数据(键、值、哈希键、哈希值)进行配置 :

  • keySerializer: 用于序列化普通键。
  • valueSerializer: 用于序列化普通值。
  • hashKeySerializer: 用于序列化哈希结构中的字段名(Hash Key)。
  • hashValueSerializer: 用于序列化哈希结构中的字段值(Hash Value)。
  • defaultSerializer: 默认序列化器。

RedisTemplate 有一个布尔属性 enableDefaultSerializer,默认为 true。当此属性为 true 时,如果上述特定的序列化器(如 keySerializer, valueSerializer 等)没有被显式设置,则它们会默认使用 defaultSerializer 的实例 。而 defaultSerializer 本身,如果也未被显式设置,则默认为 JdkSerializationRedisSerializer

使用 JdkSerializationRedisSerializer 意味着存入 Redis 的数据是 Java 对象序列化后的二进制字节流,这要求被序列化的对象必须实现 java.io.Serializable 接口。这种序列化方式的一个副作用是,直接通过 redis-cli 查看这些键或值时,它们通常是不可读的二进制内容,有时被描述为“看起来很怪异” 。为了获得更好的可读性或与其他非 Java 应用互操作,开发者通常会选择配置其他的序列化器,例如:

  • StringRedisSerializer: 将对象序列化为 UTF-8 字符串。
  • Jackson2JsonRedisSerializer: 使用 Jackson 库将对象序列化为 JSON 字符串。
  • GenericJackson2JsonRedisSerializer: 也是基于 Jackson,但能更好地处理泛型类型。

操作接口

为了方便对 Redis 中不同数据类型的操作,RedisTemplate 提供了一系列 opsForXxx() 方法,它们返回专门的操作接口实例 :

  • opsForValue(): 返回 ValueOperations,用于操作简单的键值对(Redis Strings)。
  • opsForList(): 返回 ListOperations,用于操作 Redis Lists。
  • opsForSet(): 返回 SetOperations,用于操作 Redis Sets。
  • opsForZSet(): 返回 ZSetOperations,用于操作 Redis Sorted Sets。
  • opsForHash(): 返回 HashOperations,用于操作 Redis Hashes。
  • opsForStream(): 返回 StreamOperations,用于操作 Redis Streams (Redis 5.0+)。

RedisTemplate 的设计巧妙地运用了模板方法(Template Method)和策略(Strategy)设计模式。其 execute 方法本身构成了模板方法,它定义了与 Redis 交互的固定步骤:获取连接、执行操作、释放连接。而用户提供的 RedisCallback 实例则扮演了策略的角色,封装了具体的 Redis 操作逻辑。类似地,各种序列化器也是策略模式的体现:RedisTemplate 可以配置不同的序列化策略(JDK、JSON、String 等),而其核心的执行流程保持不变。这种设计带来了高度的灵活性和代码复用性。

自定义RedisTemplate

自定义RedisTemplate的最主要原因是为了改变默认的序列化策略:

  1. 为了让 Key 在 Redis 中可读: 默认的 JdkSerializationRedisSerializer 序列化后的 Key 是一堆二进制乱码,不方便在 redis-cli 或可视化工具中直接查看。使用 StringRedisSerializer 可以让 Key 显示为清晰的字符串。

  2. 为了改变 Value 的存储格式:

  • JdkSerializationRedisSerializer 虽然方便(能序列化几乎任何 Serializable 的对象),但其序列化结果是 Java 特有的二进制格式,可读性差,且存在 严重的安全漏洞
  • 使用 Jackson2JsonRedisSerializer 可以将 Value 存储为 JSON 格式。这使得数据在 Redis 中更容易调试和查看,并且理论上(如果不依赖 Jackson 的类型信息)也更容易被非 Java 应用读取(尽管你代码中启用了类型信息,降低了一些通用性)。

redisTemplateBean的创建:

    /**
     * 配置并创建 RedisTemplate Bean。
     * RedisTemplate 是 Spring Data Redis 提供的用于方便操作 Redis 的核心工具类。
     * 通过 @Bean 标记,Spring 会将此方法的返回值作为一个 Bean 放入应用上下文,默认 Bean 名称为方法名 "redisTemplate"。
     * @param factory Spring 自动注入的 Redis 连接工厂,用于创建与 Redis 服务器的连接。
     * @return 配置好的 RedisTemplate Bean。
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        // 创建一个 RedisTemplate 实例。
        // <String, Object> 指定了这个 RedisTemplate Bean 将主要用于操作 key 为 String 类型,value 为 Object 类型的 Redis 数据。
        // 使用 Object 类型作为 value 是为了能够存储各种不同的 Java 对象。
        RedisTemplate<String, Object> template = new RedisTemplate<>();

        // 设置 Redis 连接工厂。
        // 这是 RedisTemplate 正常工作的基础,它通过此工厂获取底层的 Redis 连接。
        template.setConnectionFactory(factory);

        // 配置 Value 的序列化器(使用 Jackson)。
        // Jackson2JsonRedisSerializer 是 Spring Data Redis 提供的,它使用 Jackson 库进行对象的 JSON 序列化。
        // 创建 Jackson 的 ObjectMapper 实例,它是 Jackson 进行 JSON 转换的核心。
        ObjectMapper om = new ObjectMapper();

        // 配置 ObjectMapper 的自动检测策略。
        // PropertyAccessor.ALL: 指定检查所有类型的属性(字段、Getter/Setter等)。
        // JsonAutoDetect.Visibility.ANY: 指定任何可见性(public, private, protected)的成员都可以被检测到。
        // 综合起来,这个配置使得 Jackson 可以序列化和反序列化类的所有字段,包括私有字段,无需额外的 Jackson 注解(如 @JsonProperty)。
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);

        // 启用默认类型信息。
        // 当 value 被声明为 Object 时,实际存储的对象可能是 Object 的任何子类(如 User, Product 等)。
        // 为了在反序列化时知道需要创建哪个具体的子类对象,需要在 JSON 中包含对象的类型信息。
        // activateDefaultTyping 是比 enableDefaultTyping 更推荐的新方法。
        // LaissezFaireSubTypeValidator.instance: 一个子类型验证器,这里使用宽松模式,允许任意非 final 类的子类型。
        // ObjectMapper.DefaultTyping.NON_FINAL: 指定只对非 final 类的对象启用默认类型信息。
        // 这个配置确保了存储到 Redis 的对象在取出来时,Jackson 能够正确地反序列化为原始的具体类型。
        om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);


        // 创建 Jackson2JsonRedisSerializer 实例,并在构造方法中传入配置好的 ObjectMapper 和目标类型。
        // 注意:这里的构造方法 `new Jackson2JsonRedisSerializer<>(om, Object.class)` 是 Spring Data Redis 3.0+ 推荐的方式,
        // 它替代了旧版本中先创建实例再调用 setObjectMapper() 方法的方式,解决了 setObjectMapper() 的废弃警告。
        // <Object> 这里的泛型应该与 RedisTemplate 的 value 类型保持一致。
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(om, Object.class);


        // 配置 Key 的序列化器(使用 StringRedisSerializer)。
        // StringRedisSerializer 用于将 Java String 与 Redis 的字节数组之间进行转换,通常使用 UTF-8 编码。
        // Redis 的 key 大部分情况下都是 String 类型,使用它是最方便的选择。
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

        // 应用序列化器到 RedisTemplate 的不同数据部分。
        // 设置 key 的序列化器:所有的 key 都将使用 StringRedisSerializer 进行序列化。
        template.setKeySerializer(stringRedisSerializer);

        // 设置 Hash 数据结构中 key 的序列化器:Hash 中的 key 也将使用 StringRedisSerializer。
        template.setHashKeySerializer(stringRedisSerializer);

        // 设置 value 的序列化器:所有的 value 都将使用配置好的 Jackson2JsonRedisSerializer 进行序列化(转为包含类型信息的 JSON)。
        template.setValueSerializer(jackson2JsonRedisSerializer);

        // 设置 Hash 数据结构中 value 的序列化器:Hash 中的 value 也将使用 Jackson2JsonRedisSerializer。
        template.setHashValueSerializer(jackson2JsonRedisSerializer);

        // 调用 afterPropertiesSet() 方法。
        // 这是 Spring Bean 生命周期方法之一,在 Bean 的所有属性设置完毕后调用,用于执行一些初始化逻辑。
        // 对于 RedisTemplate,它会检查必要的属性是否已设置,并进行一些内部初始化。
        template.afterPropertiesSet();

        // 返回配置好的 RedisTemplate Bean,Spring 会将其注册到应用上下文中。
        return template;
    }
阅读量: 47

评论区

登录后发表评论

正在加载评论...
相关阅读

暂无相关文章推荐

返回首页浏览更多文章