Spring Boot 默认缓存

Spring 提供了三个常用的注解:@Cacheable、@CachePut 和 @CacheEvict

一、@Cacheable,@CachePut,@CacheEvict区别

当使用缓存时,Spring 提供了三个常用的注解:@Cacheable、@CachePut 和 @CacheEvict,它们的区别如下:

@Cacheable 注解:

作用:将方法的返回值缓存起来,以便下次相同的方法调用时可以直接从缓存中获取结果。

使用场景:适用于读取操作频繁,但数据很少改变的场景。

示例代码:

java 复制代码
@Cacheable(value = "products", key = "#productId") 
public Product getProductById(Long productId) { 
// 从数据库中获取产品信息 
}

在上述示例中,使用 @Cacheable 注解将 getProductById 方法的返回值缓存起来,缓存的名称为 "products"。每次调用该方法时,如果缓存中存在对应的结果,就直接返回缓存值;否则执行方法逻辑,并将结果放入缓存中。

@CachePut 注解:

作用:将方法的返回值更新到缓存中。

使用场景:适用于写入或更新操作,需要将结果放入缓存并确保缓存的数据是最新的。

示例代码:

java 复制代码
@CachePut(value = "products", key = "#product.id") 
public Product saveProduct(Product product) { 
// 保存产品信息到数据库 return product; 
}

在上述示例中,使用 @CachePut 注解将 saveProduct 方法的返回值放入缓存中,缓存的名称为 "products"。每次调用该方法时,无论缓存中是否存在对应的值,都会执行方法逻辑,并将结果更新到缓存中。

@CacheEvict 注解:

作用:从缓存中移除一个或多个缓存项。

使用场景:适用于删除操作或数据更新后的缓存清理。

示例代码:

java 复制代码
@CacheEvict(value = "products", key = "#productId") 
public void deleteProduct(Long productId) { 
// 从数据库中删除产品信息
 }

在上述示例中,使用 @CacheEvict 注解将 deleteProduct 方法执行后,将缓存中指定 productId 的缓存项移除,缓存的名称为 "products"。

二、进阶使用

进阶使用 @Cacheable、@CachePut 和 @CacheEvict 注解的一些注意事项和高级用法包括:

多缓存管理器支持:如果应用程序中使用了多个缓存管理器,可以使用 cacheManager 属性指定具体的缓存管理器。

java 复制代码
@Cacheable(value = "products", key = "#productId", cacheManager = "cacheManager1") 
public Product getProductById(Long productId) { // ... }

条件缓存:可以使用 SpEL 表达式在注解中定义条件,只有满足条件时才进行缓存操作。

java 复制代码
@Cacheable(value = "products", key = "#productId", condition = "#productId > 0") 
public Product getProductById(Long productId) { // ... }

自定义缓存策略:通过实现 CacheResolver 接口或者使用 cacheResolver 属性,可以自定义缓存的解析和管理逻辑。

java 复制代码
@Cacheable(value = "products", key = "#productId", cacheResolver = "customCacheResolver") 
public Product getProductById(Long productId) { // ... }

缓存更新时机:可以使用 @CachePut 注解在方法执行后手动触发缓存更新,而不是每次方法调用都更新缓存。

java 复制代码
@CachePut(value = "products", key = "#product.id") 
public Product updateProduct(Product product) { // ... return product; }

缓存清除策略:使用 @CacheEvict 注解时,可以通过 beforeInvocation 属性来控制清除缓存的时机,默认是在方法执行后清除缓存。

java 复制代码
@CacheEvict(value = "products", key = "#productId", beforeInvocation = true) 
public void deleteProduct(Long productId) { // ... }

缓存注解继承:可以使用注解继承方式,简化对多个方法应用相同缓存注解的操作。

java 复制代码
@Inherited @Target(ElementType.METHOD) 
@Retention(RetentionPolicy.RUNTIME) 
@Cacheable(value = "products", key = "#productId") 
public @interface CachedProduct {
 // ...
  }

@CachedProduct 
public Product getProductById(Long productId) { 
   // ...
    } 
@CachedProduct 
public List<Product> getAllProducts() {
     // ...
      }

自定义缓存键生成策略:可以使用 keyGenerator 属性指定自定义的缓存键生成器。

java 复制代码
@Cacheable(value = "products",
 keyGenerator = "customKeyGenerator") 
 public Product getProductById(Long productId) {
  // ... }

SpEL 表达式使用:在注解的属性值中可以使用 SpEL 表达式,动态计算缓存键、条件等。

java 复制代码
@Cacheable(value = "products", key = "'product:' + #productId") 
public Product getProductById(Long productId) { 
// ...
 }

缓存注解的优先级:在方法上同时使用多个缓存注解时,它们的执行顺序和优先级可以通过 @Order 注解进行控制。

java 复制代码
@Cacheable(value = "products", key = "#productId") 
@CachePut(value = "products", key = "#productId") 
@Order(1) 
public Product getProductById(Long productId) { // ... }

配置缓存过期时间:可以使用缓存管理器的配置来设置缓存的过期时间,或者在注解中通过 expiration 属性指定缓存的过期时间。

java 复制代码
@Configuration 
@EnableCaching 
public class CacheConfig extends CachingConfigurerSupport { 
// ... 
@Override public CacheManager cacheManager() 
{ 
SimpleCacheManager cacheManager = new SimpleCacheManager(); 
// 设置缓存过期时间
 cacheManager.setCaches(Arrays.asList( new ConcurrentMapCache("products", getExpirationDuration(30)), 
 // ...
  )); 
  return cacheManager; 
  } 
  private Duration getExpirationDuration(int minutes) {
   return Duration.ofMinutes(minutes);
    } } 
    @Cacheable(value = "products", key = "#productId", expiration = 10) public Product getProductById(Long productId) {
     // ... 
     }

缓存条件判断:可以使用 condition 属性在注解中指定一个 SpEL 表达式,根据条件判断是否执行缓存操作。

java 复制代码
@Cacheable(value = "products",
 key = "#productId", condition = "#productId > 0")
 public Product getProductById(Long productId) { // ... }

同步缓存操作:使用 @CachePut 注解可以实现同步缓存操作,即先执行方法,然后更新缓存。

java 复制代码
@CachePut(value = "products", key = "#product.id") 
public Product updateProduct(Product product) { 
// ... return product; }

缓存清除策略:@CacheEvict 注解可以用于清除缓存中的数据,可以通过 key 属性指定要清除的缓存项。

java 复制代码
@CacheEvict(value = "products", key = "#productId") 
public void deleteProduct(Long productId) {
 // ... 
 }

缓存注解顺序:当一个方法上同时存在多个缓存注解时,可以使用 @CacheConfig 注解或 @Order 注解来控制注解的执行顺序。

java 复制代码
@CacheConfig(cacheNames = "products") 
public class ProductRepository { 
@Cacheable(key = "#id") 
@CachePut(key = "#result.id") 
public Product getProductById(Long id) { 
// ... 
} }

使用 SpEL 表达式:可以在注解中使用 SpEL 表达式动态地生成缓存键。

java 复制代码
@Cacheable(value = "products", key = "'product:' + #productId") 
public Product getProductById(Long productId) {
 // ... 
 }

缓存与事务管理:在使用缓存注解时,需要注意与事务管理的交互。默认情况下,Spring 的事务管理会在方法执行前将缓存清空,以保证数据的一致性。如果希望在事务提交后再执行缓存操作,可以使用 @CachePut 注解并将方法放在一个新的事务中。

java 复制代码
@Transactional public void updateProduct(Product product) {
 // 更新数据库中的数据 
 // ... 
 // 手动执行缓存操作
  updateProductCache(product); 
  } 
  @CachePut(value = "products", key = "#product.id") @Transactional(propagation = Propagation.REQUIRES_NEW) 
  public Product updateProductCache(Product product) { 
  // 更新缓存中的数据 
  // ... 
  return product;
   }

多级缓存配置:可以配置多个级别的缓存,例如使用一级缓存作为本地缓存,二级缓存作为分布式缓存。可以通过 @CacheConfig 注解和 CacheManager 进行配置。

java 复制代码
@CacheConfig(cacheNames = "products") 
public class ProductRepository { 
@Autowired private CacheManager cacheManager; 
@Cacheable(key = "#id", cacheManager = "localCacheManager") 
public Product getProductById(Long id) {
 
 // ... 
 } 
 @Cacheable(key = "#id", cacheManager = "distributedCacheManager") public Product getProductByIdFromDistributedCache(Long id) { 
 // ...
  } }

缓存预热:可以在应用启动时,通过调用特定的方法来预先加载缓存数据,以提高系统的性能和响应速度。

java 复制代码
@Component public class CachePreloader 
{ 
@Autowired private ProductRepository productRepository; 
@PostConstruct public void preloadCache() {
 List<Product> products = productRepository.getAllProducts(); for (Product product : products) { productRepository.getProductById(product.getId()); 
 } } }

异步缓存操作:使用异步方式执行缓存操作,以减少对主线程的影响,提高系统的并发性能。

java 复制代码
@CachePut(value = "products", key = "#product.id") 
@Async 
public CompletableFuture<Product> updateProductAsync(Product product) { // ... 
return CompletableFuture.completedFuture(product);
 }

以上是一些进阶用法和注意事项,它们可以帮助你更好地使用 @Cacheable、@CachePut 和 @CacheEvict 注解来管理缓存,并根据具体的业务需求和场景进行优化和配置。请根据实际情况选择合适的用法,并结合缓存框架和缓存管理器的文档进行深入研究和调整。

三、总结

@Cacheable 注解用于指示方法的结果应该被缓存,以提高后续对相同输入参数调用的性能。它会首先检查缓存中是否存在结果,如果存在,则直接返回缓存中的值;如果不存在,则执行方法并将结果存入缓存。可以通过设置缓存的键(key)来区分不同的缓存项。

@CachePut 注解用于指示方法的结果应该被缓存,但它每次都会执行方法并将结果存入缓存,不会像 @Cacheable 那样检查缓存中是否已存在结果。它常用于更新缓存中的数据,确保缓存的数据与数据库或其他数据源保持同步。

@CacheEvict 注解用于指示方法执行后应清除缓存中的某些项。可以通过设置缓存的键(key)来指定要清除的特定缓存项。它还提供了一些属性,如 allEntries 和 beforeInvocation,用于清除所有缓存项或在方法执行前清除缓存。

这些注解可以与不同的缓存提供程序集成,如 Spring Boot 默认的缓存提供程序 Caffeine、Ehcache、Redis 等。通过合理地使用这些注解,可以显著提高应用程序的性能和响应速度。

相关推荐
lang201509282 小时前
Spring Boot优雅关闭全解析
java·spring boot·后端
刘一说3 小时前
Spring Boot 启动慢?启动过程深度解析与优化策略
java·spring boot·后端
壹佰大多3 小时前
【spring如何扫描一个路径下被注解修饰的类】
java·后端·spring
DokiDoki之父4 小时前
Spring—注解开发
java·后端·spring
_Johnny_4 小时前
Redis 升级操作指南:单机与主从模式
数据库·redis·缓存
不爱洗脚的小滕4 小时前
【Redis】三种缓存问题(穿透、击穿、双删)的 Golang 实践
redis·缓存·golang
提笔了无痕4 小时前
什么是Redis的缓存问题,以及如何解决
数据库·redis·后端·缓存·mybatis
lang201509284 小时前
Spring Boot缓存机制全解析
spring boot·后端·缓存
摇滚侠4 小时前
Spring Boot 3零基础教程,WEB 开发 默认页签图标 Favicon 笔记29
java·spring boot·笔记
lang201509284 小时前
Spring Boot SQL数据库全攻略
数据库·spring boot·sql