
Redis 的Java 客户端很多,常用的几种: Jedis Lettuce Spring Data Redis Spring Data Redis是Spring的一部分,对Redis底层开发包进行了高度封装。 在Spring 项目中,可以使用spring Data Redis来简化操作。
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 如果需要连接池 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>yaml
spring:
redis:
host: localhost
port: 6379
password:
database: 0
timeout: 5000ms
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
max-wait: -1ms
注意层级关系,运用占用符可以方便代码的修改,解耦。
java
@Configuration
@EnableCaching
public class RedisConfig {
@Bean(连接工厂对象已经在spring的容器中创建完成,所以我们只需要通过Bean注解)
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
// JSON序列化配置
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer =
new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
// String序列化配置
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key采用String序列化
template.setKeySerializer(stringRedisSerializer);
template.setHashKeySerializer(stringRedisSerializer);
// value采用Jackson序列化
template.setValueSerializer(jackson2JsonRedisSerializer);
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer =
new Jackson2JsonRedisSerializer<>(Object.class);
// 配置缓存
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofHours(1)) // 缓存有效期1小时
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
.disableCachingNullValues();
return RedisCacheManager.builder(factory)
.cacheDefaults(config)
.build();
}
}这里需要注意的是,我们在idea中编写配置类时,idea可能会给我们报错,找不到连接工厂对象,这是我们不用担心,也不用想着怎么不报错,先运行再说,结果发现能运行,这就是idea自己的问题了,
redisTemplate 可能为 null
想象你要寄快递(操作 Redis),你需要: 没有连接工厂 = 你自己开车去每个收件人家门口
有连接工厂 = 顺丰/京东的分拨中心
java
// 连接工厂帮你管理所有Redis连接
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 之后你只需要
redisTemplate.opsForValue().set("name", "张三"); // 工厂会自动分配连接连接工厂的工作:
你要往Redis存东西,Redis只认识字节,就像快递员只认识包裹。 没有序列化器 = 你直接把活猪交给快递员
有序列化器 = 专业的打包员
1. StringRedisSerializer - 就像"标准纸箱"
java
// 存的时候: "张三" -> 字节数组 [45, 67, 88]
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.opsForValue().set("name", "张三");
// 取的时候: 字节数组 [45, 67, 88] -> "张三"
String name = (String) redisTemplate.opsForValue().get("name");2. JdkSerializationRedisSerializer - 就像"定制木箱"
java
User user = new User("张三", 18);
redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
redisTemplate.opsForValue().set("user", user);
// user对象 -> 复杂的Java序列化格式3. Jackson2JsonRedisSerializer - 就像"透明塑封"
java
User user = new User("张三", 18);
redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(User.class));
redisTemplate.opsForValue().set("user", user);
// user对象 -> {"name":"张三","age":18} JSON格式java
// 不好的方式:没有设置序列化器
redisTemplate.opsForValue().set("token:123", userObject);
// 结果:Redis里存的是乱码 \xAC\xED\x00\x05...
// 好的方式:用JSON序列化器
redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(User.class));
redisTemplate.opsForValue().set("token:123", userObject);
// 结果:Redis里存的是 {"id":1,"name":"张三","age":18} 可读性好java
// 存数字:用StringRedisSerializer最合适
stringRedisTemplate.opsForValue().set("visits", "100");
stringRedisTemplate.opsForValue().increment("visits"); // 变成101java
// 对象需要序列化才能存
@RedisHash("people") // 对象注解
public class Person {
private String id;
private String name;
private List<String> hobbies; // 复杂类型
}
// 序列化器会帮你把List也处理好java
// 这行代码看似简单
redisTemplate.opsForValue().set("name", "张三");
// 背后连接工厂做了:
1. 从连接池获取一个Redis连接
2. 通过socket发送: SET name 张三
3. 等待Redis返回: +OK
4. 把连接放回连接池
5. 处理可能的异常(超时、断线等)不设置序列化器的后果:
java
// 不设置key序列化器
redisTemplate.opsForValue().set("name", "张三");
// Redis里实际存的key是:
// \xAC\xED\x00\x05t\x00\x04name
// 而不是你期望的 "name"
// 导致的问题:
redisTemplate.opsForValue().get("name"); // 查不到!
redisTemplate.delete("name"); // 删不掉!java
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
// 1. 连接工厂(必设)- 让模板知道怎么连Redis
template.setConnectionFactory(factory);
// 2. key序列化器(推荐设)- 让key可读
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
// 3. value序列化器(按需设)- JSON格式方便跨语言
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
return template;
}
// 简单场景直接用 StringRedisTemplate
@Bean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory factory) {
return new StringRedisTemplate(factory);
}
}一句话总结:
测试类:
@Autowired
private StringRedisTemplate redisTemplate;
// 基础操作
public void stringOperations() {
// SET/GET
redisTemplate.opsForValue().set("key", "value");
String value = redisTemplate.opsForValue().get("key");
// SETEX (带过期时间)
redisTemplate.opsForValue().set("key", "value", 10, TimeUnit.SECONDS);
// SETNX (不存在才设置)
Boolean success = redisTemplate.opsForValue().setIfAbsent("key", "value");
// 递增递减
Long newValue = redisTemplate.opsForValue().increment("counter", 1);
// 批量操作
Map<String, String> map = new HashMap<>();
map.put("k1", "v1");
map.put("k2", "v2");
redisTemplate.opsForValue().multiSet(map);
List<String> values = redisTemplate.opsForValue().multiGet(Arrays.asList("k1", "k2"));
}唯一需要注意的是在运行测试之前我们要先启动Redis这样我们才能正确连接上。
public void hashOperations() {
HashOperations<String, String, Object> hashOps = redisTemplate.opsForHash();
String key = "user:1001";
// 设置单个字段
hashOps.put(key, "name", "张三");
hashOps.put(key, "age", "25");
hashOps.put(key, "city", "北京");
// 批量设置
Map<String, Object> userMap = new HashMap<>();
userMap.put("name", "李四");
userMap.put("age", "30");
userMap.put("city", "上海");
hashOps.putAll(key, userMap);
// 获取单个字段
String name = (String) hashOps.get(key, "name");
// 获取多个字段
List<Object> values = hashOps.multiGet(key, Arrays.asList("name", "age"));
// 获取所有字段和值
Map<String, Object> allFields = hashOps.entries(key);
// 获取所有字段名
Set<String> fields = hashOps.keys(key);
// 判断字段是否存在
Boolean hasName = hashOps.hasKey(key, "name");
// 递增某个字段
Long newAge = hashOps.increment(key, "age", 1);
// 删除字段
Long deleteCount = hashOps.delete(key, "age", "city");
// 获取 Hash 大小
Long size = hashOps.size(key);
}java
public void listOperations() {
ListOperations<String, String> listOps = redisTemplate.opsForList();
String key = "messages";
// 右推 (RPUSH) - 从右侧添加
listOps.rightPush(key, "msg1");
listOps.rightPush(key, "msg2");
listOps.rightPushAll(key, "msg3", "msg4", "msg5");
// 左推 (LPUSH) - 从左侧添加
listOps.leftPush(key, "msg0");
// 左弹 (LPOP) - 从左侧取出并移除
String firstMsg = listOps.leftPop(key);
// 右弹 (RPOP) - 从右侧取出并移除
String lastMsg = listOps.rightPop(key);
// 阻塞式弹出 (BLPOP) - 等待5秒
String blockedMsg = listOps.leftPop(key, 5, TimeUnit.SECONDS);
// 获取指定范围的元素 (LRANGE)
List<String> range = listOps.range(key, 0, -1); // 全部
// 获取指定索引的元素 (LINDEX)
String element = listOps.index(key, 0);
// 获取列表长度 (LLEN)
Long length = listOps.size(key);
// 修剪列表 (LTRIM) - 只保留指定范围
listOps.trim(key, 0, 100);
// 在指定值前后插入 (LINSERT)
listOps.rightPush(key, "msg2", "inserted_after_msg2");
}java
public void setOperations() {
SetOperations<String, String> setOps = redisTemplate.opsForSet();
String key1 = "user:1001:tags";
String key2 = "user:1002:tags";
// 添加元素 (SADD)
setOps.add(key1, "java", "python", "javascript", "mysql");
setOps.add(key2, "java", "python", "mongodb", "redis");
// 获取所有成员 (SMEMBERS)
Set<String> tags1 = setOps.members(key1);
// 判断成员是否存在 (SISMEMBER)
Boolean hasJava = setOps.isMember(key1, "java");
// 获取集合大小 (SCARD)
Long size = setOps.size(key1);
// 移除元素 (SREM)
setOps.remove(key1, "mysql");
// 交集 (SINTER)
Set<String> intersect = setOps.intersect(key1, key2);
// 交集并存储到新集合
setOps.intersectAndStore(key1, key2, "user:common:tags");
// 并集 (SUNION)
Set<String> union = setOps.union(key1, key2);
// 差集 (SDIFF) - 在 key1 不在 key2 中的元素
Set<String> diff = setOps.difference(key1, key2);
// 随机弹出元素 (SPOP)
String randomTag = setOps.pop(key1);
// 随机获取元素不弹出 (SRANDMEMBER)
String randomTag2 = setOps.randomMember(key1);
List<String> randomTags = setOps.randomMembers(key1, 3); // 获取3个
}java
public void zSetOperations() {
ZSetOperations<String, String> zSetOps = redisTemplate.opsForZSet();
String key = "game:rank";
// 添加元素,带分数 (ZADD)
zSetOps.add(key, "player1", 1000);
zSetOps.add(key, "player2", 2000);
zSetOps.add(key, "player3", 1500);
// 批量添加
Set<ZSetOperations.TypedTuple<String>> tuples = new HashSet<>();
tuples.add(new DefaultTypedTuple<>("player4", 1800.0));
tuples.add(new DefaultTypedTuple<>("player5", 1200.0));
zSetOps.add(key, tuples);
// 获取分数 (ZSCORE)
Double score = zSetOps.score(key, "player1");
// 获取排名 (ZRANK) - 从低到高
Long rank = zSetOps.rank(key, "player1"); // 0开始
// 获取反向排名 (ZREVRANK) - 从高到低
Long revRank = zSetOps.reverseRank(key, "player2");
// 获取分数范围内的元素 (ZRANGEBYSCORE)
Set<String> rangeByScore = zSetOps.rangeByScore(key, 1000, 2000);
// 获取指定排名范围的元素 (ZRANGE)
Set<String> top3 = zSetOps.range(key, 0, 2); // 前三名
// 带分数的范围查询
Set<ZSetOperations.TypedTuple<String>> rangeWithScores =
zSetOps.rangeWithScores(key, 0, -1);
// 增加分数 (ZINCRBY)
Double newScore = zSetOps.incrementScore(key, "player1", 500);
// 统计分数范围内的元素数量 (ZCOUNT)
Long count = zSetOps.count(key, 1500, 2000);
// 获取集合大小 (ZCARD)
Long size = zSetOps.zCard(key);
// 移除元素 (ZREM)
Long removeCount = zSetOps.remove(key, "player5");
// 按排名范围移除 (ZREMRANGEBYRANK)
zSetOps.removeRange(key, 0, 1); // 移除排名最低的两个
// 按分数范围移除 (ZREMRANGEBYSCORE)
zSetOps.removeRangeByScore(key, 500, 1000);
}想象你住在一个小区,小区里有快递柜(Redis服务器),你要存取快递:
java
// Redis服务器就是你本地启动的那个程序
redis-server.exe 启动后 -> 相当于快递柜通上电,开始工作java
RedisConnectionFactory factory = new LettuceConnectionFactory("localhost", 6379);这就像你去物业办登记:
java
RedisTemplate template = new RedisTemplate();
template.setConnectionFactory(factory);这就像物业给你的一个多功能工具包:
java
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new Jackson2JsonRedisSerializer<>(User.class));这就像快递的打包标准:
java
// 1. 你创建了一个用户对象(你要寄的东西)
User user = new User("张三", 18, "北京");
// 2. 拿出工具包(RedisTemplate)
redisTemplate.opsForValue().set("user:1", user);背后发生的事情:
graph TD
A[你的Java代码] -->|1. 调用set方法| B[RedisTemplate]
B -->|2. 找序列化器| C[Jackson2JsonRedisSerializer]
C -->|3. 对象转JSON| D[{"name":"张三","age":18,"city":"北京"}]
D -->|4. 找连接工厂| E[LettuceConnectionFactory]
E -->|5. 从连接池拿门禁卡| F[Redis连接]
F -->|6. 通过网络发送| G[Redis服务器]
G -->|7. 存到内存| H[快递柜: user:1]java
@Configuration
public class RedisConfig {
@Bean
public RedisConnectionFactory redisConnectionFactory() {
// 去物业办登记,拿到门禁卡系统
return new LettuceConnectionFactory("localhost", 6379);
}
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
// 把这个门禁卡系统装进工具包
template.setConnectionFactory(factory);
// 设置key的打包方式:用普通纸箱(字符串格式)
template.setKeySerializer(new StringRedisSerializer());
// 设置value的打包方式:用透明塑封(JSON格式)
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
return template; // 给你一个完整的工具包
}
}java
// 用你的工具包存数据
User user = new User("李四", 25);
redisTemplate.opsForValue().set("user:2", user);
// 相当于:
// 1. 序列化器把李四变成JSON: {"name":"李四","age":25}
// 2. 连接工厂用门禁卡打开快递柜
// 3. 把JSON放进编号为"user:2"的格子java
User user = (User) redisTemplate.opsForValue().get("user:2");
// 相当于:
// 1. 连接工厂找到对应的快递柜
// 2. 打开"user:2"的格子取出JSON
// 3. 序列化器把JSON变回User对象没有这些层,你要自己写:
java
// 噩梦般的原始写法
Socket socket = new Socket("localhost", 6379);
OutputStream out = socket.getOutputStream();
out.write("*3\r\n$3\r\nSET\r\n$5\r\nuser:1\r\n$15\r\n{\"name\":\"张三\"}\r\n".getBytes());
// 还要处理返回结果、异常、连接关闭...有了这些层,你只需要:
java
redisTemplate.opsForValue().set("user:1", user);技术概念 | 生活比喻 | 职责 |
|---|---|---|
Redis服务器 | 小区快递柜 | 存放数据的地方 |
连接工厂 | 物业门禁系统 | 管理如何进入小区、找到快递柜 |
连接池 | 多张门禁卡 | 多人同时取件也不怕 |
RedisTemplate | 多功能工具包 | 封装各种取件工具 |
opsForXXX | 不同工具(扫码枪等) | 针对不同快递的操作方法 |
序列化器 | 打包/拆包规则 | Java对象 ↔ 网络传输数据 |

结语:如果对你有帮助,请点赞,关注,收藏,你的支持就是我最大的动力!