Lecture06-Redis-SpringBoot4整合

文章目录

    [toc]

    单词词性音标含义
    serializern./ˈsɪrəlaɪzə(r)/序列化器(对象↔字节流/JSON 的工具,Redis 存取必配)
    templaten./ˈtempleɪt/模板(RedisTemplate 是 Spring Data Redis 提供的操作封装)
    lettucen./ˈletɪs/生菜(SpringBoot 默认的 Redis 客户端,基于 Netty 异步)
    jedisn./ˈdʒedɪs/一种同步阻塞的 Redis Java 客户端
    jacksonn./ˈdʒæksən/JSON 处理库,序列化 value 用
    boundadj./baʊnd/绑定的(Bound 操作器:绑定 key 后所有操作围绕该 key)
    pooln./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 里有两个关键配置:

    配置作用
    bindRedis 监听哪些网卡地址(决定允许谁连)
    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;
        }
    }
    
    

    因为自定义了 redisTemplate Bean,自动配置里的默认 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()stringset/get/increment/append
    opsForHash()hashput/get/entries/putAll/increment
    opsForList()listleftPush/rightPush/leftPop/rightPop/range/size
    opsForSet()setadd/members/isMember/remove/size
    opsForZSet()zsetadd/range/reverseRange/rangeWithScores/incrementScore/rank
    opsForGeo()geoadd/position/distance/radius
    opsForHyperLogLog()hyperlogadd/size/union
    opsForValue().getBit/setBitbitmap位操作

    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>
    
    对比项JedisLettuce
    连接模型同步阻塞,需连接池异步非阻塞(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、课堂练习

    1. 建一个 SpringBoot 4 项目,引入 data-redis,配置连接本机 Redis,用 StringRedisTemplate 完成 set/get。
    2. 自定义 RedisTemplate<String,Object>,把 Student 对象存进去,用 redis-cli 查看 value 是 JSON 格式。
    3. 分别用 boundHashOps 和 opsForHash() 存一个"用户对象"并读取。
    4. 用 opsForZSet().add 添加三个成绩,用 reverseRange 取前两名(排行榜雏形)。

    8、本节验证

    • 会在 SpringBoot4 中引入 spring-boot-starter-data-redis 并配置 ✅
    • 会为 Redis 配置 bind + requirepass 以允许远程连接(多个 IP 写在同一行),知道不建议关闭 protected-mode ✅
    • 会用 RedisTemplate / StringRedisTemplate 读写数据 ✅
    • 会自定义序列化器,用 RedisTemplate 存对象 ✅
    • 会用 opsForXxx / Bound 操作器,知道默认 Lettuce、可切 Jedis ✅
    end