前言
在 Spring Boot 项目中操作 Redis,原生的 Jedis 客户端需要编写大量重复代码,而Spring Data Redis 作为 Spring 生态下的模块,提供了高度封装的RedisTemplate,极大简化了 Redis 的操作流程。本文将从 Spring Data Redis 核心概念入手,一步步实现 Redis 的整合、序列化配置优化,以及通用 Redis 客户端封装,解决实际开发中的痛点问题。
一、Spring Data Redis 核心概念
1.1 Spring Data 简介
Spring Data 是用于简化数据库访问的开源框架,核心目标是降低数据访问层的开发成本,包含多个子项目:
- Spring Data JDBC:简化 JDBC 操作
- Spring Data JPA:简化 JPA 操作
- Spring Data MongoDB:操作 MongoDB
- Spring Data Redis:简化 Redis 操作(本文核心)
1.2 Spring Data Redis 核心能力
Spring Data Redis 对 Jedis 做了深度封装,核心特性:
- 封装
RedisTemplate模板类,支持 Redis 五大数据结构:
java
redisTemplate.opsForValue():操作字符串(String)
redisTemplate.opsForHash():操作哈希(Hash)
redisTemplate.opsForList():操作列表(List)
redisTemplate.opsForSet():操作集合(Set)
redisTemplate.opsForZSet():操作有序集合(ZSet)
- 底层通信客户端:SpringBoot2.x 后默认使用 Lettuce(基于 Netty 的异步非阻塞 IO),高并发下性能优于 Jedis。
- 序列化器:解决 Redis 存储数据的编码问题,内置三种核心序列化器:
| 序列化器 | 适用场景 | 优缺点 |
|---|---|---|
| JdkSerializationRedisSerializer | POJO 对象存取 | 基于 JDK 序列化,兼容性好;存储字节序列,可读性差、占用空间大 |
| StringRedisSerializer | Key/Value 为字符串 | 轻量高效,纯字符串存储,可读性好 |
| GenericJackson2JsonRedisSerializer | POJO 对象存取 | 序列化为 JSON 格式,可读性好、占用空间小;支持 JSON 与 POJO 互转 |
二、Spring Boot 整合 Spring Data Redis
2.1 创建工程 & 引入依赖
新建 Spring Boot 工程,pom.xml引入核心依赖:
XML
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.2.RELEASE</version>
</parent>
<groupId>com.hg</groupId>
<artifactId>springdata_redis</artifactId>
<version>1.0-SNAPSHOT</version>
<!-- 编码 & JDK版本配置 -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<dependencies>
<!-- SpringBoot Web启动器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Data Redis核心依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 测试依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
</project>
2.2 配置 Redis 连接(application.yml)
支持单机 / 集群配置,本文以 Redis 集群为例:
XML
spring:
redis:
cluster:
nodes:
- 192.168.204.131:7001
- 192.168.204.131:7002
- 192.168.204.131:7003
- 192.168.204.131:7004
- 192.168.204.131:7005
- 192.168.204.131:7006
jedis:
pool:
max-active: 20 # 连接池最大连接数
max-idle: 10 # 最大空闲连接
min-idle: 5 # 最小空闲连接
2.3 核心配置类(RedisConfig)
初始化RedisTemplate,后续优化序列化配置:
java
package com.hg.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
/**
* Redis整合核心配置
*/
@Configuration
public class RedisConfig {
/**
* 初始化RedisTemplate
*/
@Bean
public RedisTemplate<String, Object> getRedisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
return template;
}
}
2.4 启动类
java
package com.hg;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
三、序列化器实战:解决 POJO 存取痛点
3.1 定义测试 POJO
java
package com.hg.pojo;
import java.io.Serializable;
public class User implements Serializable {
private Integer id;
private String name;
private Integer age;
// Getter & Setter & toString 省略
}
3.2 痛点:每次存取都要设置序列化器
默认RedisTemplate使用JdkSerializationRedisSerializer,直接存取 POJO 会导致:
- Key/Value 乱码
- 每次操作都要手动指定序列化器
测试代码(手动指定序列化器):
java
package com.hg.test;
import com.hg.App;
import com.hg.pojo.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = {App.class})
public class RedisTest {
@Autowired
private RedisTemplate redisTemplate;
// 1. 字符串存取(StringRedisSerializer)
@Test
public void testSetStr(){
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
redisTemplate.opsForValue().set("str", "张三丰");
}
// 2. POJO存取(JdkSerializationRedisSerializer)
@Test
public void testSetPojo(){
User user = new User();
user.setId(1);
user.setName("张三丰");
user.setAge(140);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
redisTemplate.opsForValue().set("user", user);
}
// 3. POJO存取(GenericJackson2JsonRedisSerializer,推荐)
@Test
public void testSetPojo2(){
User user = new User();
user.setId(1);
user.setName("张三丰");
user.setAge(140);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.opsForValue().set("user2", user);
}
}
3.3 优化:配置全局通用序列化器
修改RedisConfig,统一配置序列化器,避免重复编码:
java
package com.hg.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory factory){
RedisTemplate redisTemplate = new RedisTemplate();
redisTemplate.setConnectionFactory(factory);
// 全局序列化配置
redisTemplate.setKeySerializer(new StringRedisSerializer()); // Key用字符串序列化
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); // Value用JSON序列化
redisTemplate.setHashKeySerializer(new StringRedisSerializer()); // Hash Key序列化
redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer()); // Hash Value序列化
return redisTemplate;
}
}
配置后,测试代码无需手动设置序列化器,直接存取:
java
@Test
public void testSetPojo3(){
User user = new User();
user.setId(1);
user.setName("张三丰");
user.setAge(140);
redisTemplate.opsForValue().set("user3", user); // 直接使用,自动序列化
}
@Test
public void testGetPojo3(){
User user3 = (User) redisTemplate.opsForValue().get("user3");
System.out.println(user3); // 自动反序列化为User对象
}
四、进阶:封装通用 Redis 客户端(RedisClient)
RedisTemplate的 API 与 Redis 原生命令不一致,封装通用客户端,让操作更直观:
java
package com.hg.redis;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
* Redis通用客户端封装(对标Redis原生命令)
*/
@Component
public class RedisClient {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
// ==================== 通用操作 ====================
/**
* 设置缓存过期时间
* @param key 键
* @param time 时间(秒)
*/
public boolean expire(String key, long time) {
try {
if (time > 0) {
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 获取过期时间
* @param key 键
* @return 过期时间(秒),0=永久有效
*/
public long ttl(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
/**
* 判断key是否存在
*/
public Boolean exists(String key) {
return redisTemplate.hasKey(key);
}
/**
* 删除缓存
*/
public Boolean del(String key) {
return redisTemplate.delete(key);
}
// ==================== String操作 ====================
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 递增
*/
public long incr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递增因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, delta);
}
/**
* 递减
*/
public long decr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递减因子必须大于0");
}
return redisTemplate.opsForValue().decrement(key, delta);
}
// ==================== Hash操作 ====================
public Object hget(String key, String item) {
return redisTemplate.opsForHash().get(key, item);
}
public boolean hset(String key, String item, Object value) {
try {
redisTemplate.opsForHash().put(key, item, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public void hdel(String key, Object... item) {
redisTemplate.opsForHash().delete(key, item);
}
// ==================== Set操作 ====================
public Set<Object> smembers(String key) {
try {
return redisTemplate.opsForSet().members(key);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public long sadd(String key, Object... values) {
try {
return redisTemplate.opsForSet().add(key, values);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
public long srem(String key, Object... values) {
try {
return redisTemplate.opsForSet().remove(key, values);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
// ==================== List操作 ====================
public List<Object> lrange(String key, long start, long end) {
try {
return redisTemplate.opsForList().range(key, start, end);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public boolean rpush(String key, Object value) {
try {
redisTemplate.opsForList().rightPush(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public long lrem(String key, long count, Object value) {
try {
return redisTemplate.opsForList().remove(key, count, value);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
}
使用封装后的客户端
java
@Autowired
private RedisClient redisClient;
@Test
public void testRedisClient() {
// 1. 存String
redisClient.set("name", "张三丰");
// 2. 存POJO
User user = new User();
user.setId(1);
user.setName("杨过");
user.setAge(20);
redisClient.set("user_client", user);
// 3. 获取
System.out.println(redisClient.get("name"));
System.out.println(redisClient.get("user_client"));
}
五、总结
- Spring Data Redis 核心是
RedisTemplate,封装了 Redis 五大数据结构的操作,底层默认使用 Lettuce 提升并发性能。 - 序列化器选择 :推荐使用
StringRedisSerializer(Key)+GenericJackson2JsonRedisSerializer(Value),兼顾可读性和性能。 - 通用客户端封装 :解决
RedisTemplateAPI 与原生命令不一致的问题,提升开发效率。 - 实际开发中,可基于本文的
RedisClient扩展更多 Redis 命令(如 ZSet、事务等),适配业务场景。
通过本文的配置和封装,能快速在 Spring Boot 项目中落地 Redis,解决序列化、重复编码等常见痛点,让 Redis 操作更简洁、高效。