免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

Spring Cache 使用详解:从入门到实战

Spring Cache 使用详解:从入门到实战 1. 引言在 Web 应用开发中缓存是提升系统性能最直接、最有效的手段之一。Spring 框架从 3.1 版本开始提供了基于注解的缓存抽象Spring Cache Abstraction它不依赖任何具体的缓存实现而是通过统一的注解和 API让开发者能够以极低的侵入成本为方法添加缓存能力。本文将带你系统掌握 Spring Cache 的核心概念、常用注解、配置方式以及实战中的最佳实践帮助你快速在项目中落地缓存方案。2. Spring Cache 核心概念Spring Cache 的核心思想是对方法的返回值进行缓存。当调用一个被缓存注解标记的方法时Spring 会先检查缓存中是否存在对应的数据如果存在则直接返回缓存结果不再执行方法体如果不存在则执行方法并将返回值存入缓存。理解下面三个核心接口就掌握了 Spring Cache 的骨架Cache缓存接口定义了缓存的读写、删除等基本操作如get、put、evict。CacheManager缓存管理器负责管理一组Cache实例根据缓存名称创建和管理具体的缓存区域。KeyGenerator键生成器用于根据方法参数生成缓存 key。默认使用SimpleKeyGenerator基于方法参数生成 key。3. 快速开始环境准备3.1 引入依赖以 Maven 为例在pom.xml中添加 Spring Cache 依赖。如果使用 Spring Boot只需引入spring-boot-starter-cachedependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-cache/artifactId/dependency如果使用 Spring 传统项目则需要引入spring-context和spring-context-support。3.2 开启缓存支持在配置类上添加EnableCaching注解即可开启 Spring Cache 的注解驱动支持ConfigurationEnableCachingpublicclassCacheConfig{}3.3 配置缓存管理器Spring Boot 默认使用ConcurrentMapCacheManager无需额外配置即可运行。生产环境通常使用 Redis 或 Caffeine后面章节会详细介绍。4. 核心注解详解4.1 CacheableCacheable是最常用的注解用于标记一个方法的返回值需要被缓存。当方法被调用时Spring 会先根据 key 查找缓存命中则直接返回未命中则执行方法并缓存结果。ServicepublicclassUserService{Cacheable(valueuser,key#id)publicUsergetUserById(Longid){// 模拟耗时查询returnuserMapper.selectById(id);}}value/cacheNames指定缓存名称可以是一个数组表示多个缓存区域。key指定缓存的 key支持 SpEL 表达式。默认 key 由方法参数决定。condition满足条件才缓存如condition #id 10。unless满足条件则不缓存如unless #result null。4.2 CachePutCachePut用于更新缓存它会始终执行方法并将返回值写入缓存。适合在数据更新后同步刷新缓存的场景。CachePut(valueuser,key#user.id)publicUserupdateUser(Useruser){userMapper.updateById(user);returnuser;}4.3 CacheEvictCacheEvict用于删除缓存通常在删除数据或数据失效时使用。CacheEvict(valueuser,key#id)publicvoiddeleteUser(Longid){userMapper.deleteById(id);}allEntries true清空该缓存区域下的所有数据。beforeInvocation true在方法执行前删除缓存默认在方法执行后删除。4.4 Caching当需要在一个方法上同时应用多个缓存操作时可以使用Caching组合注解Caching(put{CachePut(valueuser,key#user.id)},evict{CacheEvict(valueuserList,allEntriestrue)})publicUsersaveUser(Useruser){userMapper.insert(user);returnuser;}4.5 CacheConfigCacheConfig是类级别注解用于统一指定该类中缓存注解的公共属性如缓存名称ServiceCacheConfig(cacheNamesuser)publicclassUserService{// 类中的 Cacheable 无需再写 valueCacheable(key#id)publicUsergetUserById(Longid){...}}5. 缓存 Key 的生成策略默认情况下Spring 使用SimpleKeyGenerator生成 key无参方法使用SimpleKey.EMPTY单参数方法直接使用该参数多参数方法使用SimpleKey包装所有参数。实际开发中推荐使用 SpEL 表达式自定义 key以更精确地控制缓存粒度// 使用对象属性作为 keyCacheable(valueuser,key#user.id)// 组合多个参数Cacheable(valueorder,key#userId : #orderNo)// 使用方法返回值属性仅 CachePut 可用CachePut(valueuser,key#result.id)当默认策略无法满足需求时可以实现KeyGenerator接口自定义 key 生成器Component(customKeyGenerator)publicclassCustomKeyGeneratorimplementsKeyGenerator{OverridepublicObjectgenerate(Objecttarget,Methodmethod,Object...params){returntarget.getClass().getSimpleName()_method.getName()_Arrays.toString(params);}}使用时通过keyGenerator属性指定Cacheable(valueuser,keyGeneratorcustomKeyGenerator)publicUsergetUserById(Longid){...}6. 集成 Redis 缓存生产环境中Redis 是最常用的分布式缓存方案。在 Spring Boot 中集成 Redis 缓存非常简单。6.1 引入依赖dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId/dependency6.2 配置 Redis 连接在application.yml中配置 Redis 连接信息spring:redis:host:localhostport:6379password:database:06.3 自定义 RedisCacheManager为了让缓存值以 JSON 格式存储并设置合理的过期时间需要自定义RedisCacheManagerConfigurationpublicclassRedisCacheConfig{BeanpublicRedisCacheManagercacheManager(RedisConnectionFactoryfactory){// 配置序列化方式GenericJackson2JsonRedisSerializerserializernewGenericJackson2JsonRedisSerializer();RedisCacheConfigurationconfigRedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(serializer)).entryTtl(Duration.ofMinutes(30))// 默认过期时间 30 分钟.disableCachingNullValues();// 为不同缓存名称设置不同的过期时间MapString,RedisCacheConfigurationcacheConfigurationsnewHashMap();cacheConfigurations.put(user,config.entryTtl(Duration.ofHours(1)));cacheConfigurations.put(order,config.entryTtl(Duration.ofMinutes(10)));returnRedisCacheManager.builder(factory).cacheDefaults(config).withInitialCacheConfigurations(cacheConfigurations).build();}}7. 集成 Caffeine 本地缓存Caffeine 是高性能的本地缓存库适合单机场景或作为多级缓存的一级缓存。7.1 引入依赖dependencygroupIdcom.github.ben-manes.caffeine/groupIdartifactIdcaffeine/artifactId/dependency7.2 配置 CaffeineCacheManagerConfigurationpublicclassCaffeineCacheConfig{BeanpublicCacheManagercacheManager(){CaffeineCacheManagercacheManagernewCaffeineCacheManager(user,order);cacheManager.setCaffeine(Caffeine.newBuilder().maximumSize(500)// 最大缓存条目数.expireAfterWrite(Duration.ofMinutes(10))// 写入后 10 分钟过期.recordStats());// 开启统计returncacheManager;}}8. 多级缓存实战在大型系统中常采用「本地缓存 分布式缓存」的多级缓存架构兼顾性能与一致性。下面是一个简单的二级缓存示例ServicepublicclassProductService{AutowiredprivateCacheManagercacheManager;publicProductgetProduct(Longid){// 1. 先查本地缓存CaffeineCachelocalCachecacheManager.getCache(localProduct);ProductproductlocalCache.get(id,Product.class);if(product!null){returnproduct;}// 2. 再查 Redis 缓存CacheredisCachecacheManager.getCache(redisProduct);productredisCache.get(id,Product.class);if(product!null){localCache.put(id,product);returnproduct;}// 3. 最后查数据库并回填缓存productproductMapper.selectById(id);if(product!null){redisCache.put(id,product);localCache.put(id,product);}returnproduct;}}9. 常见问题与最佳实践9.1 缓存穿透缓存穿透指查询一个不存在的数据导致请求直接打到数据库。解决方案缓存空值在Cacheable中设置unless #result null会跳过缓存应改为缓存空值并设置较短过期时间。使用布隆过滤器在缓存前先判断 key 是否存在。9.2 缓存雪崩缓存雪崩指大量缓存同时过期导致请求全部打到数据库。解决方案设置随机过期时间避免同时失效。使用多级缓存本地缓存兜底。限流降级保护数据库。9.3 缓存击穿缓存击穿指某个热点 key 过期瞬间大量请求同时打到数据库。解决方案使用互斥锁如synchronized、Redisson 分布式锁保证只有一个线程去查询数据库。热点数据设置永不过期后台异步更新。9.4 缓存一致性当数据更新时需要保证缓存与数据库的一致性。推荐策略先更新数据库再删除缓存Cache Aside 模式。对于强一致场景可使用分布式事务或消息队列异步同步。9.5 注意事项Cacheable基于 Spring AOP 实现同类内部方法调用不会触发缓存因为代理不生效。缓存注解作用于public方法private方法无法被代理。序列化对象必须实现Serializable接口使用 JDK 序列化时。合理设置缓存过期时间避免数据长期不一致。10. 总结Spring Cache 通过简洁的注解抽象大幅降低了缓存接入的成本。本文从核心概念出发依次介绍了常用注解、Key 生成策略、Redis 与 Caffeine 集成、多级缓存实战以及常见问题的最佳实践。在实际项目中建议根据业务场景选择合适的缓存方案单机小规模应用可使用 Caffeine分布式系统优先选择 Redis追求极致性能可组合使用多级缓存。同时务必关注缓存穿透、雪崩、击穿等经典问题做好兜底设计才能真正发挥缓存的威力。
返回列表