超详细大白话讲解:RedisTemplate 和 Jackson 配置细节

大家好,今天我们来聊聊 Spring 项目中 Redis 和 Jackson 的配置细节,尤其是 RedisConfigCacheWrapper 这两个类的代码。这俩家伙在缓存系统中特别常见,理解它们能让你在项目中用 Redis 存数据时少踩坑。咱们会尽量用大白话,一步步拆开代码,讲得细到不能再细,还要接地气!


一、RedisConfig 类:打造 RedisTemplate 的"万能钥匙"

RedisConfig 是一个配置类,用来创建一个 RedisTemplate 对象。简单来说,RedisTemplate 是 Spring Data Redis 提供的一个工具,专门用来跟 Redis 数据库打交道,比如存数据、取数据啥的。咱们直接看代码:

java 复制代码
@Configuration
public class RedisConfig {

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

        // 使用String序列化器处理Key
        template.setKeySerializer(new StringRedisSerializer());
        
        // 使用JSON序列化器处理Value
        Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.registerModule(new JavaTimeModule());
        om.activateDefaultTyping(om.getPolymorphicTypeValidator(), 
            ObjectMapper.DefaultTyping.NON_FINAL);
        serializer.setObjectMapper(om);

        template.setValueSerializer(serializer);
        template.setHashValueSerializer(serializer);
        return template;
    }
}

1. @Configuration@Bean:Spring 的"魔法标签"

  • @Configuration :这个注解告诉 Spring:"嘿,这个类是用来配置东西的,里面有重要的初始化代码,你得认真对待!"。简单说,它标记了 RedisConfig 是个配置类。
  • @Bean :这个注解用在方法上,表示这个方法会返回一个对象(这里是 RedisTemplate),Spring 会把它塞进自己的容器里管理。以后项目里哪需要用 RedisTemplate,都可以直接从 Spring 拿。

2. RedisConnectionFactory:Redis 的"门票"

  • 方法参数里有个 RedisConnectionFactory factory,这是 Spring 注入进来的一个工厂类。你可以把它想象成一张"门票",有了它,RedisTemplate 才能连上 Redis 数据库。
  • 配置好连接后,咱们通过 template.setConnectionFactory(factory) 把这张门票交给 RedisTemplate,让它知道去哪儿找 Redis。

3. 创建 RedisTemplate<String, Object>:定义"主角"

  • RedisTemplate<String, Object> template = new RedisTemplate<>():这里我们创建了一个 RedisTemplate 对象,泛型是 <String, Object>
    • String 是 Key(键)的类型,意思是 Redis 里的键都用字符串表示,比如 "user:123"
    • Object 是 Value(值)的类型,意思是值可以是任意对象,比如一个 User 对象、List 啥的,超级灵活。

4. Key 的序列化:StringRedisSerializer

  • template.setKeySerializer(new StringRedisSerializer()):这行代码指定了 Key 的序列化器是 StringRedisSerializer
  • 啥是序列化? 简单说,Redis 只认识字节流(byte[]),而我们代码里用的是 Java 对象(比如 String),序列化就是把 Java 对象转成字节流的过程,反过来叫反序列化。
  • 为啥用 StringRedisSerializer 因为 Key 是字符串,用这个序列化器最直接,存进去是啥样,取出来还是啥样。比如存 "user:123",Redis 里就是 "user:123",不会有乱七八糟的编码问题。

5. Value 的序列化:Jackson2JsonRedisSerializer

  • Value 的处理就复杂点了,因为值是 Object 类型,可能是个对象、集合啥的,不能简单存成字符串。我们用的是 Jackson2JsonRedisSerializer,它会把对象转成 JSON 格式存进 Redis。

  • 代码拆解:

    java 复制代码
    Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
    • Jackson2JsonRedisSerializer 是基于 Jackson 库的序列化器,<Object> 表示它能处理任意类型的值。
    • 比如你存个 User 对象 {name: "张三", age: 18},它会转成 JSON 字符串 {"name": "张三", "age": 18} 存进 Redis。

配置 Jackson 的 ObjectMapper

  • ObjectMapper 是 Jackson 的核心工具,用来控制 JSON 的序列化和反序列化行为。咱们在这儿给它加了点料:

    java 复制代码
    ObjectMapper om = new ObjectMapper();
    om.registerModule(new JavaTimeModule());
    om.activateDefaultTyping(om.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL);
    serializer.setObjectMapper(om);
  • JavaTimeModule

    • 这个模块是为了支持 Java 8 的时间和日期类型,比如 LocalDateTimeInstant 这些。
    • 如果不加这个模块,存个 LocalDateTime 对象可能会报错,或者格式乱掉。加了它,时间类型就能正常转成 JSON,比如 "2023-10-25T10:00:00"
  • activateDefaultTyping

    • om.activateDefaultTyping(om.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL) 这行有点绕,咱们慢慢讲。
    • 啥意思? 默认情况下,Jackson 序列化对象时不会记录类型信息。反序列化时,它就不知道这个 JSON 应该还原成啥类型。
    • 加了啥? 这里启用了类型信息,模式是 NON_FINAL,意思是非 final 类的对象都会带上类型标记。
    • 举个栗子: 假设你存了个 User 对象 {name: "张三"},没类型信息就是 {"name": "张三"},加上类型后可能是 ["com.example.User", {"name": "张三"}]。反序列化时,Jackson 看到类型标记就知道要转成 User 类,不会出错。
    • 为啥用 NON_FINAL 因为 final 类的子类没法继承,类型固定,不需要额外标记。非 final 类可能有子类,类型信息就很重要。
  • serializer.setObjectMapper(om)

    • 把配置好的 ObjectMapper 交给 Jackson2JsonRedisSerializer,让它用这个规则去序列化 Value。

6. 设置 Value 和 HashValue 的序列化器

  • template.setValueSerializer(serializer):普通的值(比如 set("key", value) 的 value)用这个 Jackson 序列化器。
  • template.setHashValueSerializer(serializer):Hash 类型的值(比如 hset("key", "field", value) 的 value)也用这个序列化器。
  • 啥是 Hash? Redis 的 Hash 类似于 Java 的 Map,一个 Key 下可以存多个 field-value 对。用一样的序列化器,保证一致性。

7. 返回 RedisTemplate

  • return template:配置完后,把这个 RedisTemplate 返回,Spring 会把它放进容器,供项目其他地方用。

二、CacheWrapper 类:给缓存数据加个"保质期"

CacheWrapper 是一个包装类,用来把缓存数据和它的过期时间包在一起。代码如下:

java 复制代码
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CacheWrapper<T> {
    @JsonInclude(Include.NON_NULL)
    private T data;
    
    @JsonDeserialize(using = LongDeserializer.class)
    @JsonSerialize(using = ToStringSerializer.class)
    private Long expireTime;

    public boolean isExpired() {
        return System.currentTimeMillis() > expireTime;
    }
}

1. Lombok 注解:省事的"魔法"

  • @Data:自动生成 getter、setter、toString、equals、hashCode 方法,省得手写。
  • @AllArgsConstructor :生成全参构造器,比如 new CacheWrapper(data, expireTime)
  • @NoArgsConstructor :生成无参构造器,比如 new CacheWrapper(),Jackson 反序列化时需要这个。

2. 泛型 <T>:啥都能装

  • CacheWrapper<T> 用泛型 T,表示 data 可以是任意类型,比如 StringUserList<Integer>,灵活得一批。

3. data 字段:缓存的核心数据

  • @JsonInclude(Include.NON_NULL)
    • 这个注解的意思是,如果 datanull,序列化成 JSON 时就不包含这个字段。
    • 举个栗子: 假设 data = null, expireTime = 12345,序列化后是 {"expireTime": "12345"},而不是 {"data": null, "expireTime": "12345"}。这样 JSON 更干净,节省空间。

4. expireTime 字段:缓存的"保质期"

  • Long expireTime :这是缓存的过期时间,用毫秒表示(比如 System.currentTimeMillis() + 3600000 表示一小时后过期)。
  • 序列化问题: Long 类型在 JSON 里可能会丢精度(尤其大数字时),所以用了自定义的序列化方式:
    • @JsonSerialize(using = ToStringSerializer.class)
      • 序列化时,把 Long 转成字符串存进 JSON。比如 expireTime = 123456789,JSON 里是 "123456789",而不是数字 123456789
      • 为啥?因为 JavaScript 处理超大数字时精度会丢,转成字符串就没这问题。
    • @JsonDeserialize(using = LongDeserializer.class)
      • 反序列化时,把 JSON 里的字符串(比如 "123456789")转回 Long 类型。
      • 这俩注解配对使用,保证存取一致。

5. isExpired() 方法:检查"保质期"

  • public boolean isExpired() { return System.currentTimeMillis() > expireTime; }
    • 这个方法很简单,拿当前时间跟 expireTime 比,如果当前时间超过了 expireTime,说明缓存过期了,返回 true
    • 栗子: 当前时间是 10000expireTime = 900010000 > 9000,返回 true,缓存过期。

三、整体咋用?接地气的场景

  • 存缓存:

    java 复制代码
    RedisTemplate<String, Object> redisTemplate = // 从 Spring 拿
    CacheWrapper<User> wrapper = new CacheWrapper<>(new User("张三", 18), System.currentTimeMillis() + 3600000);
    redisTemplate.opsForValue().set("user:123", wrapper);
    • 存进 Redis 后,可能是这样的 JSON:{"data": {"name": "张三", "age": 18}, "expireTime": "1698316800000"}
  • 取缓存:

    java 复制代码
    CacheWrapper<User> wrapper = (CacheWrapper<User>) redisTemplate.opsForValue().get("user:123");
    if (wrapper != null && !wrapper.isExpired()) {
        User user = wrapper.getData();
        System.out.println(user.getName()); // 输出 "张三"
    } else {
        System.out.println("缓存过期或不存在");
    }

四、总结:为啥这么配?

  1. RedisTemplate
    • StringRedisSerializer 让 Key 简单清晰。
    • Jackson2JsonRedisSerializer 让 Value 能存复杂对象,还支持 Java 8 时间类型和类型信息。
  2. CacheWrapper
    • 把数据和过期时间包一起,方便判断缓存是否有效。
    • 用 Jackson 注解优化 JSON 格式,处理 Long 精度问题。

这套配置接地气又实用,适合大部分缓存场景。希望这篇博客讲得够细、够明白,如果你还有啥疑问,欢迎留言,咱们接着聊!

相关推荐
movee7 分钟前
一台低配云主机也能轻松愉快地玩RDMA
linux·人工智能·后端
程序员清风2 小时前
什么时候会考虑用联合索引?如果只有一个条件查就没有建联合索引的必要了么?
java·后端·面试
Seven972 小时前
【设计模式】掌握建造者模式:如何优雅地解决复杂对象创建难题?
java·后端·设计模式
子洋2 小时前
AnythingLLM + SearXNG 实现私有搜索引擎代理
前端·人工智能·后端
heart000_12 小时前
基于SpringBoot的智能问诊系统设计与隐私保护策略
java·spring boot·后端
汐泽学园2 小时前
基于ASP.NET校园二手交易网站设计与实现
后端·asp.net
MZWeiei3 小时前
Scala:case class(通俗易懂版)
开发语言·后端·scala
小杨4043 小时前
springboot框架项目实践应用三(监控运维组件admin)
spring boot·后端·监控
sevevty-seven5 小时前
Spring Boot 自动装配原理详解
java·spring boot·后端