当前位置: 首页 > article >正文

Spring Cache @Cacheable:提升应用性能的利器

  在构建企业级应用时,性能优化至关重要。Spring Cache 提供了一种简便而强大的方式来缓存方法调用的结果,从而减少数据库访问、提高响应速度。其中,@Cacheable 注解是 Spring Cache 的核心,本文将深入剖析 @Cacheable 注解,助你掌握缓存技术,打造更高效的应用。

一、Spring Cache 简介

  Spring Cache 是 Spring 框架提供的抽象层,用于简化缓存的使用。它提供了一组注解和接口,使得开发者可以轻松地集成不同的缓存解决方案(如Ehcache、Caffeine、Redis等),而不必关心底层的具体实现细节。Spring缓存抽象层主要包括以下几个核心组件:

  • 缓存管理器(CacheManager):负责管理缓存实例。
  • 缓存(Cache):实际存储缓存数据的容器。
  • 缓存注解:如@Cacheable、@CachePut、@CacheEvict等,用于定义缓存的行为。

二、@Cacheable 注解的作用

  @Cacheable 注解用于将方法的返回值缓存起来。当调用被 @Cacheable 注解的方法时,Spring Cache 会首先检查缓存中是否存在对应 key 的缓存数据。如果缓存中存在数据,则直接从缓存中获取数据并返回,不会执行方法体。如果缓存中不存在数据,则执行方法体,并将方法返回值缓存到指定的缓存中。

三、@Cacheable 注解的属性

  • cacheNames 或 value: 指定缓存的名称。可以指定一个或多个缓存名称。
@Cacheable(value = "userCache")
  • key: 指定缓存的 key。可以使用 SpEL 表达式来动态生成 key。
@Cacheable(value = "users", key = "#id")
public User getUserById(Long id) { ... }
  • condition: 指定缓存的条件。只有满足条件时,才会进行缓存。可以使用 SpEL 表达式来定义条件。
@Cacheable(value = "users", condition = "#id > 0")
public User getUserById(Long id) { ... }
  • unless: 指定不缓存的条件。只有不满足条件时,才会进行缓存。可以使用 SpEL 表达式来定义条件。
@Cacheable(value = "users", unless = "#result == null")
public User getUserById(Long id) { ... }
  • sync: 设置为 true 时, 只有一个线程可以执行方法体, 其他线程会被阻塞直到方法执行完成。 用于解决缓存击穿问题。
@Cacheable(value = "users", sync = true)
public User getUserById(Long id) { ... }
  • keyGenerator: 指定 KeyGenerator 的 Bean 名称。用于自定义 key 的生成策略。
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.lang.reflect.Method;
import java.util.Arrays;
@Configuration
public class MyCacheConfig {
    @Bean("myKeyGenerator")
    public KeyGenerator keyGenerator(){
        return new KeyGenerator(){
            @Override
            public Object generate(Object target, Method method, Object... params) {
                return method.getName()+"["+ Arrays.asList(params).toString()+"]";
            }
        };
    }
}
@Cacheable(value="emp", keyGenerator = "myKeyGenerator")
  • cacheManager: 指定 CacheManager 的 Bean 名称。用于指定缓存管理器。
@Configuration
public class CustomCacheManager implements CacheManager {
    // 实现CacheManager接口的方法,例如getCache, getCacheNames等
}
@Cacheable(value = "someCache", cacheManager = "customCacheManager")

四、简单示例

4.1 启用缓存

  在 Spring Boot 启动类上添加 @EnableCaching 注解,启用缓存功能。

import org.springframework.cache.annotation.EnableCaching;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@EnableCaching // 启用缓存
public class MyApplication {

    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

4.2 使用 @Cacheable 注解

import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

@Service
public class ProductService {

    @Cacheable(cacheNames = "products", key = "#id")
    public Product getProductById(Long id) {
        System.out.println("Executing getProductById method..."); // 模拟从数据库查询
        // 从数据库查询 Product
        Product product = new Product(id, "Product Name " + id);
        return product;
    }
}

  在这个示例中,getProductById 方法会被缓存, 缓存名称为 products, 缓存 Key 为 id。 当第一次调用 getProductById 方法时,会执行方法体,并将返回的 Product 对象缓存到 products 缓存中。 当后续再次调用 getProductById 方法时,会直接从缓存中获取数据,而不会执行方法体,从而提高了应用的性能。

4.3 定义缓存配置

  可以在 application.properties 或 application.yml 文件中定义缓存配置。

spring.cache.type= caffeine #  使用 Caffeine 缓存
spring.cache.caffeine.spec=maximumSize=1000,expireAfterAccess=60s #  配置 Caffeine 缓存的参数

五、缓存更新与失效

5.1 使用 @CachePut 更新缓存

  @CachePut 注解用于在更新数据后同步更新缓存。

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    @CachePut(value = "users", key = "#user.id")
    public User updateUser(User user) {
        userRepository.save(user);
        return user;
    }
}

5.2 使用 @CacheEvict 清除缓存

  @CacheEvict 注解用于清除缓存中的数据。

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    @CacheEvict(value = "users", key = "#id")
    public void deleteUser(Long id) {
        userRepository.deleteById(id);
    }
}

5.3 全部清除缓存

  有时我们需要清除整个缓存,可以使用 allEntries 属性。

@CacheEvict(value = "users", allEntries = true)
public void clearUserCache() {
    // 清除所有用户缓存
}

六、缓存管理器配置

6.1 使用 Caffeine 作为缓存管理器

  Caffeine 是一个高性能的本地缓存库,适用于单机应用。

spring:
  cache:
    type: caffeine
    caffeine:
      spec: maximumSize=500,expireAfterAccess=60s

6.2 使用 Redis 作为缓存管理器

  Redis 是一个流行的分布式缓存系统,适用于集群环境。

spring:
  cache:
    type: redis
  redis:
    host: localhost
    port: 6379

6.3 自定义缓存管理器

  有时我们需要自定义缓存管理器,可以通过实现 CacheManager 接口来完成

@Configuration
@EnableCaching
public class CacheConfig {

    @Bean
    public CacheManager cacheManager() {
        SimpleCacheManager cacheManager = new SimpleCacheManager();
        List<Cache> caches = new ArrayList<>();
        caches.add(new ConcurrentMapCache("users"));
        caches.add(new ConcurrentMapCache("configs"));
        cacheManager.setCaches(caches);
        return cacheManager;
    }
}

七、最佳实践与注意事项

7.1 合理选择缓存策略

  • 本地缓存 vs 分布式缓存:根据应用的部署方式选择合适的缓存策略。单机应用可以选择本地缓存(如Caffeine),集群应用应选择分布式缓存(如Redis)。
  • 缓存过期时间:合理设置缓存的过期时间,避免缓存数据过期导致的脏读问题。

7.2 缓存一致性

  • 避免缓存穿透(缓存和数据库中都没有的数据,可用户还是源源不断的发起请求,导致每次请求都会到数据库,从而压垮数据库): 可以使用布隆过滤器或者缓存空对象来避免缓存穿透。
  • 避免缓存击穿(Redis中一个热点key在失效的同时,大量的请求过来,从而会全部到达数据库,压垮数据库): 可以使用互斥锁或者将缓存过期时间设置为随机值来避免缓存击穿。
  • 注意缓存雪崩(Redis中缓存的数据大面积同时失效,或者Redis宕机,从而会导致大量请求直接到数据库,压垮数据库): 可以使用多级缓存来避免缓存雪崩,建议采用随机过期时间或分批失效策略。

7.3 缓存监控

  • 监控缓存命中率:定期监控缓存的命中率,评估缓存的效果。
  • 日志记录:记录缓存操作的日志,便于排查问题。

http://www.kler.cn/a/543591.html

相关文章:

  • [概率论] 随机变量
  • c#中“事件-event”的经典示例与理解
  • 【图片合并转换PDF】如何将每个文件夹下的图片转化成PDF并合并成一个文件?下面基于C++的方式教你实现
  • 高等代数笔记—线性变换
  • doris集群
  • 开源机器人+具身智能 解决方案+AI
  • 迅为RK3568开发板篇OpenHarmony实操HDF驱动配置LED-编译源码
  • Go 语言编程指南
  • Python调用C++动态库详细步骤(附源码)
  • 螺旋折线(蓝桥杯18G)
  • 第七篇:时序逻辑的“时间折叠”——VCU状态机与换电控制算法
  • 网络安全威胁是什么
  • 2025年2月12日笔记
  • 2025年SEO自动优化工具
  • Java 大视界 -- 人工智能驱动下 Java 大数据的技术革新与应用突破(83)
  • UE求职Demo开发日志#29 继续流程实现
  • conda介绍及常用命令举例
  • STM32外设分类--最小系统引脚和GPIO引脚
  • webpack配置之---output.path
  • DeepSeek 深度解析:引领 SEO 与数据分析新时代的智能工具
  • 国产ARM处理器工控机如何助力企业实现自主可控?
  • vue2 definecomponent is not defined
  • 如何准备软考高级系统分析师考试
  • 蓝桥杯(B组)-每日一题(求最大公约数最小公倍数)
  • 用easyExcel如何实现?
  • 青少年编程与数学 02-009 Django 5 Web 编程 08课题、数据库操作