SpringBoot从零构建符合工程规范的Redis配置类(全解析)

作为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的"约定大于配置"核心体现:

  1. spring-boot-starter-data-redis内置RedisAutoConfiguration自动配置类,启动时会扫描;
  2. 内置的RedisProperties类通过@ConfigurationProperties(prefix = "spring.redis")注解,自动绑定yml中以spring.redis为前缀的配置项;
  3. 自动创建RedisConnectionFactory(连接工厂),无需手动读取yml,直接注入即可使用;
  4. 若未配置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;
    }
}
关键步骤解释:
  1. @Configuration:标记类为Spring配置类,替代传统XML配置;
  2. RedisConnectionFactory:SpringBoot自动创建的连接工厂,无需手动实例化,直接注入即可关联yml中的Redis配置;
  3. StringRedisSerializer:Key用字符串序列化,避免默认JDK序列化产生的乱码(如\xAC\xED\x00\x05t\x00\x03key);
  4. GenericJackson2JsonRedisSerializer:Value用JSON序列化,兼顾可读性和跨语言兼容性,支持对象/集合序列化;
  5. 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);
    }
}

八、核心要点总结

  1. yml自动读取 :SpringBoot通过RedisProperties自动绑定spring.redis前缀配置,无需手动解析;
  2. RedisTemplate优化:核心是替换序列化器(String+JSON),解决乱码问题;
  3. ObjectMapper自定义:优化JSON序列化规则,适配Java 8时间类型、私有字段、null值等;
  4. RedisCacheManager:统一缓存注解规则,设置过期时间、禁止缓存null值,避免缓存穿透/脏数据;
  5. 工程规范 :所有配置复用同一ObjectMapper,保证序列化规则一致,便于维护。

这套配置覆盖了Redis在SpringBoot中的核心使用场景,解决了默认配置的所有痛点,可直接复制到企业项目中使用。

相关推荐
+VX:Fegn089515 小时前
计算机毕业设计|基于springboot + vue鲜花商城系统(源码+数据库+文档)
数据库·vue.js·spring boot·后端·课程设计
识君啊16 小时前
SpringBoot 事务管理解析 - @Transactional 的正确用法与常见坑
java·数据库·spring boot·后端
CaracalTiger16 小时前
如何解决Unexpected token ‘<’, “<!doctype “… is not valid JSON 报错问题
java·开发语言·jvm·spring boot·python·spring cloud·json
学到头秃的suhian17 小时前
Redis缓存
数据库·redis·缓存
苏渡苇17 小时前
Java + Redis + MySQL:工业时序数据缓存与持久化实战(适配高频采集场景)
java·spring boot·redis·后端·spring·缓存·架构
Hx_Ma1617 小时前
Springboot整合mybatis注解版
java·spring boot·mybatis
t***442318 小时前
Spring boot整合quartz方法
java·前端·spring boot
蛐蛐蜉蝣耶18 小时前
互联网大厂Java面试实录:当严肃面试官遇上搞笑程序员谢飞机
spring boot·微服务·java面试·电商系统·分布式系统·技术面试·程序员面试
enjoy嚣士19 小时前
springboot 之 时区问题
java·spring boot·后端·时区
沙河板混20 小时前
@RequestMapping的参数
java·spring boot·spring