通过 Caffeine 和 Spring Cache 的集成,实现高性能的本地缓存

简介

Spring @Cacheable 注解

@Cacheable 是什么

@Cacheable 是 Spring Framework 提供的一个注解,用于方法级别的缓存管理。当一个被标注为 @Cacheable 的方法被调用时,它会先检查缓存中是否有对应的结果。如果有,则直接返回缓存结果,而不会执行方法逻辑。如果没有,则执行方法逻辑并将结果存入缓存,以便下次调用时直接使用缓存值。


作用

  1. 提升性能:避免重复调用耗时的计算或 I/O 操作(如数据库查询或远程调用)。
  2. 减少资源消耗:减少系统对外部依赖(如数据库、API)的访问频率。
  3. 简单易用:通过注解配置,无需显式管理缓存逻辑。

参数解析

java 复制代码
@Cacheable(
    cacheNames = ..., // 缓存的名字(可理解为缓存的分类)
    key = ...,        // 缓存的键(默认使用方法参数作为键)
    unless = ...,     // 条件表达式,返回 true 时不会缓存
    condition = ...   // 条件表达式,返回 true 时才缓存
)
  • cacheNames: 指定缓存名称,用于区分不同的缓存空间。
  • key: 自定义缓存键的生成规则,支持 SpEL(Spring Expression Language)。
  • unless: 返回结果满足条件时,不存入缓存。
  • condition: 满足条件时,才会存入缓存。
  • sync : 如果为 true,则多个线程同时访问会先锁定,确保只执行一次计算。

Caffeine 本地缓存

Caffeine 是一个高性能、本地缓存库,提供类似于 Guava Cache 的功能,并且性能更优。想了解跟多可以自行百度,这里不做过多介绍哈(感谢支持)

Caffeine 和 Spring Cache 实现本地缓存

1. 添加 Maven 依赖

pom.xml 中添加以下依赖:

xml 复制代码
<dependencies>
    <!-- Spring Cache Starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-cache</artifactId>
    </dependency>
    <!-- Caffeine Cache -->
    <dependency>
        <groupId>com.github.ben-manes.caffeine</groupId>
        <artifactId>caffeine</artifactId>
        <version>3.1.7</version> <!-- 使用最新稳定版本 -->
    </dependency>
</dependencies>

2.配置类 LocalCacheConfig

java 复制代码
import com.github.benmanes.caffeine.cache.Caffeine;
import com.atom.utils.CollectionUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.caffeine.CaffeineCache;
import org.springframework.cache.support.SimpleCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.TimeUnit;

@Configuration
public class LocalCacheConfig {

    public final static String PRODUCT_CACHE = "PRODUCT_CACHE";

    @Bean
    public Cache flashRedisFallbackCache(CacheManager cacheManager) {
        CaffeineCache localCache = new CaffeineCache(PRODUCT_CACHE, Caffeine.newBuilder()
                .recordStats()
                .expireAfterWrite(300, TimeUnit.SECONDS)
                .initialCapacity(10000)
                .maximumSize(10000).build());
        reInitCacheManager(cacheManager, localCache);
        return localCache;
    }

    /**
     * SimpleCacheManager 中的缓存实例是初始化时一次性加载的,需要重新初始化一下CacheManager,将自定义cache池加入CacheManager
     */
    private void reInitCacheManager(CacheManager cacheManager, Cache cache) {
        SimpleCacheManager simpleCacheManager = (SimpleCacheManager) cacheManager;
        Collection<String> cacheNames = simpleCacheManager.getCacheNames();
        ArrayList<Cache> caches = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(cacheNames)) {
            cacheNames.forEach(cacheName -> caches.add(simpleCacheManager.getCache(cacheName)));
        }
        caches.add(cache);
        ((SimpleCacheManager) cacheManager).setCaches(caches);
        ((SimpleCacheManager) cacheManager).initializeCaches();
    }
}

3. 在业务方法中使用缓存注解

使用 Spring 提供的缓存注解(如 @Cacheable@CachePut@CacheEvict)来实现缓存的读写操作。

示例 1: 使用 @Cacheable

java 复制代码
@Service
public class ProductService {

    @Cacheable(cacheNames = "PRODUCT_CACHE", key = "'product:' + #productId")
    public ProductDTO getProductById(String productId) {
        // 模拟查询数据库
        return productRepository.findProductById(productId);
    }
}

示例 2: 更新缓存 (@CachePut)

java 复制代码
@CachePut(cacheNames = "PRODUCT_CACHE", key = "'product:' + #productId.id")
public ProductDTO updateProduct(ProductDTO product) {
    productRepository.updateProduct(product);
    return product;
}

示例 3: 清除缓存 (@CacheEvict)

java 复制代码
@CacheEvict(cacheNames = "PRODUCT_CACHE", key = "'product:' + #productId")
public void deleteProduct(String productId) {
    productRepository.deleteProductById(productId);
}

4. 验证缓存是否生效

验证缓存时,可以通过以下方式检查:

  1. 日志输出 :启用 Spring Cache 的 debug 日志,查看缓存操作的记录。

    application.yml 中添加:

    yaml 复制代码
    logging:
      level:
        org.springframework.cache: DEBUG
  2. 统计信息 :通过 Caffeine.newBuilder().recordStats() 启用统计信息,并使用以下代码查看缓存命中率等:

    java 复制代码
    CaffeineCache storeCache = (CaffeineCache) cacheManager.getCache("STORE_CACHE");
    System.out.println(storeCache.getNativeCache().stats());

总结

通过 Caffeine 和 Spring Cache 的集成简单易用,通过注解配置,无需显式管理缓存逻辑。在调用数据的方法上加可以减少重复调用问题,降低被调用方的压力,也可结合 Redis 快速实现多级缓存,解决热点数据问题。更多妙用,开发者可以继续探索,感谢支持,下期再见👋

相关推荐
考虑考虑1 天前
Jpa使用union all
java·spring boot·后端
用户3721574261351 天前
Java 实现 Excel 与 TXT 文本高效互转
java
浮游本尊1 天前
Java学习第22天 - 云原生与容器化
java
渣哥1 天前
原来 Java 里线程安全集合有这么多种
java
间彧1 天前
Spring Boot集成Spring Security完整指南
java
间彧1 天前
Spring Secutiy基本原理及工作流程
java
Java水解1 天前
JAVA经典面试题附答案(持续更新版)
java·后端·面试
洛小豆1 天前
在Java中,Integer.parseInt和Integer.valueOf有什么区别
java·后端·面试
前端小张同学1 天前
服务器上如何搭建jenkins 服务CI/CD😎😎
java·后端
ytadpole1 天前
Spring Cloud Gateway:一次不规范 URL 引发的路由转发404问题排查
java·后端