1. 概述

Caffeine 是目前 Java 领域性能最强的本地缓存库之一,基于 Java 8 构建,集成了最佳实践的缓存策略(如 W-TinyLFU),在命中率和吞吐量上都显著优于传统的 Guava Cache。

本文将介绍如何在 Spring Boot 项目中集成 Caffeine,实现高效的方法级缓存。✅

2. 依赖配置

要使用 Caffeine + Spring Boot 的缓存能力,需引入两个核心依赖:

  • spring-boot-starter-cache:Spring Boot 提供的缓存抽象启动器
  • caffeine:Caffeine 缓存库本身

Maven 配置如下:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-cache</artifactId>
    </dependency>
    <dependency>
        <groupId>com.github.ben-manes.caffeine</groupId>
        <artifactId>caffeine</artifactId>
    </dependency>
</dependencies>

⚠️ 注意:无需手动引入 spring-contextcache-api,这些已被 starter 自动包含。

3. 缓存配置

Spring 的缓存机制是基于抽象的,要使用 Caffeine,需提供两个核心 Bean:

3.1 配置 Caffeine 实例

通过 @Bean 定义一个 Caffeine 实例,用于设置缓存行为,例如:

  • 写入后过期时间
  • 最大缓存条目数
  • 是否开启统计(用于监控)
@Bean
public Caffeine caffeineConfig() {
    return Caffeine.newBuilder()
                   .expireAfterWrite(60, TimeUnit.MINUTES)
                   .maximumSize(500)
                   .recordStats(); // 启用统计
}

recordStats() 是调试和监控的关键,开启后可通过 Cache.stats() 获取命中率、请求总数等指标。

3.2 配置 CacheManager

Spring 通过 CacheManager 管理缓存实例。Caffeine 提供了 CaffeineCacheManager 实现类:

@Bean
public CacheManager cacheManager(Caffeine caffeine) {
    CaffeineCacheManager cacheManager = new CaffeineCacheManager();
    cacheManager.setCaffeine(caffeine);
    return cacheManager;
}

⚠️ 注意:CaffeineCacheManager 默认会为每一个 @Cacheable 中的 value 创建一个独立缓存实例,无需额外配置。

3.3 启用缓存支持

在任意 @Configuration 类上添加 @EnableCaching 注解,开启 Spring 的缓存功能:

@Configuration
@EnableCaching
public class CacheConfig {
    // 配置 Bean 方法...
}

✅ 这一步是必须的,否则 @Cacheable 等注解不会生效。

4. 使用示例

配置完成后,即可在服务中使用缓存注解。

4.1 基础用法:@Cacheable

最常见的方式是在服务方法上使用 @Cacheable,自动缓存方法返回值。

@Service
public class AddressService {

    @Cacheable("address_cache")
    public AddressDTO getAddress(long customerId) {
        System.out.println("查询数据库: " + customerId);
        // 模拟 DB 查询
        return new AddressDTO("北京市朝阳区", "100001");
    }
}

✅ 第一次调用会执行方法体并缓存结果,后续相同参数调用将直接从缓存返回,不再执行方法。

4.2 自定义缓存名与键

默认情况下:

  • 缓存名:使用类名 + 方法名
  • 缓存键:方法参数(若只有一个参数,则直接用该参数值)

可通过注解参数显式指定:

@Service
public class AddressService {

    @Cacheable(value = "address_cache", key = "#customerId")
    public AddressDTO getAddress(long customerId) {
        System.out.println("查询数据库: " + customerId);
        return new AddressDTO("北京市朝阳区", "100001");
    }
}
  • value:指定缓存名称,对应 CaffeineCacheManager 中的缓存实例
  • key:使用 SpEL 表达式定义缓存键 ❗

✅ 推荐始终显式指定 value,避免命名冲突和后期维护困难。

4.3 多参数方法的缓存键

当方法有多个参数时,需明确指定 key,否则可能因参数顺序或类型导致缓存错乱:

@Cacheable(value = "user_order", key = "#userId + '_' + #orderId")
public OrderDTO getOrder(long userId, long orderId) {
    // 查询逻辑
}

✅ 使用 SpEL 拼接复合键是常见做法,清晰且可控。

4.4 手动操作缓存

除了注解方式,也可直接注入 CacheManager 进行编程式缓存操作:

@Service
public class AddressService {

    @Autowired
    private CacheManager cacheManager;

    public AddressDTO getAddress(long customerId) {
        Cache cache = cacheManager.getCache("address_cache");
        
        // 先尝试从缓存获取
        Cache.ValueWrapper wrapper = cache.get(customerId);
        if (wrapper != null) {
            return (AddressDTO) wrapper.get();
        }

        // 缓存未命中,查数据库
        AddressDTO address = queryFromDB(customerId);
        cache.put(customerId, address); // 手动放入缓存
        return address;
    }

    private AddressDTO queryFromDB(long customerId) {
        System.out.println("查询数据库: " + customerId);
        return new AddressDTO("北京市朝阳区", "100001");
    }
}

⚠️ 手动方式更灵活,但需注意缓存一致性,建议仅在复杂场景下使用。

5. 结论

通过本文,我们完成了:

  • ✅ 引入 Caffeine 依赖
  • ✅ 配置 CaffeineCaffeineCacheManager Bean
  • ✅ 使用 @EnableCaching 启用缓存
  • ✅ 实践 @Cacheable 注解和手动缓存操作

Caffeine + Spring Boot 的组合简单粗暴,性能拉满,是本地缓存的首选方案。尤其适合高频读、低频写的场景,比如配置缓存、用户信息缓存等。

所有示例代码已托管至 GitHub:

👉 https://github.com/eugenp/tutorials/tree/master/spring-boot-modules/spring-boot-libraries

踩坑提醒:
❌ 忘记加 @EnableCaching —— 注解不生效
❌ 缓存 key 冲突 —— 多方法共用同一缓存名时未区分 key
✅ 建议结合 Micrometer + Prometheus 监控缓存命中率,及时发现问题。


原始标题:Spring Boot and Caffeine Cache