个人博客网站搭建day6--Spring Boot自定义RedisTemplate配置:优化序列化与Java8时间类型支持

Spring Boot自定义RedisTemplate配置:优化序列化与Java8时间类型支持

博客文章归档功能的实现与优化: 博客文章归档功能

Spring Boot JWT Token 认证配置的内容:Spring Boot JWT Token 认证配置

MyBatis-Plus核心配置与自动填充机制:MyBatis-Plus核心配置与自动填充机制详解

📚 目录(点击跳转对应章节)

[1. 为什么要自定义RedisTemplate?](#1. 为什么要自定义RedisTemplate?)
[2. 核心代码结构](#2. 核心代码结构)
[3. 核心知识点拆解与代码解析](#3. 核心知识点拆解与代码解析)
[4. 使用效果与验证](#4. 使用效果与验证)
[5. 总结](#5. 总结)


前言

在搭建个人博客网站的开发过程中,缓存优化是提升网站响应速度、减轻数据库压力的关键环节,而 Redis 作为高性能的缓存中间件,自然成为了博客项目中缓存方案的首选。

Spring Boot 为我们提供了RedisTemplate这一操作 Redis 的核心工具类,极大简化了 Redis 的集成与使用流程。

但在实际开发博客项目的过程中发现,Spring Boot 默认的RedisTemplate存在不少适配性问题:

  • 1.比如默认序列化方式会导致 Redis 中存储的数据可读性差、无法直接识别;
  • 2.同时对 Java 8 新增的 LocalDateTime、LocalDate 等时间类型缺乏原生支持,而博客场景中文章发布时间、更新时间等核心字段又频繁用到这类时间类型,直接使用会出现序列化异常;
  • 3.此外,不同业务场景下时间格式不统一也会增加数据解析的复杂度。

因此,针对个人博客的实际开发需求,自定义RedisTemplate就显得尤为必要 ------ 通过优化序列化器、适配 Java 8 时间类型、统一时间格式,能让 Redis 缓存在博客项目中发挥出更稳定、更易用的作用,这也是我在博客开发中总结 Redis 配置与使用的核心要点之一。


一、为什么要自定义RedisTemplate?

Spring Boot默认的RedisTemplate使用JdkSerializationRedisSerializer作为序列化器,存在以下问题:

  1. 序列化后的数据是二进制格式,Redis客户端中无法直接阅读,调试困难;
  2. 序列化体积大,占用更多Redis内存;
  3. 不支持Java8新增的LocalDateTimeLocalDateLocalTime等时间类型,直接序列化会抛出异常;
  4. 序列化规则不统一,易导致HTTP接口和Redis缓存的时间格式不一致。

因此,我们需要自定义RedisTemplate,核心目标是:替换为Jackson JSON序列化器,支持Java8时间类型,统一时间格式


二、核心代码结构

本文涉及两个核心类:

  • RedisConfig:自定义RedisTemplate的核心配置类;
java 复制代码
package com.xuan.common.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import com.xuan.common.utils.DateTimeFormatUtils;
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.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;


/**
 * Redis配置类
 * <p>
 * 作用:
 * 1. 自定义RedisTemplate配置,替代Spring Boot默认配置
 * 2. 优化序列化方式,使用Jackson2JsonRedisSerializer替代默认的JDK序列化
 * 3. 增强序列化功能,支持复杂对象和Java 8时间类型
 * 4. 时间格式统一为 yyyy-MM-dd HH:mm:ss,与 HTTP API 保持一致
 */
@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);

        // 1. 实例化并全面配置 ObjectMapper
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.activateDefaultTyping(objectMapper.getPolymorphicTypeValidator(),
                ObjectMapper.DefaultTyping.NON_FINAL);

        // 注册 Java8 时间模块(自定义格式,与 HTTP 序列化保持一致)
        JavaTimeModule javaTimeModule = new JavaTimeModule();

        // 设置 Java8 时间模块
        javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatUtils.DATETIME_FORMATTER));
        javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatUtils.DATETIME_FORMATTER));
        javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatUtils.DATE_FORMATTER));
        javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatUtils.DATE_FORMATTER));
        javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatUtils.TIME_FORMATTER));
        javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatUtils.TIME_FORMATTER));

        // 注册 Java8 时间模块
        objectMapper.registerModule(javaTimeModule);
        // 禁用默认的时间戳格式
        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

        // 2. 将配置完成的 ObjectMapper 传入序列化器
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(
                objectMapper, Object.class);

        // 3. 设置序列化方式
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

        // 设置 key 和 value 的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        template.setValueSerializer(jackson2JsonRedisSerializer);

        // 设置 hash 的 key 和 value 的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        template.setHashValueSerializer(jackson2JsonRedisSerializer);

        // 初始化
        template.afterPropertiesSet();
        return template;
    }
}
  • DateTimeFormatUtils:时间格式统一工具类,保证HTTP API、Redis缓存等场景的时间格式一致。
java 复制代码
package com.xuan.common.utils;

import java.time.format.DateTimeFormatter;

import static com.xuan.common.constant.DateTimeFormatConstant.DATETIME_PATTERN;
import static com.xuan.common.constant.DateTimeFormatConstant.DATE_PATTERN;
import static com.xuan.common.constant.DateTimeFormatConstant.TIME_PATTERN;

/**
 * 日期时间格式统一配置
 * <p>
 * 作用:
 * 1. 统一管理项目中所有时间格式常量
 * 2. 确保 HTTP API、Redis 缓存等场景的时间格式保持一致
 * 3. 避免在多个配置类中重复定义相同的格式字符串
 *
 * @author 玄〤
 * @since 2026-02-21
 */
public class DateTimeFormatUtils {



    /** 日期时间格式化器 */
    public static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern(DATETIME_PATTERN);
    /** 日期格式化器 */
    public static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern(DATE_PATTERN);
    /** 时间格式化器 */
    public static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern(TIME_PATTERN);

    private DateTimeFormatUtils() {
        // 私有构造函数,防止实例化
    }
}

三、核心知识点拆解与代码解析

3.1 配置ObjectMapper:Jackson序列化的核心

ObjectMapper是Jackson库的核心类,负责Java对象和JSON的相互转换。在Redis序列化中,我们需要对ObjectMapper进行精细化配置,以适配Redis的序列化需求。

3.1.1 基础配置:属性可见性与多态类型支持
java 复制代码
// 1. 实例化并全面配置 ObjectMapper
ObjectMapper objectMapper = new ObjectMapper();
// 设置所有属性(包括private、protected)都能被Jackson序列化/反序列化
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 开启多态类型支持,保证子类实例(如List<User>、抽象类实现类)能正确反序列化
objectMapper.activateDefaultTyping(objectMapper.getPolymorphicTypeValidator(),
        ObjectMapper.DefaultTyping.NON_FINAL);
  • PropertyAccessor.ALL + JsonAutoDetect.Visibility.ANY:默认情况下,Jackson只序列化public属性,此配置让Jackson能访问所有访问修饰符(private、protected、public)的属性,保证对象的所有字段都能被序列化。
  • activateDefaultTyping:开启多态类型支持。例如,若缓存的是List<User>类型,默认序列化会丢失泛型信息,反序列化时无法还原为List<User>,此配置会在JSON中添加类型信息(如@class字段),确保反序列化的准确性;NON_FINAL表示仅对非final类生效,避免不必要的性能开销。
3.1.2 关键配置:支持Java8时间类型并统一格式

Java8的LocalDateTime等时间类型并非JDK原生序列化支持的类型,且默认序列化会输出时间戳,不符合业务的"yyyy-MM-dd HH:mm:ss"格式要求。因此需要手动注册时间模块并自定义序列化规则:

java 复制代码
// 注册 Java8 时间模块(自定义格式,与 HTTP 序列化保持一致)
JavaTimeModule javaTimeModule = new JavaTimeModule();

// 为LocalDateTime配置自定义序列化/反序列化器,指定统一格式
javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatUtils.DATETIME_FORMATTER));
javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatUtils.DATETIME_FORMATTER));
// 同理配置LocalDate、LocalTime
javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatUtils.DATE_FORMATTER));
javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatUtils.DATE_FORMATTER));
javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatUtils.TIME_FORMATTER));
javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatUtils.TIME_FORMATTER));

// 注册Java8时间模块到ObjectMapper
objectMapper.registerModule(javaTimeModule);
// 禁用默认的时间戳格式(默认会把LocalDateTime序列化为13位时间戳)
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
  • JavaTimeModule:Jackson专门为Java8时间类型提供的模块,必须手动注册,否则无法序列化LocalDateTime等类型;
  • LocalDateTimeSerializer/LocalDateTimeDeserializer:自定义序列化/反序列化器,接收DateTimeFormatter参数,指定时间格式;
  • disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS):关闭默认的时间戳序列化方式,改为字符串格式(如"2026-02-25 15:30:00")。

3.2 时间格式统一管理:DateTimeFormatUtils

为了避免在Redis配置、HTTP接口配置等多处重复定义时间格式字符串,我们抽离出工具类DateTimeFormatUtils

java 复制代码
public class DateTimeFormatUtils {
    /** 日期时间格式化器 */
    public static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern(DATETIME_PATTERN);
    /** 日期格式化器 */
    public static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern(DATE_PATTERN);
    /** 时间格式化器 */
    public static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern(TIME_PATTERN);

    private DateTimeFormatUtils() {
        // 私有构造函数,防止实例化
    }
}
  • 核心作用:统一管理项目中所有时间格式常量(如DATETIME_PATTERN通常定义为"yyyy-MM-dd HH:mm:ss"),保证Redis缓存、HTTP接口返回值、数据库查询等场景的时间格式一致;
  • 私有构造函数:工具类不需要实例化,通过私有构造函数防止外部new DateTimeFormatUtils(),是工具类的标准写法;
  • 依赖常量类:DATETIME_PATTERN等常量通常定义在DateTimeFormatConstant中,进一步解耦配置。

3.3 配置RedisTemplate序列化器

完成ObjectMapper配置后,需要将其注入Jackson2JsonRedisSerializer,并设置到RedisTemplate中:

java 复制代码
// 2. 将配置完成的 ObjectMapper 传入序列化器
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(
        objectMapper, Object.class);

// 3. 设置序列化方式
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

// 设置 key 和 value 的序列化方式
template.setKeySerializer(stringRedisSerializer);
template.setValueSerializer(jackson2JsonRedisSerializer);

// 设置 hash 的 key 和 value 的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
template.setHashValueSerializer(jackson2JsonRedisSerializer);

// 初始化
template.afterPropertiesSet();
  • StringRedisSerializer:用于Redis的Key和HashKey序列化。因为Redis的Key本质是字符串,使用该序列化器能保证Key的可读性(如"user:1001"),且兼容所有Redis客户端;
  • Jackson2JsonRedisSerializer:用于Redis的Value和HashValue序列化。将Java对象序列化为JSON字符串,可读性强、体积小,且支持复杂对象和Java8时间类型;
  • afterPropertiesSet()RedisTemplate的初始化方法,必须调用,否则序列化配置不生效(该方法会验证并应用所有序列化器配置)。

3.4 注解与Bean声明

java 复制代码
@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        // ... 配置逻辑
    }
}
  • @Configuration:标记该类为Spring配置类,Spring启动时会扫描并加载该类中的Bean;
  • @Bean:将RedisTemplate对象注入Spring容器,替代默认的RedisTemplate实例;
  • RedisConnectionFactory:Spring自动注入的Redis连接工厂(由spring-boot-starter-data-redis自动配置),无需手动创建,保证Redis连接的通用性。

四、使用效果与验证

配置完成后,我们可以通过简单的测试验证效果:

java 复制代码
@Autowired
private RedisTemplate<String, Object> redisTemplate;

@Test
public void testRedisSerialization() {
    // 测试对象:包含LocalDateTime字段
    User user = new User();
    user.setId(1L);
    user.setName("张三");
    user.setCreateTime(LocalDateTime.now());
    
    // 存入Redis
    redisTemplate.opsForValue().set("user:1", user);
    
    // 从Redis取出
    User cachedUser = (User) redisTemplate.opsForValue().get("user:1");
    System.out.println(cachedUser.getCreateTime()); // 输出:2026-02-25 15:30:00(格式化后的字符串)
}

在Redis客户端中查看user:1的值,会发现是格式化后的JSON字符串,而非二进制数据,且createTime字段为指定格式的字符串,而非时间戳。

json 复制代码
{
  "@class": "com.example.entity.User",
  "id": 1,
  "name": "张三",
  "createTime": "2026-02-25 15:30:00"
}

五、总结

本文详细讲解了Spring Boot自定义RedisTemplate的核心知识点,关键总结如下:

  1. 序列化优化:替换默认的JDK序列化器为Jackson JSON序列化器,解决Redis数据不可读、体积大的问题;
  2. Java8时间支持 :通过注册JavaTimeModule并自定义序列化器,实现LocalDateTime等类型的序列化,并统一时间格式;
  3. 配置规范 :抽离时间格式常量到工具类,保证项目内时间格式统一;通过@Configuration@Bean注入自定义RedisTemplate,替代默认实现;
  4. 细节注意 :开启Jackson多态类型支持保证复杂对象反序列化准确,调用afterPropertiesSet()确保RedisTemplate配置生效。

该配置方案适用于绝大多数Spring Boot + Redis项目,既保证了Redis数据的可读性和兼容性,又解决了Java8时间类型序列化的痛点,是企业级项目中Redis配置的最佳实践之一。

相关推荐
西门吹-禅1 小时前
【eclipse 升级】
java·ide·eclipse
A懿轩A1 小时前
【SpringBoot 快速开发】面向后端开发的 HTTP 协议详解:请求报文、响应码与常见设计规范
spring boot·http·设计规范
Seven971 小时前
剑指offer-78、求平⽅根
java
Flobby5291 小时前
深入理解 MySQL 索引:从 B+ 树到索引下推
数据库·后端·mysql
知我Deja_Vu1 小时前
@Transactional 与 @Transactional(rollbackFor = Exception.class) 的区别详解
java·spring
敲敲千反田2 小时前
CAS和AQS相关问题
java
上海合宙LuatOS2 小时前
LuatOS核心库API——【iotauth 】 IOT 鉴权库
java·单片机·嵌入式硬件·物联网·struts·计算机外设·硬件工程
女王大人万岁2 小时前
Golang实战Eclipse Paho MQTT库:MQTT通信全解析
服务器·开发语言·后端·golang
luod2 小时前
Docker 快速安装Jenkins
java·docker·jenkins