在Java业务系统中,数据库IO瓶颈是导致性能下降的核心原因之一:据鳄鱼java社区2026年《Spring生态性能调研》显示,80%的高并发场景下,数据库QPS超过阈值后会引发响应延迟甚至雪崩,而缓存能将数据库压力降低90%以上。Spring Cache缓存抽象与Redis整合的核心价值,就在于用注解驱动的声明式编程替代冗余的手动缓存代码,同时借助Redis的分布式缓存能力,实现跨节点的缓存共享,让系统的吞吐量提升5-10倍,缓存代码量减少80%,成为企业级Spring项目中解决性能瓶颈的标准方案。
为什么需要Spring Cache+Redis?手动缓存与本地缓存的局限性

很多开发者会手动编写Redis缓存代码:比如在查询方法中先查Redis,不存在再查数据库,查询结果写入Redis;更新方法中更新数据库后再删除或更新Redis。但这种方式存在三大致命痛点:
其一,代码冗余且业务逻辑耦合,缓存逻辑会占据业务代码的40%以上,比如用户信息查询方法中,手动写Redis的get/set操作会让代码变得臃肿,鳄鱼java社区的开发者反馈,手动缓存的代码维护成本比纯业务代码高60%;其二,缓存一致性难以保障,容易出现更新后未清除缓存、清除缓存时遗漏关联key等问题,比如某电商项目曾因商品更新后未清除缓存,导致用户看到的还是旧价格,引发客诉;其三,本地缓存无法适配分布式场景,比如使用Caffeine、Guava Cache等本地缓存,在多节点部署时会出现缓存不一致的问题,每个节点的缓存数据独立,无法共享,导致重复查询数据库。
而Spring Cache的缓存抽象机制,通过注解驱动将缓存逻辑与业务代码解耦,同时整合Redis实现分布式缓存共享,完美解决了这些痛点。
Spring Cache缓存抽象核心:注解驱动的声明式缓存
Spring Cache是Spring提供的缓存抽象层,基于CacheManager和Cache接口实现,支持多种缓存中间件(Redis、Caffeine、EhCache等),其核心优势是通过注解实现声明式缓存,无需修改业务代码结构。
Spring Cache提供了5个核心注解,覆盖缓存的查询、更新、删除等全场景:
@Cacheable:标记方法的返回值将被缓存,下次调用时直接从缓存获取,避免重复执行方法(适用于查询操作);@CachePut:每次执行方法并将返回值存入缓存,用于更新缓存(适用于更新操作);@CacheEvict:清除指定缓存,用于删除或更新操作后清理缓存;@Caching:组合多个缓存注解,比如更新操作时同时清除多个缓存;@CacheConfig:统一配置类级别的缓存参数,比如缓存名、key生成器等;
基础示例:用@Cacheable实现用户信息查询缓存:
import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service;这段代码中,@Cacheable注解自动完成了“查缓存→缓存不存在查数据库→写入缓存”的逻辑,业务代码保持纯净,无需手动处理Redis操作。@Service @CacheConfig(cacheNames = "user") // 类级别统一配置缓存名 public class UserService { private final UserMapper userMapper;
public UserService(UserMapper userMapper) { this.userMapper = userMapper; } // 缓存key为userId,条件是userId不为空 @Cacheable(key = "#userId", condition = "#userId != null") public User getUserById(Long userId) { // 只有当缓存中没有数据时,才会执行该方法 return userMapper.selectById(userId); }}
Spring Cache缓存抽象与Redis整合:从依赖配置到自定义CacheManager
要实现Spring Cache缓存抽象与Redis整合,需要完成依赖配置、Redis连接配置、自定义CacheManager(解决序列化等问题)三个核心步骤,这是整合的关键:
1. 添加Maven依赖: 需要引入Spring Boot的Redis starter和Spring Cache依赖(Spring Boot已整合Spring Cache,无需额外引入):
<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连接:
在application.yml中配置Redis的地址、端口、密码、连接池等参数:
spring:
redis:
host: localhost
port: 6379
password: 123456
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
cache:
type: redis
3. 自定义CacheManager解决序列化问题: Spring Cache默认使用JDK序列化,存在序列化后体积大、不可读、无法跨语言等问题,因此需要自定义CacheManager,用Jackson2JsonRedisSerializer实现JSON序列化,同时解决Java 8时间API(LocalDateTime)的序列化问题:
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.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializationContext; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;鳄鱼java社区提醒:自定义CacheManager是整合Redis的关键步骤,解决了默认JDK序列化的弊端,同时可以统一配置缓存过期时间、空值缓存策略等。import java.time.Duration;
@Configuration @EnableCaching // 开启Spring Cache功能 public class RedisCacheConfig {
@Bean public RedisCacheConfiguration redisCacheConfiguration() { ObjectMapper objectMapper = new ObjectMapper(); // 注册JavaTimeModule,解决LocalDateTime序列化问题 objectMapper.registerModule(new JavaTimeModule()); // 禁用将日期序列化为时间戳 objectMapper.disable(com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); Jackson2JsonRedisSerializer<Object> jsonSerializer = new Jackson2JsonRedisSerializer<>(Object.class); jsonSerializer.setObjectMapper(objectMapper); return RedisCacheConfiguration.defaultCacheConfig() // 设置缓存过期时间为30分钟 .entryTtl(Duration.ofMinutes(30)) // 禁用缓存null值,避免缓存穿透(根据业务需求可选) .disableCachingNullValues() // 设置值的序列化器为JSON序列化 .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jsonSerializer)); }}
实战:用Spring Cache+Redis实现电商商品缓存
下面结合电商商品场景,完整展示Spring Cache+Redis的实战用法,包含查询缓存、更新缓存、清除缓存等操作:
1. 商品查询缓存:用@Cacheable缓存商品信息,key为商品ID,条件是商品ID不为空:
@Service
@CacheConfig(cacheNames = "product")
public class ProductService {
private final ProductMapper productMapper;
public ProductService(ProductMapper productMapper) {
this.productMapper = productMapper;
}
@Cacheable(key = "#productId", condition = "#productId != null")
public Product getProductById(Long productId) {
return productMapper.selectById(productId);
}
}
2. 商品更新缓存:用@CacheEvict清除缓存,或者用@CachePut更新缓存:
// 方法1:更新后清除缓存,下次查询时重新加载 @CacheEvict(key = "#product.id") @Transactional public void updateProduct
版权声明
本文仅代表作者观点,不代表百度立场。
本文系作者授权百度百家发表,未经许可,不得转载。





