SpringBoot整合
默认客户端
Spring Boot在2.0版本后,将默认的Redis客户端从Jedis切换到了Lettuce
以下是主要原因:
- 线程模型和连接管理 (最核心的区别):
- Jedis: 是一个阻塞式 I/O (BIO) 的客户端。每个 Jedis 连接实例不是线程安全的。这意味着在多线程环境下,你需要为每个线程(或每个请求)从连接池中获取和释放连接。如果连接池耗尽或管理不当,很容易成为性能瓶颈。在高并发下,大量线程等待连接会消耗过多资源。
- Lettuce: 是一个基于 Netty 的非阻塞式 I/O (NIO) 的客户端。它的连接实例是线程安全的,可以被多个线程共享。这意味着你可以用少量的连接来处理大量的并发请求。Lettuce 利用 Netty 的事件循环模型,可以用较少的线程处理更多的并发,从而提高吞吐量和资源利用率。
- 响应式编程支持:
- Jedis: 本身并不直接支持响应式编程模型(如 Project Reactor 或 RxJava)。虽然可以进行一些封装,但不是其核心设计。
- Lettuce: 原生支持响应式编程。它提供了与 Project Reactor(Spring WebFlux 使用的核心响应式库)完美集成的 API。这使得在构建完全响应式的 Spring Boot 应用时,Lettuce 成为自然的选择。
- 性能和可伸缩性:
- Jedis: 由于其阻塞特性和连接池依赖,在高并发场景下,性能可能会受到连接获取和线程阻塞的限制,可伸缩性相对较差。
- Lettuce: 其异步和非阻塞的设计,以及高效的连接共享,通常在高并发下能提供更好的性能和可伸缩性。它能更有效地利用服务器资源,减少上下文切换和线程开销。
- 资源利用率:
- Jedis: 需要维护一个连接池,每个活跃的并发操作可能都需要一个独立的连接,这会消耗客户端和 Redis 服务器两端的资源(如文件描述符、内存)。
- Lettuce: 由于连接可以共享,通常只需要很少的连接就能满足需求,从而降低了资源消耗。
- 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
的最主要原因是为了改变默认的序列化策略:
-
为了让 Key 在 Redis 中可读: 默认的
JdkSerializationRedisSerializer
序列化后的 Key 是一堆二进制乱码,不方便在redis-cli
或可视化工具中直接查看。使用StringRedisSerializer
可以让 Key 显示为清晰的字符串。 -
为了改变 Value 的存储格式:
JdkSerializationRedisSerializer
虽然方便(能序列化几乎任何Serializable
的对象),但其序列化结果是 Java 特有的二进制格式,可读性差,且存在 严重的安全漏洞。- 使用
Jackson2JsonRedisSerializer
可以将 Value 存储为 JSON 格式。这使得数据在 Redis 中更容易调试和查看,并且理论上(如果不依赖 Jackson 的类型信息)也更容易被非 Java 应用读取(尽管你代码中启用了类型信息,降低了一些通用性)。
redisTemplate
Bean的创建:
/**
* 配置并创建 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;
}
评论区
请登录后发表评论