如何在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
相关产品推荐
相关产品推荐

