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;
}
}
七、最佳实践
-
合理设置缓存大小和过期时间
根据业务需求配置
maximumSize
和expireAfterWrite
,避免内存溢出。 -
使用 SpEL 定义动态缓存键
例如
key = "#userId + '_' + #version"
,确保缓存键唯一。 -
避免缓存雪崩
为缓存设置随机过期时间,或结合 Redis 等分布式缓存。
-
监控缓存命中率
通过
CacheStats
和 Actuator 监控缓存性能,及时优化配置。 -
结合异步加载
对耗时操作使用
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)或
@Cacheable
的unless
条件。
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 等结合使用,形成完整的缓存架构。