记录一下最近在从零搭建项目时集成Redis的使用心得,主要内容如下:

  1. SpringBoot如何引入Redis;
  2. SpringBoot引入Redis依赖后,为什么不能直接注入RedisTemplate;
  3. SpringBoot中Redis的序列化方式;
  4. 自动装配默认使用的Redis客户端为什么是Lettuce;

SpringBoot如何引入Redis

首先,如果作为初次在SpringBoot项目中使用Redis的人,可以按照如下方法查找关于Redis的依赖包。

  1. 浏览器打开spring官网,找到SpringBoot项目。

    image-20240826160335356

  2. 点击当前版本(CURRENT)SpringBoot的参考资料(Reference Doc.)。

    image-20240826160737190

  3. 进入文档后,在搜索栏(Search)搜索Redis,一般第一个就是对应的文档目录。

    image-20240826161026006

  4. 根据文档内容,可以了解到SpringBoot提供了一个 spring-boot-starter-data-redis 依赖包用于管理SpringBoot中关于Redis的依赖配置。并且在文档中还有大致的使用说明和示例。

因此,要想使用Redis,只需要在SpringBoot项目中引入如下依赖即可:

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

spring-boot-starter-data-redis 依赖的版本号在 spring-boot-dependencies 有依赖管理。

如果想要开启Redis连接池,则需要依赖 commons-pool2

1
2
3
4
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>

同样的,commons-pool2 依赖的版本号在 spring-boot-dependencies 有依赖管理。

SpringBoot项目为什么不能直接注入RedisTemplate

这个问题有一点歧义,其实SpringBoot项目在引入spring-boot-starter-data-redis依赖后,是可以直接注入的,通过 RedisAutoConfiguration 源码分析可知,它默认注册了两个RedisTemplate类型的Bean。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@AutoConfiguration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {

@Bean
@ConditionalOnMissingBean(RedisConnectionDetails.class)
PropertiesRedisConnectionDetails redisConnectionDetails(RedisProperties properties) {
return new PropertiesRedisConnectionDetails(properties);
}

@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}

@Bean
@ConditionalOnMissingBean
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
return new StringRedisTemplate(redisConnectionFactory);
}

}

Spring依赖注入的原理是基于Bean类型+名称确定,通过@Autowried注入唯一RedisTemplate对象时,可以注入下面两种:

  • RedisTemplate<Object, Object> redisTemplate;
  • StringRedisTemplate stringRedisTemplate;

StringRedisTemplate 继承于RedisTemplate<String, String>,因此从Spring容器获取所有RedisTemplate的Bean对象时,StringRedisTemplate也会在其中。

细心点可以发现,StringRedisTemplateRedisTemplate<Object, Object>主要是范型类型不一样,所以这里就涉及到一个知识点。

在Java中,尽管泛型类型在运行时会被擦除,但在 Spring 的上下文中,Bean 的定义还是会包含泛型信息,这样可以在自动装配时进行更加精确的匹配。

因此,通过@Autowired自动注入Bean时,Spring会尝试匹配 Bean 的类型和泛型参数。

所以,回到问题本身,如果在项目代码中,使用如下方式注入,就会报错:

1
2
3
// 报错,因为SpringBoot自动装配注册的Bean为RedisTemplate<Object, Object>类型
@Autowired
RedisTemplate<String, Object> redisTemplate;

如果想要具备RedisTemplate<String, Object>类型的Bean,就得手动注册一个。

SpringBoot中Redis的序列化方式

准确来说,应该是RedisTemplate的序列化方式,在 Spring Data Redis 中,RedisTemplate 使用序列化器(Serializer)将 Java 对象序列化为二进制数据(字节数组),并反序列化为 Java 对象。

默认情况下,RedisTemplate 会使用 JdkSerializationRedisSerializer 来序列化 keyvalue,这意味着对象会使用 Java 自带的序列化机制(Serializable 接口)进行序列化。

序列化器的接口为 RedisSerializer,Spring Data Redis中默认实现了几种序列化方式,常见的有:

  • StringRedisSerializer:将字符串序列化为字节数组,常用于序列化 key,因为 Redis 的 key 通常是字符串类型。

  • JdkSerializationRedisSerializer:使用 Java 序列化机制,将对象序列化为字节数组,这也是默认的 RedisTemplate 序列化方式。

  • Jackson2JsonRedisSerializer:使用 Jackson 库将对象序列化为 JSON 字符串,适合存储和读取 JSON 数据。

  • GenericJackson2JsonRedisSerializer:类似于 Jackson2JsonRedisSerializer,但更通用,可以处理泛型类型。

  • GenericToStringSerializer:将对象的 toString() 方法的结果进行序列化,适用于可以通过 toString() 表达的简单对象。

再看SpringBoot自动装配中提供的 StringRedisTemplate Bean对象,就会发现它使用的全部是 StringRedisSerializer 序列化器,它要求调用 RedisTemplate 存储键值对时,数据类型都为 String ,然后在序列化时,直接调用 String 类的getBytes方法,反序列化时则通过new String(bytes)方式。

自动装配默认使用的Redis客户端为什么是Lettuce

Spring Boot Starter Redis 默认使用 Lettuce 作为 Redis 客户端,是基于其性能优势、异步与反应式支持、线程安全性、集群与高可用性支持等多方面的优点。关键原因如下:

  1. Lettuce 的异步与反应式支持:Lettuce可以让应用程序以非阻塞的方式处理 Redis 操作。
  2. 线程安全:Lettuce 是一个线程安全的 Redis 客户端,允许多个线程共享同一个连接实例进行操作。
  3. 高可用与集群支持:Lettuce 支持 Redis 集群模式和分片(Sharding),并且能够处理主从复制架构中的故障转移情况,确保应用程序在 Redis 节点故障时仍然能够正常运行。
  4. 轻量且无第三方依赖:Lettuce 是一个轻量级的客户端,并且不依赖于 Netty 之外的第三方库。

再说 Jedis ,它与 Lettuce 相比具有如下限制:

  1. 同步 API:Jedis 是一个传统的同步 Redis 客户端,不支持异步操作,因此在处理高并发请求时,性能可能不如 Lettuce。
  2. 连接池依赖:Jedis 依赖于连接池来管理 Redis 连接,这在一些高并发场景中可能导致性能瓶颈或连接池耗尽的问题。

因此,Lettuce 更适合现代微服务架构和高并发场景,使其成为 Spring Boot 的首选 Redis 客户端。