[toc]
| 单词 | 词性 | 音标 | 含义 |
|---|---|---|---|
| serializer | n. | /ˈsɪrəlaɪzə(r)/ | 序列化器(对象↔字节流/JSON 的工具,Redis 存取必配) |
| template | n. | /ˈtempleɪt/ | 模板(RedisTemplate 是 Spring Data Redis 提供的操作封装) |
| lettuce | n. | /ˈletɪs/ | 生菜(SpringBoot 默认的 Redis 客户端,基于 Netty 异步) |
| jedis | n. | /ˈdʒedɪs/ | 一种同步阻塞的 Redis Java 客户端 |
| jackson | n. | /ˈdʒæksən/ | JSON 处理库,序列化 value 用 |
| bound | adj. | /baʊnd/ | 绑定的(Bound 操作器:绑定 key 后所有操作围绕该 key) |
| pool | n. | /puːl/ | 连接池(复用连接,避免频繁建连) |
Lecture06-Redis-SpringBoot4整合
目标:在 SpringBoot 4 项目里用
RedisTemplate/StringRedisTemplate操作 Redis,并理解序列化与客户端选择。
1、搭建环境
1、前置:让 Redis 支持远程连接
SpringBoot 通常和 Redis 不在同一台机器上,靠 IP + 端口连接(就是下面 application.yml 里的 host: 192.168.100.100)。而 Redis 默认只允许本机连接,所以先要在 Redis 服务端放开远程访问,否则 SpringBoot 会连不上(连接超时 / Connection refused)。
在 Redis 的 redis.conf 里有两个关键配置:
| 配置 | 作用 |
|---|---|
bind | Redis 监听哪些网卡地址(决定允许谁连) |
requirepass | 访问密码(决定连接时要不要验密码) |
# 监听内网网卡(生产推荐:只监听内网 IP,不让外网直连)
bind 192.168.100.100
# 监听所有网卡(开发图方便,生产不推荐)
# bind 0.0.0.0
# 设置密码(要和 SpringBoot 的 spring.data.redis.password 保持一致)
requirepass atguigu
绑定多个 IP 时别写两行:后面的 bind 会覆盖前面的。
# ❌ 错误:第二个 bind 覆盖第一个,最终只监听 127.0.0.1
bind 192.168.100.100
bind 127.0.0.1
# ✅ 正确:写在同一行,用空格分隔
bind 192.168.100.100 127.0.0.1
关于受保护模式(protected-mode)
Redis 默认开启 protected-mode yes:当 Redis 既没有明确 bind 地址、又没有设密码时,它会拒绝外部连接,防止"裸奔"被攻击。
- Redis 6:
bind和requirepass配置其中任意一个,远程连接就能通过; - Redis 7 起:规则收紧,受保护模式下两个都要配(既 bind 地址、又设密码)才允许远程连接。
不建议关闭受保护模式:网上常见的"把
protected-mode no一改就能连"是图省事的做法,会让 Redis 在无密码状态下直接暴露到网络上,风险很大。正确做法是保留protected-mode yes,老老实实配好bind+requirepass。
为什么 Redis 要"绑定服务器端地址",而不是绑定客户端地址?
bind 限制的是 Redis 服务器监听哪个地址,也就间接限制了"客户端必须从哪个网络过来":
- 绑定
192.168.100.100:客户端必须能访问到这个内网地址才连得上,说明它和 Redis 处在同一个内网; - 内网与公网是网络层隔绝的(外部公网访问不进来),所以"来自内网的访问"相对可信;
- 如果绑定
0.0.0.0,等于监听所有网卡、公网也能直连,反而不安全。
所以"绑定服务器端(内网)地址"本质上是借助网络位置做一层访问控制——把 Redis 限制在内网里,只让可信来源连接,这个设定是合理的。
改完配置后重启 Redis 并验证:
# 重启 Redis(指定配置文件启动)
redis-server /usr/local/redis/redis.conf
# 从远程机器测试连通性(带密码)
redis-cli -h 192.168.100.100 -p 6379 -a atguigu ping
# 返回 PONG 即成功
连不上时先检查服务器防火墙 / 云安全组是否放行 6379 端口——这一步常和 Redis 配置无关。
2、创建 SpringBoot 4 项目
用 IDEA + start.spring.io 创建项目,或直接在 pom 中指定:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>4.0.0</version> <!-- 以当前稳定 4.x 为准 -->
<relativePath/>
</parent>
<properties>
<java.version>17</java.version> <!-- SpringBoot4 基线 Java 17+ -->
</properties>
3、引入依赖
<dependencies>
<!-- 整合 Redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- JSON 序列化(value 存对象时需要) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
</dependency>
<!-- Lettuce 连接池依赖 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
若项目已引入
spring-boot-starter-web,spring-boot-starter-json会被自动传递,可省去。
4、配置 application.yml
spring:
data:
redis:
host: 192.168.100.100 # Redis 服务器 IP
port: 6379
password: atguigu # 若没设密码则删除此行
database: 0
lettuce:
pool:
enabled: true
max-active: 16 # 最大连接数
max-idle: 8 # 最大空闲
min-idle: 4
max-wait: 3000ms
2、RedisTemplate 与 StringRedisTemplate
1、两者的关系
StringRedisTemplate是RedisTemplate<String, String>的子类,key/value 都按字符串处理。RedisTemplate<Object, Object>默认 key/value 都用 JDK 序列化,存进 Redis 是一串\xac\xed...乱码——不直观、不通用,所以一般要自定义序列化器。
记忆:简单字符串场景用 StringRedisTemplate;要存对象用自定义序列化器的 RedisTemplate。
2、StringRedisTemplate 基本用法
@SpringBootTest
class RedisTest {
@Autowired
private StringRedisTemplate redis; // key/value 都是 String
@Test
void stringOps() {
// 写
redis.opsForValue().set("hello:redis", "world");
// 读
String v = redis.opsForValue().get("hello:redis");
System.out.println("value = " + v);
}
}
3、自定义序列化器(存对象)
1、为什么要自定义
想让 value 以可读的 JSON形式存进 Redis,而不是 JDK 二进制乱码。key 用字符串序列化,value 用 JSON 序列化。
package com.atguigu.redis.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.GenericJacksonJsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import tools.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
// 构建多态类型校验器
BasicPolymorphicTypeValidator validator = BasicPolymorphicTypeValidator.builder()
// 允许自己项目下所有子类(改成你的根包名 com.atguigu.redis)
.allowIfSubType("com.atguigu.redis")
// 允许jdk集合类,List、Map等
.allowIfSubType("java.util")
.allowIfSubType("java.time")
.build();
// 传入validator开启default‑typing,JSON写入@class类型字段,支持反序列化POJO
GenericJacksonJsonRedisSerializer jsonSerializer = GenericJacksonJsonRedisSerializer.builder()
.enableDefaultTyping(validator)
.build();
template.setValueSerializer(jsonSerializer);
template.setHashValueSerializer(jsonSerializer);
template.afterPropertiesSet();
return template;
}
}
因为自定义了
redisTemplateBean,自动配置里的默认RedisTemplate<Object,Object>会因@ConditionalOnMissingBean被替换;StringRedisTemplate仍然自动提供,两者并存。
2、用 RedisTemplate 存对象
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Test
void hashObject() {
// 直接存一个 Java 对象(内部转 JSON)
Student stu = new Student(255, "Joseph", 243545.323);
redisTemplate.opsForValue().set("student:255", stu);
Object back = redisTemplate.opsForValue().get("student:255");
System.out.println(back);
}
⚠️ 铁律:存数据和取数据必须使用同一个序列化器,否则格式不一致读不出来。
4、操作器(Ops)速查
RedisTemplate 通过 opsForXxx() 拿到对应数据类型的操作器对象:
| 操作器 | 对应类型 | 常用方法示例 |
|---|---|---|
opsForValue() | string | set/get/increment/append |
opsForHash() | hash | put/get/entries/putAll/increment |
opsForList() | list | leftPush/rightPush/leftPop/rightPop/range/size |
opsForSet() | set | add/members/isMember/remove/size |
opsForZSet() | zset | add/range/reverseRange/rangeWithScores/incrementScore/rank |
opsForGeo() | geo | add/position/distance/radius |
opsForHyperLogLog() | hyperlog | add/size/union |
opsForValue().getBit/setBit | bitmap | 位操作 |
1、绑定 Key 操作(Bound)
boundXxxOps(key) 绑定 key 后,后续操作不用再传 key,适合对一个 key 连续操作:
BoundValueOperations<String, Object> ops = redisTemplate.boundValueOps("cart:user:1001");
ops.set("apple"); // 不需要再写 key
Object v = ops.get();
对应的还有 boundListOps / boundHashOps / boundSetOps / boundZSetOps。
2、五种类型代码演示
@Test
void listOps() {
BoundListOperations<String, Object> ops = redisTemplate.boundListOps("queue:demo");
ops.leftPushAll("task1", "task2", "task3");
List<Object> all = ops.range(0, -1); // [task3, task2, task1]
}
@Test
void zsetOps() {
BoundZSetOperations<String, Object> ops = redisTemplate.boundZSetOps("rank:game");
ops.add("jerry", 87);
ops.add("harry", 136);
ops.add("peter", 155);
Set<Object> top = ops.reverseRange(0, 1); // 前两名
}
5、底层客户端:Lettuce(默认)
1、为什么默认 Lettuce
- 基于 Netty,异步非阻塞,高并发性能更好。
- 线程安全,天然支持连接复用。
- 支持响应式(WebFlux)。
2、如果想用 Jedis
排除 Lettuce,手动引入 Jedis:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
| 对比项 | Jedis | Lettuce |
|---|---|---|
| 连接模型 | 同步阻塞,需连接池 | 异步非阻塞(Netty) |
| 线程安全 | 非线程安全,必须连接池 | 线程安全 |
| 适合 | 简单同步场景 | 高并发 / 响应式 |
教学/大部分项目用默认 Lettuce 即可,API 都一样(
RedisTemplate自动适配底层)。
6、自动化配置(够用版)
spring-boot-starter-data-redis 触发 RedisAutoConfiguration:
- 读取
spring.data.redis配置,创建连接工厂(Lettuce/Jedis)。 - 自动提供
RedisTemplate<Object,Object>与StringRedisTemplate两个 Bean(@ConditionalOnMissingBean,可被自定义覆盖)。
@AutoConfiguration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
public class RedisAutoConfiguration { ... }
7、课堂练习
- 建一个 SpringBoot 4 项目,引入 data-redis,配置连接本机 Redis,用
StringRedisTemplate完成set/get。 - 自定义
RedisTemplate<String,Object>,把Student对象存进去,用redis-cli查看 value 是 JSON 格式。 - 分别用
boundHashOps和opsForHash()存一个"用户对象"并读取。 - 用
opsForZSet().add添加三个成绩,用reverseRange取前两名(排行榜雏形)。
8、本节验证
- 会在 SpringBoot4 中引入 spring-boot-starter-data-redis 并配置 ✅
- 会为 Redis 配置 bind + requirepass 以允许远程连接(多个 IP 写在同一行),知道不建议关闭 protected-mode ✅
- 会用 RedisTemplate / StringRedisTemplate 读写数据 ✅
- 会自定义序列化器,用 RedisTemplate 存对象 ✅
- 会用 opsForXxx / Bound 操作器,知道默认 Lettuce、可切 Jedis ✅