作为Java开发新手,使用SpringBoot集成Redis时,往往会直接用默认配置,结果遇到序列化乱码、缓存穿透、类型丢失等问题。本文从0开始,一步步教你编写符合企业工程规范的Redis配置类,涵盖RedisTemplate自定义、ObjectMapper优化、RedisCacheManager配置等核心内容,每一步都标注作用和解决的问题,方便复习和团队学习。
一、前言:为什么要自定义Redis配置?
SpringBoot提供了Redis的自动配置,但默认配置存在以下问题:
- 默认序列化方式为JDK序列化,Redis中数据乱码、可读性差;
- 无默认缓存过期时间,易导致缓存永久有效、数据脏读;
- 未适配Java 8时间类型(LocalDateTime),序列化时报错;
- 缓存null值易引发缓存穿透,占用Redis内存;
- 无法灵活控制缓存注解(
@Cacheable)的行为。
本文的目标是构建一套"开箱即用"的Redis配置,解决上述所有问题,符合企业工程规范。
二、前置准备:依赖引入与基础配置
2.1 引入核心依赖(pom.xml)
首先在pom.xml中引入Redis起步依赖和连接池依赖(连接池是工程优化的必备项,避免频繁创建/销毁连接):
xml
<!-- SpringBoot Redis起步依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- Redis连接池依赖(提升性能) -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
2.2 配置Redis服务器信息(application.yml)
在resources/application.yml中配置Redis基础信息,SpringBoot会自动读取这些配置,无需手动解析:
yaml
spring:
redis:
# Redis服务器地址(本地默认localhost)
host: localhost
# Redis端口(默认6379)
port: 6379
# 密码(无密码则注释)
password:
# 数据库索引(Redis默认16个库,从0开始)
database: 0
# 连接池配置(基于commons-pool2)
lettuce:
pool:
max-active: 8 # 最大活跃连接数
max-idle: 8 # 最大空闲连接数
min-idle: 0 # 最小空闲连接数
max-wait: -1ms # 连接等待超时时间(-1表示无限制)
# 连接超时时间
timeout: 10000ms
关键说明:SpringBoot如何自动读取yml配置?
SpringBoot的"约定大于配置"核心体现:
spring-boot-starter-data-redis内置RedisAutoConfiguration自动配置类,启动时会扫描;- 内置的
RedisProperties类通过@ConfigurationProperties(prefix = "spring.redis")注解,自动绑定yml中以spring.redis为前缀的配置项; - 自动创建
RedisConnectionFactory(连接工厂),无需手动读取yml,直接注入即可使用; - 若未配置yml,会使用
RedisProperties的默认值(host=localhost、port=6379),本地Redis可直接连接。
三、核心1:自定义RedisTemplate(解决默认序列化问题)
RedisTemplate是Spring操作Redis的核心工具类,默认配置存在序列化乱码问题,需自定义优化:
java
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.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* Redis核心配置类
* @Configuration:标记为配置类,SpringBoot启动时加载
*/
@Configuration
public class RedisConfig {
/**
* 自定义RedisTemplate
* @param redisConnectionFactory 自动注入的连接工厂(基于yml配置)
* @return 优化后的RedisTemplate
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
// 1. 创建RedisTemplate实例
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
// 2. 关联Redis连接工厂(绑定yml配置的Redis服务器信息)
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 3. 配置序列化器(核心:解决乱码问题)
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); // Key序列化(字符串,可读性高)
GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer(); // Value序列化(JSON格式)
// 4. 设置序列化规则
redisTemplate.setKeySerializer(stringRedisSerializer); // Key序列化
redisTemplate.setHashKeySerializer(stringRedisSerializer); // Hash类型Key序列化
redisTemplate.setValueSerializer(jsonRedisSerializer); // Value序列化
redisTemplate.setHashValueSerializer(jsonRedisSerializer); // Hash类型Value序列化
// 5. 初始化配置
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
关键步骤解释:
@Configuration:标记类为Spring配置类,替代传统XML配置;RedisConnectionFactory:SpringBoot自动创建的连接工厂,无需手动实例化,直接注入即可关联yml中的Redis配置;StringRedisSerializer:Key用字符串序列化,避免默认JDK序列化产生的乱码(如\xAC\xED\x00\x05t\x00\x03key);GenericJackson2JsonRedisSerializer:Value用JSON序列化,兼顾可读性和跨语言兼容性,支持对象/集合序列化;afterPropertiesSet():初始化RedisTemplate的配置,确保序列化规则生效。
四、核心2:自定义ObjectMapper(优化JSON序列化规则)
上述GenericJackson2JsonRedisSerializer底层依赖ObjectMapper(Jackson核心类),默认的ObjectMapper存在日期乱码、私有字段无法序列化等问题,需自定义优化:
4.1 自定义ObjectMapper的完整配置
在RedisConfig类中新增ObjectMapper Bean:
java
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
// 新增ObjectMapper Bean
@Bean
public ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
// 1. 允许访问所有字段(包括私有),无需依赖getter/setter
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 2. 自动添加类型标识,解决反序列化类型丢失问题
objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,
ObjectMapper.DefaultTyping.NON_FINAL);
// 3. 支持Java 8时间类型(LocalDateTime/LocalDate)
JavaTimeModule timeModule = new JavaTimeModule();
// 自定义LocalDateTime序列化格式(解决默认时间戳/乱码问题)
String dateFormat = "yyyy-MM-dd HH:mm:ss";
timeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(dateFormat)));
timeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(dateFormat)));
objectMapper.registerModule(timeModule);
// 4. 关闭默认时间戳序列化(Date类型不再转数字)
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
// 5. 忽略null字段(节省Redis内存)
objectMapper.setSerializationInclusion(com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL);
// 6. 忽略未知字段(JSON有多余字段时不报错)
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
return objectMapper;
}
4.2 改造RedisTemplate:使用自定义ObjectMapper
将自定义的ObjectMapper注入GenericJackson2JsonRedisSerializer:
java
// 改造后的redisTemplate方法
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory,
ObjectMapper objectMapper) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// 使用自定义ObjectMapper
GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer(objectMapper);
redisTemplate.setKeySerializer(stringRedisSerializer);
redisTemplate.setHashKeySerializer(stringRedisSerializer);
redisTemplate.setValueSerializer(jsonRedisSerializer);
redisTemplate.setHashValueSerializer(jsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
4.3 ObjectMapper核心作用总结
| 配置项 | 解决的问题 |
|---|---|
setVisibility(ALL, ANY) |
序列化私有字段,无需依赖getter/setter |
activateDefaultTyping |
序列化时添加类型标识(@class),解决反序列化类型丢失(如Object转User) |
registerModule(JavaTimeModule) |
支持Java 8时间类型(LocalDateTime),避免序列化报错 |
disable(WRITE_DATES_AS_TIMESTAMPS) |
日期转字符串(如2025-01-01 12:00:00),而非时间戳 |
setSerializationInclusion(NON_NULL) |
忽略null字段,节省Redis内存 |
disable(FAIL_ON_UNKNOWN_PROPERTIES) |
JSON有多余字段时不报错,提升兼容性 |
五、核心3:自定义RedisCacheManager(适配缓存注解)
若使用@Cacheable/@CacheEvict等缓存注解,需自定义RedisCacheManager统一缓存规则(过期时间、序列化、空值处理):
5.1 完整配置(新增到RedisConfig类)
java
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import java.time.Duration;
// 自定义RedisCacheManager
@Bean
public RedisCacheManager redisCacheManager(RedisConnectionFactory connectionFactory,
ObjectMapper objectMapper) {
// 1. 构建JSON序列化器(复用自定义ObjectMapper)
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
// 2. 定义缓存全局规则
RedisCacheConfiguration cacheConfig = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofHours(1)) // 默认过期时间:1小时(避免缓存永久有效)
// Key序列化(字符串)
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
// Value序列化(JSON)
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
.disableCachingNullValues(); // 禁止缓存null值(防止缓存穿透)
// 3. 构建缓存管理器
return RedisCacheManager.builder(connectionFactory)
.cacheDefaults(cacheConfig) // 应用全局规则
.build();
}
5.2 关键配置解释
| 配置项 | 作用 |
|---|---|
entryTtl(Duration.ofHours(1)) |
设置缓存默认过期时间,避免缓存永久有效导致脏数据 |
serializeKeysWith/serializeValuesWith |
统一缓存注解的Key/Value序列化规则,与RedisTemplate保持一致 |
disableCachingNullValues() |
禁止缓存null值,防止恶意查询不存在的Key导致缓存穿透 |
RedisCacheManager.builder() |
将缓存规则与Redis连接工厂绑定,让注解遵循规则操作Redis |
5.2 使用示例(缓存注解)
java
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
@Service
@EnableCaching // 启动类/配置类添加:开启缓存注解
public class UserService {
// 使用缓存注解,遵循RedisCacheManager的全局规则
@Cacheable(value = "userCache", key = "#id")
public User getUserById(Long id) {
// 模拟数据库查询
System.out.println("查询数据库,ID:" + id);
return new User(id, "张三", LocalDateTime.now());
}
}
效果说明:
- 第一次调用
getUserById(1):查询数据库,结果缓存到Redis(Key:userCache::1,JSON格式,1小时过期); - 1小时内重复调用:直接从Redis读取,不查数据库;
- 返回null时:不缓存,避免缓存穿透;
- Redis中数据可读,无乱码。
六、完整RedisConfig整合版
java
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.Duration;
/**
* 符合工程规范的Redis完整配置类
*/
@Configuration
@EnableCaching // 开启缓存注解
public class RedisConfig {
/**
* 1. 自定义ObjectMapper(JSON序列化核心)
*/
@Bean
public ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
// 允许访问所有字段
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 自动添加类型标识
objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
// 支持Java 8时间类型
JavaTimeModule timeModule = new JavaTimeModule();
String dateFormat = "yyyy-MM-dd HH:mm:ss";
timeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(dateFormat)));
timeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(dateFormat)));
objectMapper.registerModule(timeModule);
// 关闭时间戳序列化
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
// 忽略null字段
objectMapper.setSerializationInclusion(com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL);
// 忽略未知字段
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
return objectMapper;
}
/**
* 2. 自定义RedisTemplate
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory,
ObjectMapper objectMapper) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer(objectMapper);
redisTemplate.setKeySerializer(stringRedisSerializer);
redisTemplate.setHashKeySerializer(stringRedisSerializer);
redisTemplate.setValueSerializer(jsonRedisSerializer);
redisTemplate.setHashValueSerializer(jsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
/**
* 3. 自定义RedisCacheManager(适配缓存注解)
*/
@Bean
public RedisCacheManager redisCacheManager(RedisConnectionFactory connectionFactory,
ObjectMapper objectMapper) {
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
RedisCacheConfiguration cacheConfig = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofHours(1))
.serializeKeysWith(RedisConnectionFactory.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisConnectionFactory.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
.disableCachingNullValues();
return RedisCacheManager.builder(connectionFactory)
.cacheDefaults(cacheConfig)
.build();
}
}
七、测试验证
7.1 测试RedisTemplate
java
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
@SpringBootTest
public class RedisTemplateTest {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Test
public void testRedisTemplate() {
// 存值(对象类型)
User user = new User(1L, "张三", LocalDateTime.now());
redisTemplate.opsForValue().set("user:1", user);
// 取值
User getUser = (User) redisTemplate.opsForValue().get("user:1");
System.out.println(getUser); // 输出:User(id=1, name=张三, createTime=2025-01-01 12:00:00)
// 删除
redisTemplate.delete("user:1");
}
}
7.2 测试缓存注解
java
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class RedisCacheTest {
@Autowired
private UserService userService;
@Test
public void testCacheable() {
// 第一次调用:查数据库
userService.getUserById(1L);
// 第二次调用:查缓存
userService.getUserById(1L);
}
}
八、核心要点总结
- yml自动读取 :SpringBoot通过
RedisProperties自动绑定spring.redis前缀配置,无需手动解析; - RedisTemplate优化:核心是替换序列化器(String+JSON),解决乱码问题;
- ObjectMapper自定义:优化JSON序列化规则,适配Java 8时间类型、私有字段、null值等;
- RedisCacheManager:统一缓存注解规则,设置过期时间、禁止缓存null值,避免缓存穿透/脏数据;
- 工程规范 :所有配置复用同一
ObjectMapper,保证序列化规则一致,便于维护。
这套配置覆盖了Redis在SpringBoot中的核心使用场景,解决了默认配置的所有痛点,可直接复制到企业项目中使用。