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 等。通过合理地使用这些注解,可以显著提高应用程序的性能和响应速度。

相关推荐
后端小张10 小时前
基于飞算AI的图书管理系统设计与实现
spring boot
金銀銅鐵14 小时前
Spring 中的 initializeBean 方法的内部逻辑小总结
spring
考虑考虑1 天前
Jpa使用union all
java·spring boot·后端
阿杆2 天前
同事嫌参数校验太丑,我直接掏出了更优雅的 SpEL Validator
java·spring boot·后端
昵称为空C2 天前
SpringBoot3 http接口调用新方式RestClient + @HttpExchange像使用Feign一样调用
spring boot·后端
麦兜*3 天前
MongoDB Atlas 云数据库实战:从零搭建全球多节点集群
java·数据库·spring boot·mongodb·spring·spring cloud
麦兜*3 天前
MongoDB 在物联网(IoT)中的应用:海量时序数据处理方案
java·数据库·spring boot·物联网·mongodb·spring
汤姆yu3 天前
基于springboot的毕业旅游一站式定制系统
spring boot·后端·旅游
-Xie-3 天前
Mysql杂志(十六)——缓存池
数据库·mysql·缓存
七夜zippoe3 天前
缓存与数据库一致性实战手册:从故障修复到架构演进
数据库·缓存·架构