苍穹外卖-day05-Java整合Redis的核心操作
这是一篇承上的文章,上一篇主要讲了有关Redis的一些入门知识和操作命令,这一篇就具体介绍实战中如何让Java与Redis取得具体的联系,有些许不清楚的地方,你也许会在上一篇找到答案!
【作者说:这篇文章主要聚焦用我们Java学习者的视角来整合一些Redis的具体操作,简单来讲就是让我们在Java中操作Redis,跟我们在Java中操作MySQL一样,我们也需要一个定位与MyBatis一样的工具Spring Data Redis;它们都是Spring生态里面的简化持久层操作的框架,总体来说,这是对Spring框架的深度理解】
一. 导入Spring Data Redis的maven框架
- 核心定义
- Spring Data Redis 是Spring 生态中用于简化 Redis 操作的持久层框架,是 Spring 对 Redis 底层客户端(如 Lettuce、Jedis)的高度封装,属于spring-boot-starter系列的官方起步依赖,引入该 Maven 坐标后,可快速集成 Spring Data Redis 到 Spring Boot 项目中,无需手动配置底层 Redis 客户端的连接、初始化等复杂操作。
简单理解为:Spring 官方已经把 Redis 相关的底层客户端、连接管理、API 封装等全部写进了 spring-boot-starter-data-redis 起步依赖里。我们项目要使用 Redis,只需要在 pom.xml 中引入这个依赖,再通过 @Autowired 注入 RedisTemplate 等对象(操作Redis必须要用对象操作),就能直接调用方法操作 Redis,不用自己写底层代码。
- 拓展疑问:为什么必须要通过 @Autowired 注入 RedisTemplate 等对象来操作Redis数据库呢?
因为 RedisTemplate 自己造不出来,它太复杂了,必须让 Spring 帮你造好,你再拿来用。RedisTemplate = 一个万能工具箱里面装好了操作 Redis 所有数据结构的工具,不用再手动一个个new,只需要对象调用就行,极大减少了代码的冗余度与复杂性。
- 操作原因
- 简化开发:直接引入封装好的起步依赖,避免手动添加 Redis 客户端(Jedis/Lettuce)、连接池等多个依赖,减少依赖冲突风险。
- 生态融合:与 Spring Boot 自动配置机制结合,可通过配置文件快速完成 Redis 数据源配置,无需编写繁琐的连接代码。
- 统一 API:提供RedisTemplate等工具类,封装了 Redis 的各类数据结构(字符串、哈希、列表等)操作,统一了不同 Redis 客户端的调用方式。
- 代码展示
//<!-- Spring Data Redis起步依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
- 代码讲解
- dependency:Maven 的依赖声明标签,用于指定项目需要引入的外部 jar 包。(只要是依赖就必须有)
- groupId:组织 / 公司级别的唯一标识,org.springframework.boot表示该依赖属于 Spring Boot 官方提供的核心组件。
- artifactId:依赖的项目唯一标识,spring-boot-starter-data-redis是 Spring Boot 为 Redis 集成定制的起步依赖,会自动引入 Spring Data Redis 核心包、默认的 Redis 客户端(Lettuce)及相关依赖。
- 注:该依赖未显式指定时,会继承 Spring Boot 父工程的版本,保证依赖版本兼容性。
- 格式上需要注意有开有合,形式上随意,最好以上面格式为准,保证代码格式正确以及代码优雅。
- 拓展了解:Spring Data Redis 核心常用注解
@Cacheable:查询数据时,先查缓存,缓存不存在则执行方法并将结果存入缓存 @CachePut:更新 / 新增数据后,自动同步更新缓存 @CacheEvict:删除数据时,自动清除对应缓存 @RedisHash:标记实体类,以 Hash 结构存入 Redis
官方完整注解文档地址:
二、配置 Redis 的数据源
- 核心定义
- 配置 Redis 的数据源是指在 Spring Boot 项目中,通过配置文件(如application.yml/application.properties)指定 Redis 服务的连接信息(地址host、端口port、密码password、数据库索引database等),Spring Boot 会根据这些配置自动初始化 Redis 连接工厂和RedisTemplate,实现 Java 程序与 Redis 服务器的通信,是 Spring Data Redis 集成的核心步骤。
数据源(DataSource) = 一套能连接到数据库的 “配置信息 + 连接工具”,简单来讲:就是用一些配置信息锁定要用哪一个数据库,然后再用连接工具进行指定连接 自动初始化 Redis 连接工厂 = Spring Boot 自动帮你创建好 “连接工具”,简单来讲:就是制造这个连接渠道的工具,RedisConnectionFactory是一个大工厂,里面Spring Boot这个机器人搭建了一个流水线专门制造一个名为Redis Connect的连接器,用这个连接器来连接Redis的服务器
- 操作原因
- 建立通信:Redis 作为独立的缓存服务器,Java 程序需通过配置数据源信息才能与 Redis 服务建立网络连接。
- 定制化连接:可根据项目需求设置数据库索引(Redis 默认 16 个数据库)、连接超时时间、密码验证等,适配不同的 Redis 部署环境(单机、集群)。
- 自动配置生效:Spring Boot 的自动配置机制会读取数据源配置,无需手动编写JedisConnectionFactory/LettuceConnectionFactory等连接工厂代码,简化开发。
【Java项目A】 【Java项目B】 【Java项目C】
│ │ │
└──────────┬───────┴──────────┬───────┘
│ │
【Redis 缓存小卖部】 【MySQL 永久大仓库】
(独立服务器) (独立服务器)
很多企业级项目的数据存储都是通过MySQL+Redis来存储数据的 前者用来存放所有核心、持久化业务数据,,它把数据存储在磁盘里,占用资源少、成本低、可以海量存储,数据永久保存不会丢失。 而后者通常专门用来存放高并发、热点、频繁访问的数据,因为它将数据存储在内存中,读写速度极快,但内存成本高、资源消耗大,不适合存大量冷数据,只做缓存临时使用。
总结一下就是:磁盘数据库 MySQL 负责持久化存海量核心数据,内存缓存 Redis 负责加速扛高并发,两者互补搭配,是企业项目标准架构。
- 代码展示(具体展示)
- 以application.yml配置文件为例(苍穹外卖项目主流配置方式):
redis: #Redis配置文件//注意是在Spring框架下
host: ${sky.redis.host}
port: ${sky.redis.port}
password: ${sky.redis.password}
database: ${sky.redis.database}
通过以上代码,我们可以看出Redis里的配置信息,后面都带有${...}这个符号,它的作用是读取配置文件里的值,而且里面的sky.redis都保持格式一致,这又涉及到外部化配置与多环境配置分离的知识(后面会进行讲解)
- 以application-dev.yml配置文件为例:
redis: #Redis配置文件(开发环境下,用于本地开发调试)
host: localhost #Redis所在服务器地址 本地就是localhost
port: 6379 #Redis本地服务器端口
password: 123456 #Redis设置的密码
database: 0 #Redis内部对应的具体数据库序号,默认为0
代码讲解(具体到代码关键字的用途)
- spring.redis.host:指定 Redis 服务器的 IP 地址,本地开发通常为localhost,生产环境需填写 Redis 服务器的实际 IP。
- spring.redis.port:Redis 服务的默认端口为6379,若 Redis 配置了自定义端口需对应修改。
- spring.redis.password:Redis 的访问密码,若 Redis 未设置密码可删除该配置项,生产环境建议开启密码验证。
- spring.redis.database:Redis 的数据库索引,用于逻辑隔离不同业务的缓存数据,默认使用第 0 个数据库。
拓展了解:外部化配置与多环境配置分离
- 核心定义
- 外部化配置:将项目的配置信息(如 Redis 数据源、数据库连接、端口号等)从代码中抽离,存储在配置文件、环境变量、命令行参数等外部介质中,实现配置与代码的解耦。
- 多环境配置分离:基于外部化配置,将开发、测试、生产等不同环境的配置信息拆分到独立文件中,通过指定激活环境快速切换配置,适配不同部署场景。
- 核心价值
- 降低耦合:配置变更无需修改代码和重新编译,直接修改外部配置文件即可生效。
- 环境隔离:避免开发环境配置污染生产环境,防止敏感信息(如数据库密码)硬编码在代码中。
- 部署高效:同一份代码可通过切换配置适配不同环境,简化运维部署流程。
简单来说:再修改过程中直接在application-dev.yml里面修改信息,比如换端口,地址等;一般来讲这个application.yml文件是会被提交在代码仓库,所以密码不能明文裸写,把它拆分多环境配置、进行配置隔离;在运维过程中,可以直接用着这套万能代码。
- Spring Boot 实现方式
-
- 多环境配置文件命名规则
按application-{环境名}.yml/properties命名,例如:
application-dev.yml:开发环境 application-test.yml:测试环境 application-prod.yml:生产环境
-
- 在主配置文件 application.yml 中,通过一行配置指定要启用的环境:
spring: #Spring框架核心配置节点,在YAML里声明Spring相关配置的根层级
profiles: #环境配置分组,用来隔离不同环境配置的机制
active: dev #激活开发环境配置,加载application-dev.yml 的环境特定配置
- 实战思想:
- 同一环境配置分离:
application.yml 与 application-dev.yml 都属于开发环境配置,二者实现同一环境下的配置分离。 application.yml:全局主配置文件,负责公共参数、环境激活等基础配置。 application-dev.yml:开发环境专属子配置文件,专门存放本地开发所需的 Redis、数据库等个性化配置
- 多环境配置隔离
项目拥有 dev(开发)、test(测试)、prod(生产)多套独立配置文件。 不同环境拥有各自独立的 Redis 连接地址、密码、数据库等配置,只需在主配置中切换激活环境,即可无缝切换部署环境,无需改动业务代码,适配项目线上线下不同场景。
三、编写配置类,创建RedisTemplate
- 核心定义
- 编写Redis配置类(通常命名为RedisConfig),手动创建并配置RedisTemplate对象,是Spring Boot整合Redis的核心步骤。RedisTemplate是Spring Data Redis提供的核心工具类,封装了Redis各类数据结构(String、Hash、List等)的操作方法,通过配置类自定义其序列化规则、连接工厂等,解决默认配置的不足(如默认序列化乱码),实现更灵活、规范的Redis操作。
对于RedisTemplate对象,上面已经进行了小小的剧透,主要是需要符合面向对象编程的思维,用对象来操作Redis,这个对象功能更强大,封装了具体操作方法,使用的时候直接具体调用,例如redisTemplate.opsForHash()来操作哈希表等,这样更加灵活规范,并且更有可读性。
- 操作原因
- 解决默认序列化问题:Spring Boot自动配置的RedisTemplate,默认使用JDK序列化,会导致Redis缓存数据乱码、可读性差,需通过配置类自定义序列化规则(如Jackson2JsonRedisSerializer)。
- 自定义配置适配需求:可根据项目需求,配置RedisTemplate的连接工厂、序列化方式、超时时间等,适配不同的Redis部署环境(与前文多环境配置呼应)。
- 统一工具类管理:将RedisTemplate配置在独立配置类中,便于项目统一管理、维护,避免重复编码,提升代码规范性。
- 代码展示
@Configuration// 1. 声明这是一个 Spring 配置类,让项目启动时自动加载这里的配置
@Slf4j//2. 开启日志功能,方便打印提示信息
public class RedisConfiguration {
@Bean
/*Spring框架核心注解,作用是把你方法里 new 出来的这个 RedisTemplate 对象
交给 Spring 的容器来管理,这样后面你在其他类里想用 RedisTemplate 的时候,
直接用 @Autowired 注入就行,不用再手动 new 了。*/
// 方法作用:手动创建一个功能完整的 RedisTemplate 对象
// 参数:自动注入 Spring 已经创建好的 Redis 连接工厂(造连接的工具)
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){
log.info("开始创建RedisTemplate对象...");
//创建 RedisTemplate 对象(真正用来操作 Redis 的工具)
RedisTemplate redisTemplate = new RedisTemplate();
// 给 RedisTemplate 设置连接工厂
// 作用:让工具知道怎么连接 Redis 服务器(地址、端口、密码都在连接工厂里)
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 设置 Redis 的 key 序列化方式
// 作用:把 key 转成字符串格式,防止存入 Redis 后出现乱码
redisTemplate.setKeySerializer(new StringRedisSerializer());
return redisTemplate;
}
}
- 代码讲解
- @Configuration:标识该类是Spring的配置类,Spring会自动扫描并加载该类,将类中@Bean注解的方法返回值交给容器管理。
- @Bean:将方法返回的RedisTemplate对象,注册到Spring容器中,供项目中其他组件(如Service)通过@Autowired注入使用。
- RedisConnectionFactory:Redis连接工厂,由Spring Boot根据前文配置的application-dev.yml中的Redis数据源信息,自动创建并注入,无需手动编写。
- RedisTemplate<String, Object>:指定RedisTemplate的key为String类型、value为Object类型,适配大多数项目的缓存需求(key用字符串,value存实体类/集合等)。
- StringRedisSerializer:字符串序列化器,专门用于Redis的key序列化,避免key乱码,是项目中最常用的key序列化方式。
- setKeySerializer/setValueSerializer等方法:分别配置Redis的key、value、哈希结构的key和value的序列化方式,确保所有缓存数据都能正常显示。
- 拓展了解拓展了解:RedisTemplate 序列化定制
- 核心定义
- RedisTemplate 默认采用 JDK 原生序列化方式 完成数据存储,该序列化仅支持 Java 内部识别,存入 Redis 的键值会出现乱码、前缀特殊字符、对象无法跨项目读取等问题。
- 序列化定制,指在自定义配置类中,手动替换 Key、Value、HashKey、HashValue 的序列化器,统一修改 RedisTemplate 底层序列化规则,实现数据可读、对象序列化兼容、存储格式标准化的配置优化操作。
- 核心价值
- 解决默认 JDK 序列化带来的数据乱码、键值格式异常问题,保证 Redis 客户端控制台数据正常可视。
- 实现 Java 对象直接序列化存储,无需手动进行对象与 JSON 字符串的转换,简化开发代码。
- 实现跨服务、跨项目数据互通,序列化后的 JSON 格式数据可被任意后端语言解析读取。
- 统一项目全局 Redis 存储格式,规范缓存数据结构,提升项目维护性。
- Spring Boot 实现方式
- 引入 JSON 序列化相关依赖,提供对象转 JSON、JSON 转回 Java 对象的底层能力。
- 在已编写完成的 RedisTemplate 配置类内部,自定义 ObjectMapper 对象,统一配置日期格式化、空值处理、属性序列化规则。
- 创建 Jackson2JsonRedisSerializer JSON 序列化器实例,绑定自定义配置的对象映射规则。
- 依次为 RedisTemplate 的 Key、Value、HashKey、HashValue 分别指定序列化器,覆盖默认 JDK 序列化。
- 将配置完成的 RedisTemplate 注入 Spring 容器,项目全局所有调用均使用该定制化模板。
- 实战思想
- 统一序列化规则 将整个项目所有的 Redis 序列化配置全部收敛在单个配置类中统一管理,所有业务模块使用同一个 RedisTemplate 模板,全项目缓存序列化标准完全统一,杜绝不同模块序列化格式混乱、数据无法互相解析的问题。
- 多序列化策略隔离 根据不同业务缓存场景,区分采用不同序列化方案:
短时效简单数据(验证码、token):使用 String 序列化,轻量化高效存储; 复杂业务对象(用户信息、商品数据、订单实体):使用 JSON 序列化,满足对象完整存储与解析需求; 实现场景化序列化策略隔离,兼顾性能与数据兼容性。
四、RedisTemplate 操作各数据类型的方法
- 核心定义
- RedisTemplate 提供了专门的操作视图对象(OpsForXxx),用于操作 Redis 的五大数据类型:字符串(String)、哈希(Hash)、列表(List)、集合(Set)、有序集合(Sorted Set)。每个视图对象封装了对应数据结构的增删改查方法,Java 调用这些方法就等同于在 Redis 客户端执行对应的命令,是 Spring Data Redis 操作 Redis 的核心手段。
简单来讲:RedisTemplate 像一个万能遥控器,上面有很多按钮(操作视图),每个按钮组(opsForValue、opsForHash、opsForList 等)对应一种 Redis 数据结构。想操作哪种数据,就按哪个按钮,不用记 Redis 的原始命令,一个方法调用就能搞定。
小小类比一下:Spring大框架用Mybatis框架在Mapper文件里通过写具体接口和相应的映射文件来用Java语言操控MySQL数据库,在这里就是Spring大框架用Spring Data Redis框架直接用Java语言通过RedisTemplate 对象来调用它已经封装好了Redis的各个数据类型从而实现对Redis缓存的操控。
- 操作原因
- 方法统一:所有操作都通过 RedisTemplate 统一调用,无需直接接触底层 Redis 客户端(Jedis/Lettuce),代码风格一致,维护成本低。
- 类型安全:通过泛型(
<String, Object>)约束 key 和 value 的类型,在编译阶段就能发现类型错误,减少运行时异常。 - 覆盖全面:每种数据结构都有对应的 OpsForXxx 接口,涵盖了实战中 90% 以上的缓存场景需求,无需二次封装。
- 链式操作:多数方法支持直接调用返回值,实现链式编程,代码简洁美观。
- 代码展示
// 注入 RedisTemplate(上一节已配置好序列化方式)
@Autowired
private RedisTemplate redisTemplate;
// ===== String(字符串)操作 =====
redisTemplate.opsForValue().set("name", "zhangsan"); // 存
Object name = redisTemplate.opsForValue().get("name"); // 取
redisTemplate.opsForValue().set("code", "6666", 5, TimeUnit.MINUTES); // 存并设置5分钟过期
// ===== Hash(哈希)操作 =====
redisTemplate.opsForHash().put("user:1", "name", "lisi"); // 存单个字段
redisTemplate.opsForHash().put("user:1", "age", 18);
Object fieldValue = redisTemplate.opsForHash().get("user:1", "name"); // 取单个字段
Map<Object, Object> userMap = redisTemplate.opsForHash().entries("user:1"); // 取所有字段
// ===== List(列表)操作 =====
redisTemplate.opsForList().leftPush("foodList", "dumpling"); // 左侧插入
redisTemplate.opsForList().leftPush("foodList", "noodle");
redisTemplate.opsForList().leftPush("foodList", "rice");
Long size = redisTemplate.opsForList().size("foodList"); // 获取列表长度
List<Object> food = redisTemplate.opsForList().range("foodList", 0, -1); // 获取全部元素
// ===== Set(集合)操作 =====
redisTemplate.opsForSet().add("tags", "java", "redis", "spring"); // 添加元素
Set<Object> allTags = redisTemplate.opsForSet().members("tags"); // 获取所有成员
Boolean hasTag = redisTemplate.opsForSet().isMember("tags", "java"); // 判断是否存在
// ===== 通用操作(所有数据类型均适用) =====
redisTemplate.delete("name"); // 删除指定 key
Boolean exists = redisTemplate.hasKey("user:1"); // 判断 key 是否存在
Boolean expire = redisTemplate.expire("name", 10, TimeUnit.SECONDS); // 设置过期时间
- 代码讲解
- opsForValue():获取操作 String 类型数据的视图对象,set() 存值,get() 取值,支持存对象、存集合,是项目中最频繁使用的操作视图。
- set(key, value, timeout, TimeUnit):带过期时间的存值方法,常用于验证码、临时令牌等需要自动失效的业务场景,TimeUnit 可选.SECONDS/.MINUTES/.HOURS 等时间单位。
- opsForHash():获取操作 Hash 类型数据的视图对象,put() 存单个字段,entries() 一次性获取 Hash 所有字段和值,适合存用户画像、商品详情等多属性数据。
- opsForList():获取操作 List 类型数据的视图对象,leftPush() 从列表左侧插入,range() 按索引范围获取元素,常用于消息队列、任务列表等有序场景。
- opsForSet():获取操作 Set 类型数据的视图对象,members() 获取所有成员,isMember() 判断元素是否存在,适合存储标签、兴趣爱好等无序唯一集合。
- delete() / hasKey() / expire():通用 Key 操作,与数据结构无关,可作用于任意类型的 Redis key,是日常开发中的高频辅助方法。
- 注:代码中展示的 Redis 客户端命令仅为辅助理解,Java 代码中无需写命令,RedisTemplate 已全部封装为方法调用。
- 拓展了解:如何根据业务场景选择合适的数据结构?
一、String(字符串)—— 最简单、最常用
- 适用场景:单个对象的缓存,如单个用户信息、单条商品信息、验证码(配合过期时间)。
- 特点:key-value 一对一,操作简单,性能最高,Redis 中 80% 以上的缓存场景用它就够了。
- 苍穹外卖实战举例:redisTemplate.opsForValue().set("user:1", user); // 缓存 id 为 1 的用户
二、Hash(哈希)—— 存对象属性,一对多
- 适用场景:一个对象有多个字段需要存取,如用户详情(含 name、age、phone 等多个字段)。
- 特点:key - (field - value) 的两层结构,比 String 节省存储空间,支持单字段操作,适合频繁修改对象中某一个属性的场景。
- 苍穹外卖实战举例:redisTemplate.opsForHash().put("shop:10", "name", "黄焖鸡米饭"); 和 redisTemplate.opsForHash().put("shop:10", "status", "营业中");
三、List(列表)—— 有序可重复,支持两端操作
- 适用场景:需要保持顺序、允许重复的任务队列、消息列表、操作日志等。
- 特点:元素有序排列,支持从左/右侧插入和弹出(lpush/lpop/rpush/rpop),常用于实现简单的消息队列或最新消息展示。
- 苍穹外卖实战举例:存储用户最近浏览的菜品列表,可通过 opsForList().leftPush() 保证最新浏览的菜品在最前面。
四、Set(集合)—— 无序唯一,不允许重复
- 适用场景:存储互不重复的标签、分类、黑白名单、用户点赞集合等。
- 特点:自动去重,适合做交集(sinter)、并集(sunion)等集合运算,例如找出同时喜欢"川菜"和"火锅"的用户。
- 苍穹外卖实战举例:存储某用户已浏览过的商品 ID 集合,自动去重,防止重复统计。
一句话总结:能用 String 解决的就用 String,简单高效;需要操作对象多个字段时才用 Hash;需要顺序和重复时用 List;需要去重和集合运算时用 Set。实战中 90% 的场景 String + Hash 就完全够用了。