Spring缓存注解@Cache,@CachePut , @CacheEvict,@CacheConfig使用

本文深入解析Spring框架中用于缓存管理的三个核心注解@Cacheable、@CachePut、@CacheEvict的功能、参数及其配置方法,并提供实例代码展示其应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

@Cacheable、@CachePut、@CacheEvict 注释介绍

表 1. @Cacheable 作用和配置方法
@Cacheable 的作用  主要针对方法配置,能够根据方法的请求参数对其结果进行缓存
@Cacheable 主要的参数
value 缓存的名称,在 spring 配置文件中定义,必须指定至少一个 例如:
@Cacheable(value=”mycache”) 或者 
@Cacheable(value={”cache1”,”cache2”}
key 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合 例如:
@Cacheable(value=”testcache”,key=”#userName”)
condition 缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存 例如:
@Cacheable(value=”testcache”,condition=”#userName.length()>2”)
------------------------------------------------------------
--////////////////////////////////////////////////////////////////////////////////
表 2. @CachePut 作用和配置方法
@CachePut 的作用  主要针对方法配置,能够根据方法的请求参数对其结果进行缓存,和 @Cacheable 不同的是,它每次都会触发真实方法的调用
@CachePut 主要的参数
value 缓存的名称,在 spring 配置文件中定义,必须指定至少一个 例如:
@Cacheable(value=”mycache”) 或者 
@Cacheable(value={”cache1”,”cache2”}
key 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合 例如:
@Cacheable(value=”testcache”,key=”#userName”)
condition 缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存 例如:
@Cacheable(value=”testcache”,condition=”#userName.length()>2”)

//////////////////////////////////////////////////////

表 3. @CacheEvict 作用和配置方法
@CachEvict 的作用  主要针对方法配置,能够根据一定的条件对缓存进行清空
@CacheEvict 主要的参数
value 缓存的名称,在 spring 配置文件中定义,必须指定至少一个 例如:
@CachEvict(value=”mycache”) 或者 
@CachEvict(value={”cache1”,”cache2”}
key 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合 例如:
@CachEvict(value=”testcache”,key=”#userName”)
condition 缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才清空缓存 例如:
@CachEvict(value=”testcache”,
condition=”#userName.length()>2”)
allEntries 是否清空所有缓存内容,缺省为 false,如果指定为 true,则方法调用后将立即清空所有缓存 例如:
@CachEvict(value=”testcache”,allEntries=true)
beforeInvocation 是否在方法执行前就清空,缺省为 false,如果指定为 true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存 例如:
@CachEvict(value=”testcache”,beforeInvocation=true)

--------------
额外补充 :@cache(“something");这个相当于save()操作,@cachePut相当于Update()操作,只要他标示的方法被调用,那么都会缓存起来,而@cache则是先看下有没已经缓存了,然后再选择是否执行方法。@CacheEvict相当于Delete()操作。用来清除缓存用的。

这写配置的声明需要配置好了@enableCache才有用,具体的配置可以看这篇文章
http://blog.csdn.net/sanjay_f/article/details/47363845

如果忘记了SpEL怎么用了,  do yourself a favor and read  Chapter 9, Spring Expression Language (SpEL) :

-------------
importorg.springframework.stereotype.Service;
importcom.springcache.annotation.Cacheable;
@Service
@Cacheable
public class MemcachedService{
  @Cacheable(name="remote",key="'USER_NAME_'+#args[0]")
public String storeUserName(String accountId,String name)
{
  return name;
}
  @Cacheable(name="remote")
 public String storeUserAddress(String accountId,String address){
   return address;
  }
}





不知道你们注意到一个问题没有,就是所有的@Cacheable()里面都有一个name=“xxx”的属性,这显然如果方法多了,写起来也是挺累的,如果可以一次性声明完 那就省事了,
所以,有了@CacheConfig这个配置, @CacheConfig  is a class-level annotation that allows to share the cache names,不过不用担心,如果你在你的方法写别的名字,那么依然以方法的名字为准。

@CacheConfig("books")
public class BookRepositoryImpl implements BookRepository {

    @Cacheable
    public Book findBook(ISBN isbn) {...}
}


当然还有另外一个情况, @ Cacheable ( name = "remote" , key = "'USER_NAME_'+#args[0]" ,conditional=“xxx”,allEntries=true,beforeInvocation=true ) ,像这样的配置就很长,
@Cacheable(name = "book", key="#isbn",conditional=“xxx”,allEntries=true,beforeInvocation=true
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
这样的配置很长,而且有可能声明在很多个方法的,所以我们很想精简点,容易配置些。所以
@findBookByIsbnervice
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

新建一个文件 findBookByIsbn, 内容如下
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Cacheable(cacheNames="books", key="#isbn")
public @interface findBookByIsbn {
}



-------------------------------

Features summary

For those who are familiar with Spring’s caching annotations, the following table describes the main differences between the Spring annotations and the JSR-107 counterpart:

Table 35.3. Spring vs. JSR-107 caching annotations

Spring JSR-107 Remark

@Cacheable

@CacheResult

Fairly similar. @CacheResult can cache specific exceptions and force the execution of the method regardless of the content of the cache.

@CachePut

@CachePut

While Spring updates the cache with the result of the method invocation, JCache requires to pass it as an argument that is annotated with @CacheValue. Due to this difference, JCache allows to update the cache before or after the actual method invocation.

@CacheEvict

@CacheRemove

Fairly similar. @CacheRemove supports a conditional evict in case the method invocation results in an exception.

@CacheEvict(allEntries=true)

@CacheRemoveAll

See @CacheRemove.

@CacheConfig

@CacheDefaults

Allows to configure the same concepts, in a similar fashion.


--------------


关于异常

JCache can manage exceptions thrown by annotated methods:

this can prevent an update of the cache but it can also cache the exception as an indicator of the failure instead of calling the method again. 

Let’s assume that InvalidIsbnNotFoundException is thrown if the structure of the ISBN is invalid.

This is a permanent failure, no book could ever be retrieved with such parameter. 

The following caches the exception so that further calls with the same,

invalid ISBN, throws the cached exception directly instead of invoking the method again.

@CacheResult(cacheName="books", exceptionCacheName="failures"
             cachedExceptions = InvalidIsbnNotFoundException.class)
public Book findBook(ISBN isbn)



<think>嗯,用户让我介绍一下如何使用Spring@Cacheable、@CachePut@CacheEvict注解来优化应用性能,并且生成一篇经验案例。首先,我需要确认自己对这三个注解的理解是否正确,然后考虑如何结构清晰地组织内容。 首先,我应该回忆这三个注解的作用。@Cacheable用于方法的返回值缓存,当方法被调用时,会先检查缓存是否存在,存在则直接返回,不执行方法。@CachePut则是无论缓存是否存在,都会执行方法,并将结果存入缓存,适合更新操作。@CacheEvict用于清除缓存,可以在方法执行后或前清除指定的缓存条目。 接下来,用户需要的是经验案例,所以最好结合实际场景来说明。可能需要虚构一个应用场景,比如电商系统中的商品信息查询,这里通常会遇到频繁读取相同数据的情况,适合使用缓存优化。 然后,我需要考虑如何分步骤介绍这三个注解使用。可能的结构是先介绍每个注解的用途和参数,再给出示例代码,最后总结最佳实践和注意事项。 需要注意用户可能对Spring缓存抽象不太熟悉,所以需要解释一些基本概念,比如CacheManager、缓存名称、键的生成策略等。此外,还要提到如何配置缓存,比如使用EhCacheRedis作为缓存实现。 可能还需要提到常见的陷阱,比如缓存穿透、雪崩、击穿的问题,以及如何通过设置过期时间、使用互斥锁等方式来避免。同时,强调一致性问题的处理,比如在更新数据库后及时清除或更新缓存,避免脏数据。 另外,用户可能需要知道如何测试缓存的效果,比如通过日志查看缓存命中情况,或者使用Spring缓存统计信息。这部分也可以简要提及。 最后,确保整个案例结构清晰,有实际代码示例,并且语言通俗易懂,避免过于技术化的术语,但又要足够详细,让读者能够按照步骤实现。</think>以下是一篇基于Spring缓存注解的性能优化实战案例: --- ### 电商平台商品服务缓存优化实战 #### 场景背景 某电商平台商品服务接口QPS峰值达5000+,频繁出现以下问题: 1. 热门商品查询数据库压力过大 2. 商品价格更新后存在短暂数据不一致 3. 促销时段接口响应时间波动明显 #### 技术选型 采用Spring Cache + Redis二级缓存方案: ```properties # 配置文件示例 spring.cache.type=redis spring.redis.host=127.0.0.1 spring.redis.port=6379 spring.cache.redis.time-to-live=3600s ``` #### 核心实现 1. **商品查询缓存** ```java @Service public class ProductService { @Cacheable(value = "products", key = "#productId", unless = "#result.stock < 10") public Product getProductDetail(Long productId) { // 数据库查询逻辑 return productRepository.findById(productId); } } ``` * 参数说明: - `value`:定义缓存名称 - `key`:使用SpEL表达式构建缓存键 - `unless`:库存低于10时不缓存 2. **价格更新双写策略** ```java @CachePut(value = "products", key = "#product.id") public Product updatePrice(Product product) { productRepository.updatePrice(product); return product; // 必须返回更新后的对象 } @Transactional @CacheEvict(value = "products", key = "#productId", beforeInvocation = true) public void emergencyPriceUpdate(Long productId, BigDecimal newPrice) { // 先清理缓存再执行数据库更新 productRepository.updatePrice(productId, newPrice); } ``` 3. **库存批量清理** ```java @CacheEvict(value = "products", allEntries = true) public void batchUpdateInventory(List<Product> products) { productRepository.batchUpdate(products); } ``` #### 高级配置技巧 1. 自定义键生成器(解决复杂参数问题) ```java @Bean public KeyGenerator productKeyGenerator() { return (target, method, params) -> { StringBuilder sb = new StringBuilder(); sb.append(params[0].getClass().getSimpleName()); sb.append("_"); sb.append(((Product)params[0]).getId()); return sb.toString(); }; } ``` 2. 多级缓存配置(Caffeine + Redis) ```java @Configuration @EnableCaching public class CacheConfig { @Bean public CacheManager cacheManager() { CaffeineCacheManager caffeineCacheManager = new CaffeineCacheManager(); caffeineCacheManager.setCaffeine(Caffeine.newBuilder() .expireAfterWrite(10, TimeUnit.MINUTES) .maximumSize(1000)); RedisCacheManager redisCacheManager = RedisCacheManager.create(redisConnectionFactory()); return new CompositeCacheManager( caffeineCacheManager, redisCacheManager ); } } ``` #### 避坑指南 1. **缓存穿透防护** ```java @Cacheable(value = "products", key = "#id", unless = "#result == null", cacheResolver = "nullSafeCacheResolver") ``` 2. **分布式锁集成** ```java @Cacheable(value = "hotProducts", key = "#id", cacheManager = "lockAwareCache
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值