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 等结合使用,形成完整的缓存架构。


十、参考资料

相关推荐
为java加瓦3 小时前
Rust 的类型自动解引用:隐藏在人体工学设计中的魔法
java·服务器·rust
SimonKing3 小时前
分布式日志排查太头疼?TLog 让你一眼看穿请求链路!
java·后端·程序员
消失的旧时光-19433 小时前
Kotlin 判空写法对比与最佳实践
android·java·kotlin
小许学java3 小时前
Spring AI快速入门以及项目的创建
java·开发语言·人工智能·后端·spring·ai编程·spring ai
一叶飘零_sweeeet3 小时前
从 “死锁“ 到 “解耦“:重构中间服务破解 Java 循环依赖难题
java·循环依赖
Terio_my3 小时前
Spring Boot Web环境测试配置
spring boot
*长铗归来*3 小时前
ASP.NET Core Web API 中控制器操作的返回类型及Swagger
后端·c#·asp.net·.netcore
靓仔建3 小时前
Asp.net core用Swashbuckle.AspNetCore库出现错误信息:No operations defined in spec!
后端·asp.net·swagger