Spring Boot 中使用 Caffeine 缓存详解与案例

Spring Boot 中使用 Caffeine 缓存详解与案例


一、概述

Caffeine 是一个高性能的 Java 本地缓存库,适用于 Spring Boot 应用中需要快速响应、低延迟的缓存场景。Spring Boot 提供了对 Caffeine 的开箱即用支持,通过 @Cacheable@CachePut@CacheEvict 等注解,开发者可以轻松集成 Caffeine 缓存功能。


二、依赖配置

2.1 添加 Maven 依赖

xml 复制代码
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>3.1.8</version>
</dependency>

2.2 启用缓存支持

在 Spring Boot 主类或配置类上添加 @EnableCaching 注解:

java 复制代码
@SpringBootApplication
@EnableCaching
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

三、Caffeine 缓存配置

3.1 基础配置(application.yml)

yaml 复制代码
spring:
cache:
type: caffeine
caffeine:
spec: maximumSize=1000, expireAfterWrite=10s

3.2 自定义配置类

通过 Java 配置类覆盖默认配置:

java 复制代码
@Configuration
public class CaffeineConfig {

@Bean
public CacheManager cacheManager() {
return Caffeine.newBuilder()
.maximumSize(1000)
.expireAfterWrite(10, TimeUnit.SECONDS)
.recordStats()
.build();
}
}

四、核心注解与使用

4.1 @Cacheable:读取缓存

java 复制代码
@Service
public class UserService {

@Cacheable(value = "users", key = "#userId")
public User getUser(String userId) {
// 模拟从数据库查询
return fetchFromDB(userId);
}

private User fetchFromDB(String userId) {
return new User(userId, "User_" + userId);
}
}
参数说明:
  • value:缓存名称(对应 Caffeine 的 Cache 实例)。
  • key:SpEL 表达式,定义缓存键(如 #userId 表示方法参数 userId)。

4.2 @CachePut:更新缓存

java 复制代码
@CachePut(value = "users", key = "#user.id")
public User updateUser(User user) {
// 模拟更新数据库
return updateInDB(user);
}

4.3 @CacheEvict:清除缓存

java 复制代码
@CacheEvict(value = "users", key = "#userId")
public void deleteUser(String userId) {
// 模拟删除数据库
}

五、高级配置与优化

5.1 多缓存策略配置

通过 Caffeine 构建多个缓存实例:

java 复制代码
@Configuration
public class MultiCacheConfig {

@Bean
public CacheManager userCacheManager() {
return Caffeine.newBuilder()
.maximumSize(1000)
.expireAfterWrite(10, TimeUnit.SECONDS)
.build();
}

@Bean
public CacheManager productCacheManager() {
return Caffeine.newBuilder()
.maximumSize(500)
.expireAfterAccess(5, TimeUnit.MINUTES)
.build();
}
}
使用多缓存:
java 复制代码
@Cacheable(value = "users", cacheManager = "userCacheManager", key = "#userId")
public User getUser(String userId) { ... }

5.2 异步加载缓存

通过 AsyncLoadingCache 实现异步加载:

java 复制代码
@Service
public class AsyncService {

private final AsyncLoadingCache<String, String> asyncCache;

public AsyncService() {
this.asyncCache = Caffeine.newBuilder()
.maximumSize(100)
.buildAsync(key -> CompletableFuture.supplyAsync(() -> fetchFromDB(key)));
}

public CompletableFuture<String> getData(String key) {
return asyncCache.get(key);
}

private String fetchFromDB(String key) {
// 模拟耗时操作
return "Value_" + key;
}
}

5.3 缓存统计与监控

启用统计功能并查看缓存命中率:

java 复制代码
@Cacheable(value = "users", key = "#userId")
public User getUser(String userId) {
return fetchFromDB(userId);
}

// 获取统计信息
Cache cache = cacheManager.getCache("users", String.class, User.class);
CacheStats stats = cache.stats();
System.out.println("命中率: " + stats.hitRate());

结合 Spring Boot Actuator 查看缓存统计:

yaml 复制代码
management:
endpoints:
web:
exposure:
include: "*"

访问 http://localhost:8080/actuator/cache 查看缓存详情。


六、使用案例详解

6.1 用户信息缓存

场景:减少数据库查询压力
java 复制代码
@Service
public class UserService {

@Cacheable(value = "users", key = "#userId")
public User getUser(String userId) {
// 模拟数据库查询
return fetchFromDB(userId);
}

private User fetchFromDB(String userId) {
// 实际业务中从数据库或外部服务获取数据
return new User(userId, "User_" + userId);
}
}

6.2 热点数据预加载

场景:主动加载高频访问数据
java 复制代码
@Service
public class PreloadService {

@Autowired
private Cache<String, String> cache;

@PostConstruct
public void preload() {
List<String> hotKeys = Arrays.asList("key1", "key2", "key3");
hotKeys.forEach(key -> cache.put(key, fetchFromDB(key)));
}

private String fetchFromDB(String key) {
return "Value_" + key;
}
}

6.3 分布式缓存结合

场景:Caffeine 作为本地缓存,Redis 作为分布式缓存
java 复制代码
@Service
public class DistributedService {

@Autowired
private Cache<String, String> localCache;

@Autowired
private RedisTemplate<String, String> redisTemplate;

public String getData(String key) {
String value = localCache.getIfPresent(key);
if (value == null) {
value = redisTemplate.opsForValue().get(key);
if (value == null) {
value = fetchFromDB(key);
redisTemplate.opsForValue().set(key, value);
}
localCache.put(key, value);
}
return value;
}
}

七、最佳实践

  1. 合理设置缓存大小和过期时间

    根据业务需求配置 maximumSizeexpireAfterWrite,避免内存溢出。

  2. 使用 SpEL 定义动态缓存键

    例如 key = "#userId + '_' + #version",确保缓存键唯一。

  3. 避免缓存雪崩

    为缓存设置随机过期时间,或结合 Redis 等分布式缓存。

  4. 监控缓存命中率

    通过 CacheStats 和 Actuator 监控缓存性能,及时优化配置。

  5. 结合异步加载

    对耗时操作使用 AsyncLoadingCache,避免阻塞主线程。


八、常见问题与解决方案

8.1 缓存穿透(Cache Penetration)

问题 :查询不存在的数据导致频繁访问数据库。
解决方案

  • 缓存空值("")并设置短过期时间。
  • 使用布隆过滤器拦截非法请求。
java 复制代码
@Cacheable(value = "products", key = "#productId")
public Product getProduct(String productId) {
Product product = fetchFromDB(productId);
if (product == null) {
cache.put(productId, "");// 缓存空值
}
return product;
}

8.2 缓存击穿(Cache Breakdown)

问题 :热点数据过期后大量请求直接访问数据库。
解决方案

  • 使用互斥锁(Mutex Lock)或 @Cacheableunless 条件。
java 复制代码
@Cacheable(value = "hotData", key = "#key", unless = "#result == null")
public String getHotData(String key) {
return fetchFromDB(key);
}

8.3 缓存雪崩(Cache Avalanche)

问题 :大量缓存同时失效,导致数据库压力激增。
解决方案

  • 为缓存设置随机过期时间。
  • 结合 Redis 等分布式缓存分层管理。

九、总结

Spring Boot 与 Caffeine 的集成提供了高效、灵活的本地缓存解决方案,适用于高并发、低延迟的场景。通过合理配置缓存策略、使用注解简化开发,并结合监控工具优化性能,开发者可以显著提升系统吞吐量和响应速度。对于需要分布式缓存的场景,Caffeine 可作为本地缓存层与 Redis 等结合使用,形成完整的缓存架构。


十、参考资料

相关推荐
武子康14 小时前
大数据-157 Apache Kylin 全面指南:MOLAP 架构、Hive/Kafka 实战与实时 OLAP 落地
大数据·后端·apache kylin
YQ_ZJH14 小时前
Redisson 看门狗机制详解
java·redis
ssshooter14 小时前
传参优于外部变量
前端·后端·面试
那我掉的头发算什么14 小时前
【javaEE】多线程——线程安全进阶☆☆☆
java·jvm·安全·java-ee·intellij-idea
qq_225891746614 小时前
基于Python+Django餐饮评论大数据分析与智能推荐系统 毕业论文
开发语言·后端·python·信息可视化·数据分析·django
悟空CRM服务14 小时前
我用一条命令部署了完整CRM系统!
java·人工智能·开源·开源软件
组合缺一14 小时前
Solon AI 开发学习 - 1导引
java·人工智能·学习·ai·openai·solon
百***490014 小时前
基于SpringBoot和PostGIS的各省与地级市空间距离分析
java·spring boot·spring
百***266314 小时前
使用 Logback 的最佳实践:`logback.xml` 与 `logback-spring.xml` 的区别与用法
xml·spring·logback
bcbnb14 小时前
网络调试与API测试必修课 Fiddler抓包工具使用教程、代理配置与HTTPS抓包技巧全解析
后端