java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Jedis到SpringDataRedis遇到的坑

从Jedis到SpringDataRedis:新手踩过的所有坑,一次性填平(附可直接复制代码)

作者:深蓝轨迹

本文系统解析了Redis在Spring生态中的整合过程,针对开发者常见的5大痛点进行深度拆解,感兴趣的朋友跟随小编一起看看吧

从 Jedis 到 Spring Data Redis 这条路上,新手最容易栽的坑基本都集中在‌连接管理、序列化、API 选择和过期时间‌这四类。核心结论先给你:‌能用 StringRedisTemplate 就别直接用 RedisTemplate,存对象时一定手动配好序列化器,连接统一走连接池,别用 keys 命令做模糊删除。‌‌‌

本文拆解从原生Redis客户端到Spring生态整合的全链路困惑,覆盖「连接池、序列化、Spring注入、API设计」等高频痛点,帮你彻底搞懂「为什么这么写」,而不是只会「抄代码」。建议收藏,避免踩坑!

一、开篇:新手的灵魂三问,你也一定问过

刚学Redis的Java开发者,大概率都经历过这样的迷茫(没错,说的就是你):

这些问题不是你笨,而是Redis生态和Spring框架的「分层封装」让新手看不到底层逻辑。本文就从你的真实代码出发,把所有困惑连根拔起,让你从「抄代码」升级到「懂原理」。

二、第一坑:Jedis vs SpringDataRedis,到底该用谁?

1. 本质区别:司机 vs 自动驾驶

用一个最通俗的类比,帮你分清两者定位:

维度Jedis(原生客户端)SpringDataRedis(Spring封装)
定位底层驱动,直接实现Redis协议上层工具,封装Jedis/Lettuce(底层还是用它们)
连接管理手动写连接池,借连接→用→还,一步错就泄漏自动托管连接池,全程无感,不用手动关连接
序列化只认字节/字符串,存对象要手动转JSON/JDK序列化内置多种序列化器,自动完成对象↔Redis数据转换
Spring生态无集成,需手动整合到Spring项目无缝适配@Cacheable缓存注解、Spring事务

2. 代码对比:一眼看懂差异(手动挡 vs 自动挡)

Jedis写法(手动挡,麻烦且易出错)

// 1. 手动从连接池拿连接
Jedis jedis = JedisConnectionFactory.getJedis();
try {
    // 2. 执行Redis原生命令
    jedis.zadd("ranking", 85, "tom");
} finally {
    // 3. 必须手动归还连接,忘写就会连接泄漏
    jedis.close();
}

SpringDataRedis写法(自动挡,简洁又安全)

// 1. 直接@Autowired注入,Spring自动管理
@Autowired
private RedisTemplate<String, Object> redisTemplate;
// 2. 直接调用API,不用管连接、不用关连接
redisTemplate.opsForZSet().add("ranking", "tom", 85);

✅ 结论:Spring Boot/Spring项目,无脑选SpringDataRedis;非Spring项目(比如纯JavaSE),再考虑Jedis。

三、第二坑:RedisTemplate的5个opsForXxx()到底是啥?

你看到的opsForValue()、opsForZSet(),本质是Redis 5种核心数据类型的Java封装——Spring把每种Redis类型,都做成了专门的操作类,不用记原生命令,按Java习惯调用即可。

API对应Redis类型核心使用场景简单示例
opsForValue()String(字符串)缓存字符串、计数器、验证码set("name", "张三")
opsForHash()Hash(哈希表)存储结构化对象(如用户、商品信息)put("user:1", "name", "张三")
opsForList()List(链表)消息队列、时间线、最新列表rightPush("msg", "Hello")
opsForSet()Set(无序集合)去重、共同好友、标签管理add("tags", "java", "redis")
opsForZSet()SortedSet(有序集合)排行榜、热度排序、延时任务add("ranking", "tom", 85)

实战示例:排行榜场景(最常用)

// 1. 给用户tom、jerry、lucy添加分数
redisTemplate.opsForZSet().add("ranking", "tom", 85);
redisTemplate.opsForZSet().add("ranking", "jerry", 90);
redisTemplate.opsForZSet().add("ranking", "lucy", 88);
// 2. 按分数降序取前3名(排行榜核心需求)
Set<Object> top3 = redisTemplate.opsForZSet().reverseRange("ranking", 0, 2);
// 3. 查看tom的分数
Double score = redisTemplate.opsForZSet().score("ranking", "tom");

四、第三坑:@Bean和注入,到底在搞什么?

这是新手最懵的点——为什么要写@Bean?RedisConnectionFactory凭空出现?方法参数里写个类型,Spring就自动传值?

1. 为什么要用@Bean?

@Bean的作用只有一个:把你创建的对象交给Spring容器管理,让你在项目任何地方,用@Autowired直接拿,不用自己反复new。

类比:你把一把「Redis操作工具」放到Spring的「工具箱」里,以后要用的时候,直接从箱子里取,不用每次都重新造一把,既省内存,又能统一管理。

2. RedisConnectionFactory哪来的?能直接用?

答案:它是Spring Boot自动配置好的,你完全不用自己创建!

3. 方法参数注入 vs @Autowired(3种注入方式对比)

新手常分不清3种注入方式,一张表讲透:

注入方式写法示例适用场景核心逻辑
字段注入@Autowired private RedisConnectionFactory factory;类内部直接使用Bean按类型匹配,直接注入到字段
构造器注入public UserService(RedisTemplate template) { this.template = template; }Service/Controller层(Spring官方推荐)按类型匹配,注入到构造器参数
@Bean参数注入@Bean public RedisTemplate redisTemplate(RedisConnectionFactory factory) { ... }配置类中创建Bean(如RedisConfig)创建Bean时,自动传入依赖的Bean

⚠️ 避坑:@Bean必须写在加了@Configuration的配置类里(比如RedisConfig),Spring才会识别并执行。

五、第四坑:序列化配置,为什么要写得这么复杂?

新手最烦的就是序列化配置——一堆setKeySerializer、RedisSerializer,到底在做什么?

1. 核心问题:Redis只认字节,不认Java对象

Redis是内存数据库,它的底层存储的是「字节数组」,根本不认识Java里的User、List、Map这些对象。

所以:

2. 你的配置代码,到底在做什么?

先看代码(你之前写的,逐行拆解):

// 1. 配置key和hashKey的序列化规则(用字符串序列化)
redisTemplate.setKeySerializer(RedisSerializer.string());
redisTemplate.setHashKeySerializer(RedisSerializer.string());
// 2. 配置value和hashValue的序列化规则(用JSON序列化)
GenericJackson2JsonRedisSerializer jsonSerializer = new GenericJackson2JsonRedisSerializer();
redisTemplate.setValueSerializer(jsonSerializer);
redisTemplate.setHashValueSerializer(jsonSerializer);

拆解:

3. RedisSerializer是啥?静态方法又是什么?

这是你问的核心问题,一句话讲透:

❌ 避坑:如果不配置序列化,RedisTemplate默认用「JDK原生序列化」,存到Redis里的是一堆乱码(比如\xAC\xED\x00\x05t\x00\x04name),根本没法查看和复用!

六、RedisTemplate的两种序列化实践方案

在实际开发中,Spring Data Redis 提供了两种主流的序列化方案,你可以根据场景选择:

方案一:自定义 RedisTemplate + 自动 JSON 序列化

核心思路:自定义RedisTemplate,全局配置序列化器,让Spring自动完成对象 ↔ JSON 的转换,代码更简洁。

配置代码:

@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory){
        // 创建RedisTemplate对象
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        // 设置连接工厂
        template.setConnectionFactory(connectionFactory);
        // 创建JSON序列化工具
        GenericJackson2JsonRedisSerializer jsonRedisSerializer = 
            							new GenericJackson2JsonRedisSerializer();
        // 设置Key的序列化
        template.setKeySerializer(RedisSerializer.string());
        template.setHashKeySerializer(RedisSerializer.string());
        // 设置Value的序列化
        template.setValueSerializer(jsonRedisSerializer);
        template.setHashValueSerializer(jsonRedisSerializer);
        // 返回配置好的模板
        return template;
    }
}

使用示例:

@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Test
void testSaveUser() {
    User user = new User("虎哥", 21);
    // 直接存对象,Spring自动序列化为JSON
    redisTemplate.opsForValue().set("user:100", user);
    // 直接取对象,Spring自动反序列化为User
    User user1 = (User) redisTemplate.opsForValue().get("user:100");
    System.out.println("user1 = " + user1);
}

优点:代码极简,无需手动处理序列化;缺点:JSON中会包含类全限定名,占用额外空间。

方案二:使用 StringRedisTemplate + 手动 JSON 序列化

核心思路:使用StringRedisTemplate(默认key/value都是String序列化),手动用Jackson把对象转成JSON字符串存入Redis,读取时再手动转回对象。

使用代码:

@SpringBootTest
class RedisStringTests {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    // 测试基础String操作
    @Test
    void testString() {
        // 写入一条String数据
        stringRedisTemplate.opsForValue().set("verify:phone:13600527634", "124143");
        // 获取string数据
        Object name = stringRedisTemplate.opsForValue().get("name");
        System.out.println("name = " + name);
    }
    // Jackson对象,用于手动序列化/反序列化
    private static final ObjectMapper mapper = new ObjectMapper();
    // 测试手动序列化存储对象
    @Test
    void testSaveUser() throws JsonProcessingException {
        // 创建对象
        User user = new User("虎哥", 21);
        // 手动序列化:对象 → JSON字符串
        String json = mapper.writeValueAsString(user);
        // 写入数据到Redis
        stringRedisTemplate.opsForValue().set("user:200", json);
        // 从Redis读取JSON字符串
        String jsonUser = stringRedisTemplate.opsForValue().get("user:200");
        // 手动反序列化:JSON字符串 → 对象
        User user1 = mapper.readValue(jsonUser, User.class);
        System.out.println("user1 = " + user1);
    }
}

优点:JSON更简洁,无冗余类信息,兼容性更好;缺点:需要手动处理序列化,代码稍繁琐。

七、从0到1,完整可跑的配置示例(直接复制)

1. 依赖配置(pom.xml)

<!-- Spring Data Redis 依赖(自动包含Jedis/Lettuce) -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- Jackson依赖(JSON序列化用,可选,部分版本已包含) -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
</dependency>

2. application.yml配置(Redis连接信息)

spring:
  redis:
    # Redis服务器IP(改成你自己的)
    host: 192.168.150.101
    # Redis端口(默认6379)
    port: 6379
    # Redis密码(没有密码可省略)
    password: 123321
    # 连接超时时间(1秒)
    timeout: 1000ms
    # 连接池配置(可选,Spring默认有配置,可自定义)
    lettuce:
      pool:
        max-active: 8 # 最大连接数
        max-idle: 8 # 最大空闲连接数
        min-idle: 0 # 最小空闲连接数
        max-wait: 1000ms # 最大等待时间

3. 实体类(User.java)

// 必须实现Serializable接口,用于序列化
public class User implements java.io.Serializable {
    private static final long serialVersionUID = 1L;
    private String name;
    private Integer age;
    // 构造方法、getter/setter 省略
    public User() {}
    public User(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
}

八、总结:新手必背的3条核心规律

学完这篇,不用死记硬背代码,记住这3条规律,再遇到Redis配置,就能一眼看穿本质:

  1. 分层思维:Jedis是底层驱动(司机),SpringDataRedis是上层工具(自动驾驶),后者帮你解决连接管理、序列化、生态集成的所有麻烦,不用重复造轮子;
  2. 注入本质:@Bean是「把工具放进Spring工具箱」,方法参数注入是「拿工具时,Spring自动把配套零件递过来」,核心是让Spring帮你管理对象和依赖;
  3. 序列化核心:Redis只认字节,Java对象必须序列化——有两种方案:
    • 方案一:自定义RedisTemplate,自动JSON序列化,代码简洁;
    • 方案二:用StringRedisTemplate,手动JSON序列化,兼容性更好。

下次再看到别人的Redis配置代码,你再也不会问「这是啥」,而是能一眼看穿:哦,他在配置连接工厂、设置序列化规则、把RedisTemplate交给Spring管理。

到此这篇关于从Jedis到SpringDataRedis:新手踩过的所有坑,一次性填平(附可直接复制代码)的文章就介绍到这了,更多相关Jedis到SpringDataRedis遇到的坑内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文