Redis 作为当前最流行的高性能键值对缓存数据库,凭借其快速度、多数据类型支持、分布式友好等特性,成为后端开发中缓存、会话存储、消息队列等场景的必备工具。本文将从 Redis 基础入门,逐步讲解其在 SpringBoot 项目中的整合使用,以及如何结合 Spring Cache 实现缓存注解化开发,覆盖从 “环境搭建” 到 “实战应用” 的全流程。

Redis简介

数据类型

Redis 支持多种丰富的数据类型,覆盖大部分业务场景:

  • String(字符串):最基础类型,用于存储字符串、数字(如用户 token、计数器);
  • Hash(哈希):键值对集合,适合存储对象(如用户信息、商品详情);
  • List(列表):有序可重复集合,用于消息队列、最新消息列表;
  • Set(集合):无序不可重复集合,用于去重、交集 / 并集计算(如共同好友);
  • ZSet(有序集合):带分数的 Set,用于排序场景(如排行榜、延迟队列)。

关键优势

  • 高性能:基于内存操作,单节点 QPS 可达 10w+;
  • 持久化:支持 RDB 快照、AOF 日志两种持久化方式,避免内存数据丢失;
  • 分布式友好:支持主从复制、哨兵模式、Cluster 集群,轻松实现高可用与横向扩展;
  • 功能丰富:自带发布订阅、Lua 脚本、事务(弱事务)等特性。

Redis基本使用

Windows安装

安装Redis

下载地址:Releases · tporadowski/redis

Redis支持32位和64位。这个需要更具操作系统平台的实际情况决定

  1. Redis 官方仓库下载 Windows 版本安装包(选择最新稳定版);
  2. 解压到本地目录(如D:\redis);
  3. 建议将解压目录添加到系统环境变量(方便全局执行命令)。

配置修改

打开解压目录下的redis.windows.conf,修改常用配置:

# 端口(默认6379)
port 6379
# 密码(生产环境必须设置)
requirepass 123456
# 最大内存限制
maxmemory 2gb

 启动与连接

  • 启动服务端:在解压目录打开 cmd,执行:
    redis-server.exe redis.windows.conf
  • 启动客户端:新打开 cmd,执行(连接本地服务):
    redis-cli.exe -h 127.0.0.1 -p 6379 -a 123456

基本操作示例

以常用数据类型为例:

# String类型:设置/获取值
set username "zhangsan"
get username

# Hash类型:存储用户信息
hset user:1 id 1 name "zhangsan" age 20
hgetall user:1

# List类型:添加/获取列表元素
lpush list:demo "a" "b" "c"
lrange list:demo 0 -1

可视化工具

Another Redis Desktop Manager(ARDM)
  • 特点:开源免费、跨平台(Win/Mac/Linux)、支持全数据类型、集群 / 哨兵模式
  • 下载GitHub 仓库
  • 安装命令:
    # Windows (winget)
    winget install qishibo.AnotherRedisDesktopManager
    
    # Windows (chocolatey)
    choco install another-redis-desktop-manager

SpringBoot操作Redis

spring-boot-starter-data-redis

启动器核心作用

spring-boot-starter-data-redis 是 SpringBoot 提供的 Redis 快速集成启动器,其核心价值在于 “自动配置 + 依赖聚合”,无需手动导入多个零散依赖,也无需手动编写 Redis 连接配置类,SpringBoot 会自动完成大部分初始化工作,极大简化了 Redis 在 SpringBoot 项目中的集成成本。

启动器核心依赖拆解

该启动器内部默认聚合了以下核心依赖(无需手动引入):

  1. spring-data-redis:Spring Data 家族的核心组件,提供了对 Redis 操作的统一封装(封装了 Redis 客户端的 API,屏蔽了底层客户端的差异),是RedisTemplateStringRedisTemplate等操作类的核心依赖。
  2. lettuce-core:SpringBoot 2.x 及以上版本默认集成的 Redis 客户端(替代了 SpringBoot 1.x 的jedis),基于 Netty 实现,支持异步操作、线程安全、连接池复用,性能更优,适合高并发场景。
  3. spring-context-support:提供 Spring 容器对 Redis 的上下文支持,用于整合缓存、序列化等功能。

依赖引入代码(完整)

在 SpringBoot 项目的pom.xml中添加 Redis starter:

<!-- SpringBoot Redis 快速集成启动器 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<!-- 可选:若需要手动配置lettuce连接池,引入连接池依赖(启动器已间接依赖,可直接配置) -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>

配置 Redis 连接:基础配置 + 进阶优化

基础连接配置(application.yml

满足本地开发、简单生产环境的基础连接需求,配置项清晰易懂:

spring:
  redis:
    host: 127.0.0.1        # Redis服务IP地址(生产环境填写服务器内网IP或域名)
    port: 6379             # Redis服务端口(默认6379,可自定义修改)
    password: 123456       # Redis连接密码(生产环境必须设置,不能为空)
    timeout: 5000ms        # 连接超时时间(毫秒),超过该时间未连接成功则抛出异常
    database: 0            # 连接的Redis数据库索引(Redis默认有16个数据库,索引0-15,默认使用0库)
进阶配置:Lettuce 连接池优化

由于lettuce是线程安全的客户端,通过连接池可以复用连接,减少频繁创建 / 销毁连接的性能损耗,适合高并发场景:

spring:
  redis:
    host: 127.0.0.1
    port: 6379
    password: 123456
    timeout: 5000ms
    database: 0
    lettuce: # lettuce 客户端专属配置
      pool:
        max-active: 8      # 连接池最大活跃连接数(默认8,可根据并发量调整)
        max-idle: 8        # 连接池最大空闲连接数(默认8,建议与max-active一致)
        min-idle: 2        # 连接池最小空闲连接数(默认0,设置2-4个保底连接,提升响应速度)
        max-wait: -1ms     # 连接池最大等待时间(-1表示无限制,超过该时间未获取连接则抛出异常)

基本操作

RedisTemplate 与 StringRedisTemplate 详解

两个核心操作类的区别

SpringBoot 默认提供两个 Redis 操作类,核心差异在于序列化方式适用场景

操作类 键序列化方式 值序列化方式 适用场景
StringRedisTemplate StringRedisSerializer StringRedisSerializer 仅操作 String 类型数据(最常用,如缓存 token、简单键值对)
RedisTemplate<String, Object> 默认JdkSerializationRedisSerializer 默认JdkSerializationRedisSerializer 操作 Object、Hash、List 等复杂数据类型

注意:RedisTemplate 默认序列化方式会导致 Redis 中存储的键值出现乱码(以\xAC\xED\x00\x05t\x00\x03开头),因此需要自定义序列化配置

 完整操作示例(覆盖多数据类型)
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Service
public class RedisDemoService {
    // 注入StringRedisTemplate:专用于String类型操作
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    // 注入RedisTemplate:用于复杂数据类型操作
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    // ==================== String 类型操作 ====================
    public void operateString(String key, String value) {
        // 设置键值对,有效期1小时
        stringRedisTemplate.opsForValue().set(key, value, 1, TimeUnit.HOURS);
        // 获取键对应的值
        String result = stringRedisTemplate.opsForValue().get(key);
        // 递增操作(计数器场景:如文章阅读量、点赞数)
        stringRedisTemplate.opsForValue().increment("article:read:1001", 1);
        // 递减操作
        stringRedisTemplate.opsForValue().decrement("article:read:1001", 1);
    }

    // ==================== Hash 类型操作 ====================
    public void operateHash(String userId, User user) {
        String hashKey = "user:" + userId;
        // 存入单个字段
        redisTemplate.opsForHash().put(hashKey, "id", user.getId());
        redisTemplate.opsForHash().put(hashKey, "name", user.getName());
        // 存入整个对象(字段对应对象属性)
        redisTemplate.opsForHash().putAll(hashKey, user);
        // 获取单个字段值
        String userName = (String) redisTemplate.opsForHash().get(hashKey, "name");
        // 获取该Hash的所有字段和值
        Map<Object, Object> userMap = redisTemplate.opsForHash().entries(hashKey);
        // 获取该Hash的所有字段名
        Set<Object> fields = redisTemplate.opsForHash().keys(hashKey);
    }

    // ==================== List 类型操作(有序可重复) ====================
    public void operateList(String listKey) {
        // 从列表左侧添加元素(左进右出:队列模式)
        redisTemplate.opsForList().leftPush(listKey, "元素1");
        redisTemplate.opsForList().leftPushAll(listKey, "元素2", "元素3", "元素4");
        // 从列表右侧弹出元素
        Object rightPop = redisTemplate.opsForList().rightPop(listKey);
        // 从列表右侧添加元素(右进右出:栈模式)
        redisTemplate.opsForList().rightPush(listKey, "元素5");
        // 获取列表指定区间的元素(0:起始索引,-1:结束索引,表示所有元素)
        List<Object> list = redisTemplate.opsForList().range(listKey, 0, -1);
        // 获取列表长度
        Long size = redisTemplate.opsForList().size(listKey);
    }

    // ==================== Set 类型操作(无序不可重复) ====================
    public void operateSet(String setKey) {
        // 添加元素
        redisTemplate.opsForSet().add(setKey, "a", "b", "c", "c"); // 重复元素"c"会自动去重
        // 获取Set中所有元素
        Set<Object> members = redisTemplate.opsForSet().members(setKey);
        // 判断元素是否在Set中
        Boolean isExist = redisTemplate.opsForSet().isMember(setKey, "a");
        // 求两个Set的交集(共同好友场景)
        Set<Object> intersect = redisTemplate.opsForSet().intersect("set:1", "set:2");
        // 删除指定元素
        redisTemplate.opsForSet().remove(setKey, "a");
    }
}

 工具类封装:完整实用的 RedisUtil

为简化 Redis 操作,避免重复编写模板代码,封装一个支持多数据类型、包含常用操作的RedisUtil工具类,可直接在项目中复用:

package com.yugongyun.admin.utils;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import jakarta.annotation.Resource;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * Redis工具类
 *
 * @author ktjy
 * @since 2025-09-28
 */
@Component
public class RedisUtil {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    // =============================common============================

    /**
     * 指定缓存失效时间
     *
     * @param key  键
     * @param time 时间(秒)
     * @return
     */
    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;
        }
    }

    /**
     * 根据key 获取过期时间
     *
     * @param key 键 不能为null
     * @return 时间(秒) 返回0代表为永久有效
     */
    public long getExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }

    /**
     * 判断key是否存在
     *
     * @param key 键
     * @return true 存在 false不存在
     */
    public boolean hasKey(String key) {
        try {
            return redisTemplate.hasKey(key);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 删除缓存
     *
     * @param key 可以传一个值 或多个
     */
    @SuppressWarnings("unchecked")
    public void del(String... key) {
        if (key != null && key.length > 0) {
            if (key.length == 1) {
                redisTemplate.delete(key[0]);
            } else {
                redisTemplate.delete((Collection<String>) CollectionUtils.arrayToList(key));
            }
        }
    }

    // ============================String=============================

    /**
     * 普通缓存获取
     *
     * @param key 键
     * @return 值
     */
    public Object get(String key) {
        return key == null ? null : redisTemplate.opsForValue().get(key);
    }

    /**
     * 普通缓存放入
     *
     * @param key   键
     * @param value 值
     * @return true成功 false失败
     */
    public boolean set(String key, Object value) {
        try {
            redisTemplate.opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 普通缓存放入并设置时间
     *
     * @param key   键
     * @param value 值
     * @param time  时间(秒) time要大于0 如果time小于等于0 将设置无限期
     * @return true成功 false 失败
     */
    public boolean set(String key, Object value, long time) {
        try {
            if (time > 0) {
                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
            } else {
                set(key, value);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 递增
     *
     * @param key   键
     * @param delta 要增加几(大于0)
     * @return
     */
    public long incr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("递增因子必须大于0");
        }
        return redisTemplate.opsForValue().increment(key, delta);
    }

    /**
     * 递减
     *
     * @param key   键
     * @param delta 要减少几(小于0)
     * @return
     */
    public long decr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("递减因子必须大于0");
        }
        return redisTemplate.opsForValue().increment(key, -delta);
    }

    // ================================Map=================================

    /**
     * HashGet
     *
     * @param key  键 不能为null
     * @param item 项 不能为null
     * @return 值
     */
    public Object hget(String key, String item) {
        return redisTemplate.opsForHash().get(key, item);
    }

    /**
     * 获取hashKey对应的所有键值
     *
     * @param key 键
     * @return 对应的多个键值
     */
    public Map<Object, Object> hmget(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * HashSet
     *
     * @param key 键
     * @param map 对应多个键值
     * @return true 成功 false 失败
     */
    public boolean hmset(String key, Map<String, Object> map) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * HashSet 并设置时间
     *
     * @param key  键
     * @param map  对应多个键值
     * @param time 时间(秒)
     * @return true成功 false失败
     */
    public boolean hmset(String key, Map<String, Object> map, long time) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 向一张hash表中放入数据,如果不存在将创建
     *
     * @param key   键
     * @param item  项
     * @param value 值
     * @return true 成功 false失败
     */
    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;
        }
    }

    /**
     * 向一张hash表中放入数据,如果不存在将创建
     *
     * @param key   键
     * @param item  项
     * @param value 值
     * @param time  时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
     * @return true 成功 false失败
     */
    public boolean hset(String key, String item, Object value, long time) {
        try {
            redisTemplate.opsForHash().put(key, item, value);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 删除hash表中的值
     *
     * @param key  键 不能为null
     * @param item 项 可以使多个 不能为null
     */
    public void hdel(String key, Object... item) {
        redisTemplate.opsForHash().delete(key, item);
    }

    /**
     * 判断hash表中是否有该项的值
     *
     * @param key  键 不能为null
     * @param item 项 不能为null
     * @return true 存在 false不存在
     */
    public boolean hHasKey(String key, String item) {
        return redisTemplate.opsForHash().hasKey(key, item);
    }

    /**
     * hash递增 如果不存在,就会创建一个 并把新增后的值返回
     *
     * @param key  键
     * @param item 项
     * @param by   要增加几(大于0)
     * @return
     */
    public double hincr(String key, String item, double by) {
        return redisTemplate.opsForHash().increment(key, item, by);
    }

    /**
     * hash递减
     *
     * @param key  键
     * @param item 项
     * @param by   要减少记(小于0)
     * @return
     */
    public double hdecr(String key, String item, double by) {
        return redisTemplate.opsForHash().increment(key, item, -by);
    }

    // ============================set=============================

    /**
     * 根据key获取Set中的所有值
     *
     * @param key 键
     * @return
     */
    public Set<Object> sGet(String key) {
        try {
            return redisTemplate.opsForSet().members(key);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 根据value从一个set中查询,是否存在
     *
     * @param key   键
     * @param value 值
     * @return true 存在 false不存在
     */
    public boolean sHasKey(String key, Object value) {
        try {
            return redisTemplate.opsForSet().isMember(key, value);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将数据放入set缓存
     *
     * @param key    键
     * @param values 值 可以是多个
     * @return 成功个数
     */
    public long sSet(String key, Object... values) {
        try {
            return redisTemplate.opsForSet().add(key, values);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 将set数据放入缓存
     *
     * @param key    键
     * @param time   时间(秒)
     * @param values 值 可以是多个
     * @return 成功个数
     */
    public long sSetAndTime(String key, long time, Object... values) {
        try {
            Long count = redisTemplate.opsForSet().add(key, values);
            if (time > 0)
                expire(key, time);
            return count;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 获取set缓存的长度
     *
     * @param key 键
     * @return
     */
    public long sGetSetSize(String key) {
        try {
            return redisTemplate.opsForSet().size(key);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 移除值为value的
     *
     * @param key    键
     * @param values 值 可以是多个
     * @return 移除的个数
     */
    public long setRemove(String key, Object... values) {
        try {
            Long count = redisTemplate.opsForSet().remove(key, values);
            return count;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    // ===============================list=================================

    /**
     * 获取list缓存的内容
     *
     * @param key   键
     * @param start 开始
     * @param end   结束 0 到 -1代表所有值
     * @return
     */
    public List<Object> lGet(String key, long start, long end) {
        try {
            return redisTemplate.opsForList().range(key, start, end);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 获取list缓存的长度
     *
     * @param key 键
     * @return
     */
    public long lGetListSize(String key) {
        try {
            return redisTemplate.opsForList().size(key);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 通过索引 获取list中的值
     *
     * @param key   键
     * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
     * @return
     */
    public Object lGetIndex(String key, long index) {
        try {
            return redisTemplate.opsForList().index(key, index);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 将list放入缓存
     *
     * @param key   键
     * @param value 值
     * @return
     */
    public boolean lSet(String key, Object value) {
        try {
            redisTemplate.opsForList().rightPush(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将list放入缓存
     *
     * @param key   键
     * @param value 值
     * @param time  时间(秒)
     * @return
     */
    public boolean lSet(String key, Object value, long time) {
        try {
            redisTemplate.opsForList().rightPush(key, value);
            if (time > 0)
                expire(key, time);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将list放入缓存
     *
     * @param key   键
     * @param value 值
     * @return
     */
    public boolean lSet(String key, List<Object> value) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将list放入缓存
     *
     * @param key   键
     * @param value 值
     * @param time  时间(秒)
     * @return
     */
    public boolean lSet(String key, List<Object> value, long time) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            if (time > 0)
                expire(key, time);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 根据索引修改list中的某条数据
     *
     * @param key   键
     * @param index 索引
     * @param value 值
     * @return
     */
    public boolean lUpdateIndex(String key, long index, Object value) {
        try {
            redisTemplate.opsForList().set(key, index, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 移除N个值为value
     *
     * @param key   键
     * @param count 移除多少个
     * @param value 值
     * @return 移除的个数
     */
    public long lRemove(String key, long count, Object value) {
        try {
            Long remove = redisTemplate.opsForList().remove(key, count, value);
            return remove;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }
}

封装

SpringBoot集成Redis使用Cache缓存

RedisConfig配置

配置核心作用
  1. 解决RedisTemplate默认序列化乱码问题(默认使用JdkSerializationRedisSerializer,会导致 Redis 中键值出现二进制乱码,无法直接通过命令行或可视化工具查看);
  2. 配置CacheManager,将 Spring Cache 的缓存介质指定为 Redis,同时设置缓存默认过期时间、序列化方式等。
    
    @Configuration
    public class RedisConfig {
    
        @Bean
        public KeyGenerator keyGenerator() {
            return new KeyGenerator() {
                @Override
                public Object generate(Object target, Method method, Object... params) {
                    StringBuilder sb = new StringBuilder();
                    sb.append(target.getClass().getName());
                    sb.append(method.getName());
                    for (Object obj : params) {
                        sb.append(obj.toString());
                    }
                    return sb.toString();
                }
            };
        }
    
        @Bean
        public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
            RedisTemplate<String, Object> template = new RedisTemplate<>();
           template.setConnectionFactory(factory);
           // 解决查询缓存转换异常的问题
            ObjectMapper om = new ObjectMapper();
            om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            // 注册JavaTimeModule以支持LocalDateTime等Java 8时间类型
            om.registerModule(new JavaTimeModule());
            om.activateDefaultTyping(om.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL);
            Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(om, Object.class);
    
            // 2. 设置 value 的序列化规则和 key 的序列化规则
            template.setValueSerializer(new StringRedisSerializer()); // 修改:对值使用StringRedisSerializer
            template.setKeySerializer(new StringRedisSerializer());
            template.afterPropertiesSet();
            return template;
        }
    
    
        /**
         * 配置缓存管理器(支持 @Cacheable/@CachePut/@CacheEvict 等注解)
         * @param redisConnectionFactory Redis 连接工厂
         * @return 缓存管理器
         */
        @Bean
        public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
            // 1. 复用序列化配置
            RedisSerializer<String> stringSerializer = new StringRedisSerializer();
           //解决缓存转换异常问题
            ObjectMapper om = new ObjectMapper();
            om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            // 注册JavaTimeModule以支持LocalDateTime等Java 8时间类型
            om.registerModule(new JavaTimeModule());
            om.activateDefaultTyping(om.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL);
            Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(om, Object.class);
    
            // 配置序列化解决乱码问题
            RedisCacheConfiguration cacheConfig = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofDays(7))
                    .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(stringSerializer)).
                    serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
                    .disableCachingNullValues();
    
            // 3. 构建缓存管理器
            return RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(cacheConfig).build();
        }
    
    }

Cache注解

Spring Cache 提供了多个注解实现缓存管理,核心 3 个注解的详细说明如下:

注解名 核心作用 常用属性及说明
@Cacheable 查询缓存:先查缓存,无缓存则执行方法并写入缓存 1. value:缓存名称(必填,用于区分不同缓存组,如 "user")2. key:缓存键(支持 SpEL 表达式,如 "#userId")3. condition:缓存条件(满足条件才缓存,如 "#userId > 0")4. unless:排除条件(满足条件不缓存,如 "#result == null")
@CachePut 更新缓存:执行方法后,将结果写入缓存(不查缓存) 1. value:缓存名称(与 @Cacheable 一致)2. key:缓存键(与 @Cacheable 一致,保证更新同一个缓存)
@CacheEvict 删除缓存:删除指定缓存或缓存组 1. value:缓存名称2. key:缓存键(指定删除某个缓存)3. allEntries:是否删除该缓存组所有缓存(默认 false,如allEntries = true

测试使用

  1. 在UserService中编写测试方法(没有这个类的自行创建一个业务类调用数据层的方法)添加一个getUser的方法:
    @Service
    public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
        @Autowired
        private UserMapper userMapper;
        @Cacheable(value = "user",keyGenerator = "keyGenerator")
        public User getUser(Long id){
            return userMapper.selectById(id);
        }
    }
  2. 编写测试方法:
        @Test
        public void testCache(){
            User user = userService.getUser(2L);
            System.out.println(user.getUsrName());
        }
  3. 执行测试方法:
    1. 第一次执行我们可以从sql日志中看出有执行过sql语句:
    2. 第二次执行因为走的是缓存所以并没有执行sql:

总结

本文从 Redis 基础(数据类型、安装使用),到 SpringBoot 的原生整合,再到结合 Spring Cache 实现注解化缓存,覆盖了 Redis 在 Java 后端开发中的核心使用场景。掌握这些内容后,即可在项目中快速落地 Redis 缓存,提升系统性能。

    Logo

    汇聚全球AI编程工具,助力开发者即刻编程。

    更多推荐