You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Spring Boot应用中缓存Map<String, List<Car>>这类复杂对象

搞定Spring Boot复杂对象的分布式缓存问题

嘿,我明白你的困扰——负载均衡下本地缓存不一致,Redis又误以为只能存字符串,@Cacheable还不生效,确实头疼。我之前处理过类似的场景,下面给你一步步捋清楚解决方案:

先搞懂为啥你的@Cacheable没生效

首先排查几个最常见的坑:

  • 内部调用踩坑:如果这个buildComplexObject方法是被同一个类里的其他方法调用的,Spring的AOP代理就会失效,@Cacheable根本不会触发。必须通过类的外部对象来调用这个方法才行。
  • 参数拖后腿:你的方法参数里带了CarsRepository这个Spring Bean,默认的Key生成器会把它算进缓存Key里,而每个实例的Bean引用都不一样,导致每次生成的Key都不同,自然永远命中不了缓存。
  • 序列化没配置对:哪怕ComplexObject实现了Serializable,Redis缓存时如果没配合适的序列化器,对象可能序列化失败,缓存直接失效。

Redis缓存复杂对象的正确姿势

Redis完全支持复杂对象缓存,Spring Cache整合Redis时,只要配置好序列化器就行,不用手动转成String。

1. 先把依赖加好

确保你的Maven(或Gradle)里有这两个依赖:

<!-- Maven -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>

2. 配置Redis的序列化器

写一个配置类,用JSON序列化器替代默认的JDK序列化(JSON可读性更高,还能避免JDK序列化的兼容性问题):

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.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;

@Configuration
@EnableCaching
public class RedisCacheConfig {

    @Bean
    public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                // 用GenericJackson2JsonRedisSerializer来序列化对象
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));

        return RedisCacheManager.builder(connectionFactory)
                .cacheDefaults(config)
                .build();
    }
}

3. 修正@Cacheable的用法

把CarsRepository从方法参数移到类的成员变量里注入,这样缓存Key就只由customerName决定了,不会被Bean引用干扰:

@Service
public class YourCarService {

    // 把Repository注入到类里,不要放在方法参数里
    private final CarsRepository carRepository;

    // 构造方法注入(Spring推荐的方式)
    public YourCarService(CarsRepository carRepository) {
        this.carRepository = carRepository;
    }

    @Cacheable(value = "customerCars", key = "#customerName")
    public ComplexObject buildComplexObject(String customerName) {
        System.out.println("here"); // 只有第一次调用或缓存失效时才会打印
        // 这里写你的业务逻辑:从Repository查数据,构建ComplexObject
        ComplexObject complexObj = new ComplexObject();
        // ... 填充Map<String, List<Car>>之类的内容
        return complexObj;
    }
}

4. 配置文件收尾

在application.properties里配置Redis的连接信息:

spring.cache.type=redis
spring.redis.host=localhost
spring.redis.port=6379
# 如果Redis有密码,就加上下面这行
# spring.redis.password=your-redis-password

验证缓存是否生效

  • 第一次调用buildComplexObject,控制台会打印"here";
  • 第二次用同一个customerName调用,控制台不会打印"here",说明命中缓存了;
  • 用redis-cli查看缓存内容,能看到序列化后的JSON字符串,证明复杂对象已经存在Redis里了:
redis-cli
# 查看缓存key
> keys *customerCars*
# 获取具体缓存内容
> get "customerCars::your-test-customer"

额外提醒

  • 内部对象也要能序列化:如果ComplexObject里的Car类没有实现Serializable,只要它有getter/setter,Jackson序列化器也能处理;如果有特殊字段,记得用@JsonProperty注解标记;
  • 设置缓存过期时间:可以在RedisCacheConfiguration里加一行configureTtl(Duration.ofMinutes(30)),让缓存30分钟后自动失效;
  • 分布式缓存一致性:如果有实例修改了数据,记得手动删除对应的缓存Key,或者用Redis的发布订阅机制通知其他实例清缓存,避免出现脏数据。

内容的提问来源于stack exchange,提问作者riorio

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 04:22:35