SpringBoot连接Redis
- SpringBoot连接Redis的方式
- RedisTemplate 与 StringRedisTemplate
- 自定义 RedisTemplate
SpringBoot连接Redis的方式
引入依赖,在 pom.xml 中添加:
<dependencies><!-- SpringBoot 整合 Redis 核心依赖 --><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><!-- SpringBoot 测试依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId></dependency></dependencies>spring-boot-starter-data-redis 是 Spring Boot 官方提供的 Redis 整合启动器,可自动完成 Redis 相关的核心配置,开箱即用。
自动引入的关键组件:引入 Lettuce 客户端,自动配置开发中操作 Redis 的核心模板类 RedisTemplate 和 StringRedisTemplate,自动创建 RedisConnectionFactory(Redis 连接的核心工厂类),默认提供 JDK 序列化器。
需要注意的是 Lettuce 的连接实例是线程安全的,多个线程可以共享同一个连接,无需为每个线程创建新连接,早期的 Jedis 客户端连接是线程不安全的,必须通过连接池为每个线程分配独立连接,否则会出现并发问题。commons-pool2 连接池依赖,为 Redis 客户端提供连接池支持,是实现 Redis 连接复用的核心依赖。
Redis 连接的创建与销毁开销很大,如果每次操作都新建连接,会严重影响性能,连接池可以提前创建一批连接,复用这些连接,大幅提升 Redis 操作的吞吐量。spring-boot-starter-test 是 Spring Boot 官方提供的通用测试依赖,提供 Spring Boot 单元测试与集成测试所需的全套核心组件,可用于测试 Redis、数据库、接口等所有 Spring Boot 组件的逻辑正确性。
自动引入的核心测试组件:Java 测试框架 JUnit、Spring 整合测试核心 Spring Test(Spring TestContext)、AssertJ 断言工具(简化测试结果校验)、Mockito 模拟工具(模拟外部依赖,降低测试环境依赖)。
在 application.yml 中配置连接信息:
spring:redis:# 基础连接信息host:101.201.155.5# Redis服务端IPport:6379# Redis端口password:""# Redis密码,无密码则留空database:0# 连接的Redis数据库编号,默认0号库timeout:10000# 连接超时时间,单位毫秒(默认10秒)# ==================== 以下根据客户端类型二选一配置 ====================# 【选项1】使用Lettuce客户端(SpringBoot默认,推荐)lettuce:pool:max-active:8# 连接池最大连接数(默认8)max-idle:8# 连接池最大空闲连接数(默认8)min-idle:0# 连接池最小空闲连接数(默认0)max-wait:-1ms# 连接池最大等待时间,-1表示无限制(默认)# 【选项2】使用Jedis客户端(需先排除Lettuce依赖,再引入Jedis依赖)# jedis:# pool:# max-active: 8# max-idle: 8# min-idle: 0# max-wait: -1ms连接测试
importorg.junit.Test;importorg.junit.runner.RunWith;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.boot.test.context.SpringBootTest;importorg.springframework.data.redis.core.RedisTemplate;importorg.springframework.test.context.junit4.SpringRunner;importstaticorg.junit.Assert.assertEquals;@RunWith(SpringRunner.class)@SpringBootTestpublicclassRedisTest{@AutowiredprivateRedisTemplateredisTemplate;@TestpublicvoidtestSetAndGet(){Stringkey="test:key";Stringvalue="Hello Redis";// 写入redisTemplate.opsForValue().set(key,value);// 2. 读取Objectresult=redisTemplate.opsForValue().get(key);System.out.println("读取结果: "+result);// 3. 断言assertEquals(value,result);}}RedisTemplate 与 StringRedisTemplate
RedisTemplate
- 序列化规则
默认采用 JdkSerializationRedisSerializer(JDK 原生对象序列化器),不仅将对象的数据内容转换为字节数组,还会附加 Java 对象的元信息(如类全限定名、序列化版本号、类型标识等),其中 \xac\xed\x00\x05 是 JDK 序列化字节流的固定前缀,用于标识 JDK 序列化格式;
需要注意的是被序列化的 Java 对象必须实现 java.io.Serializable 接口,否则会抛出 NotSerializableException 异常;
序列化范围:默认对 Key、Value、HashKey、HashValue 均采用该序列化器。 - 乱码现象
在 redis-cli、Redis Desktop Manager 等客户端查看数据时出现的乱码,是解析规则不匹配造成的。
Redis 客户端默认按 UTF-8 字符串规则解析字节流,但 JDK 序列化的字节流包含非 UTF-8 编码的前缀、元信息字节,这些字节无法被 UTF-8 正常解析,从而显示为乱码符号。Key 和 Value 都会出现该现象,并且与数据是否为中文无关。 - 适用场景
需要直接存储与读取完整 Java 实体对象,且无需在 Redis 客户端直接查看数据的场景;
不推荐直接使用默认的 JDK 序列化器(可读性差、序列化体积大、版本兼容风险高),生产环境建议替换为 JSON 序列化器,既支持对象序列化,又能在客户端以 JSON 格式可读。
StringRedisTemplate
- 序列化规则
作为 RedisTemplate<String, String> 的子类,默认全维度采用 StringRedisSerializer(UTF-8 字符串序列化器),仅处理字符串类型数据,将 Java 字符串直接按 UTF-8 字符集转换为纯字节数组,无任何额外前缀和元信息;反序列化时,将字节数组按 UTF-8 直接转回字符串;
序列化范围:Key、Value、HashKey、HashValue 均采用该序列化器(全维度字符串序列化)。 - 无乱码特性
序列化与反序列化规则与 Redis 原生客户端(redis-cli)、第三方可视化工具(如 Redis Desktop Manager)的默认解析规则(UTF-8)完全一致;与 Redis 原生命令(如 incr、keys、expire)完全兼容,可直接通过命令行操作 StringRedisTemplate 存储的 Key/Value。 - 适用场景
将 Java 对象先序列化为 JSON 字符串,再存储为字符串;
存储令牌(token)、验证码、用户会话信息、配置项等纯字符串数据;
分布式锁、计数器(incr/decr)、过期缓存控制等依赖 Redis 原生命令的场景;
StringRedisTemplate 可读性强、兼容原生命令、无序列化额外开销,是生产环境的首选模板类。
但若直接给 StringRedisTemplate 传 User 对象,编译阶段就会报错,因为 StringRedisTemplate 的 opsForValue().set() 方法要求第二个参数是 String 类型,编译不通过。
要 StringRedisTemplate 能处理 User 对象,必须要先把 Object 转成 String 格式,读取时再把 String 转回 Object,如下所示:
importcom.fasterxml.jackson.core.JsonProcessingException;importcom.fasterxml.jackson.databind.ObjectMapper;importorg.junit.Test;importorg.junit.runner.RunWith;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.boot.test.context.SpringBootTest;importorg.springframework.data.redis.core.StringRedisTemplate;importorg.springframework.test.context.junit4.SpringRunner;@RunWith(SpringRunner.class)@SpringBootTestpublicclassRedisTest{@AutowiredprivateStringRedisTemplatestringRedisTemplate;privatefinalObjectMapperobjectMapper=newObjectMapper();@TestpublicvoidtestSaveUserRight()throwsJsonProcessingException{// 创建 User 对象(Object 类型)Useruser=newUser(1L,"张三",25);// 手动把 User 对象转为 JSON 字符串StringuserJson=objectMapper.writeValueAsString(user);// 把 JSON 字符串传给 StringRedisTemplatestringRedisTemplate.opsForValue().set("user:1",userJson);// 读取时先取 JSON 字符串再手动转回 User 对象StringsavedUserJson=stringRedisTemplate.opsForValue().get("user:1");UsersavedUser=objectMapper.readValue(savedUserJson,User.class);System.out.println(savedUser.getName());}}publicclassUser{privateLongid;privateStringname;privateIntegerage;publicUser(){}publicUser(Longid,Stringname,Integerage){this.id=id;this.name=name;this.age=age;}publicLonggetId(){returnid;}publicvoidsetId(Longid){this.id=id;}publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}publicIntegergetAge(){returnage;}publicvoidsetAge(Integerage){this.age=age;}}Jackson 反序列化 JSON 为 Java 对象的核心逻辑是:先创建一个空的 User 对象(需要依赖无参构造器),再通过 setter 方法把 JSON 里的字段值赋值给空对象的属性。
使用 RedisTemplate 直接存储与取出 Object 类型数据
importorg.junit.Test;importorg.junit.runner.RunWith;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.boot.test.context.SpringBootTest;importorg.springframework.data.redis.core.RedisTemplate;importorg.springframework.test.context.junit4.SpringRunner;@RunWith(SpringRunner.class)@SpringBootTestpublicclassRedisTest{@AutowiredprivateRedisTemplateredisTemplate;@TestpublicvoidtestSaveUserWrong(){// 创建待存储的 User 对象Useruser=newUser(1L,"张三",25);// 存入 RedisStringredisKey="user:1";redisTemplate.opsForValue().set(redisKey,user);System.out.println("User 对象已存入 Redis,Key = "+redisKey);// 从 Redis 取出对象(需强制类型转换为 User)UsersavedUser=(User)redisTemplate.opsForValue().get(redisKey);System.out.println("从 Redis 取出 User 对象:"+savedUser);}}自定义 RedisTemplate
使用 RedisTemplate 直接存储与取出 Object 类型数据,但 RedisTemplate 会在 redis-cli、Redis Desktop Manager 等客户端查看数据时会出现乱码问题,下面进行编写自定义 RedisTemplate 可以避免这个问题,如下所示:
importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.data.redis.connection.RedisConnectionFactory;importorg.springframework.data.redis.core.RedisTemplate;importorg.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;importorg.springframework.data.redis.serializer.StringRedisSerializer;@ConfigurationpublicclassRedisConfig{@BeanpublicRedisTemplate<String,Object>redisTemplate(RedisConnectionFactoryredisConnectionFactory){// 1. 创建 RedisTemplate 实例RedisTemplate<String,Object>redisTemplate=newRedisTemplate<>();// 2. 绑定 Redis 连接工厂redisTemplate.setConnectionFactory(redisConnectionFactory);// 3. 配置序列化器(解决乱码,同时支持 Java 对象序列化)StringRedisSerializerstringRedisSerializer=newStringRedisSerializer();// 键序列化器GenericJackson2JsonRedisSerializerjacksonRedisSerializer=newGenericJackson2JsonRedisSerializer();// 值序列化器// 4. 设置全局序列化规则redisTemplate.setKeySerializer(stringRedisSerializer);redisTemplate.setValueSerializer(jacksonRedisSerializer);redisTemplate.setHashKeySerializer(stringRedisSerializer);redisTemplate.setHashValueSerializer(jacksonRedisSerializer);// 5. 初始化 RedisTemplateredisTemplate.afterPropertiesSet();returnredisTemplate;}}自定义 RedisTemplate 是字符串序列化(Key/HashKey)与 JSON 序列化(Value/HashValue)的组合:
Key/HashKey 采用 StringRedisSerializer 按 UTF-8 规则明文序列化,Redis 中 Key/HashKey 均为纯字符串,无任何乱码;Value/HashValue 采用 GenericJackson2JsonRedisSerializer 序列化为标准 JSON 字符串,既解决了原生 RedisTemplate 因 JDK 二进制序列化导致的乱码问题,又保留了直接存储与读取 Java Object 的能力(无需手动转换 JSON 字符串)。