Spring Boot集成Caffeine本地缓存完整方案
以下是基于您项目架构的Caffeine本地缓存集成方案,包含依赖配置、缓存策略和使用示例,适配DDD架构设计。
一、添加依赖
在/pom.xml
中添加Caffeine依赖:
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>3.1.8</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
二、缓存配置类
创建Caffeine缓存配置类,放在基础设施层:
package cn.bugstack.config;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.TimeUnit;
@Configuration
@EnableCaching // 启用缓存功能
public class CacheConfig {
/**
* 配置Caffeine缓存管理器
*/
@Bean
public CacheManager cacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
// 默认缓存配置
cacheManager.setCaffeine(defaultCacheConfig());
return cacheManager;
}
/**
* 自定义缓存配置(多策略)
*/
@Bean
public Caffeine<Object, Object> defaultCacheConfig() {
return Caffeine.newBuilder()
.maximumSize(10_000) // 最大缓存条目
.expireAfterWrite(10, TimeUnit.MINUTES) // 写入后过期时间
.recordStats() // 启用统计功能
.evictionListener((key, value, cause) -> {
// 缓存驱逐监听器(可选)
// log.info("Cache evicted - key:{}, cause:{}", key, cause);
});
}
/**
* 商品缓存配置(特定策略)
*/
@Bean
public Caffeine<Object, Object> productCacheConfig() {
return Caffeine.newBuilder()
.maximumSize(5_000)
.expireAfterWrite(5, TimeUnit.MINUTES) // 商品缓存5分钟过期
.recordStats();
}
/**
* 用户缓存配置(特定策略)
*/
@Bean
public Caffeine<Object, Object> userCacheConfig() {
return Caffeine.newBuilder()
.maximumSize(20_000)
.expireAfterAccess(30, TimeUnit.MINUTES) // 访问后过期
.recordStats();
}
}
三、多缓存策略配置(可选)
如需为不同业务场景配置差异化缓存策略:
// 在CacheConfig中添加
@Bean
public CacheManager cacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
// 为不同缓存名称配置不同策略
Map<String, Caffeine<Object, Object>> cacheMap = new HashMap<>();
cacheMap.put("productCache", productCacheConfig());
cacheMap.put("userCache", userCacheConfig());
cacheMap.put("activityCache", activityCacheConfig());
cacheManager.setCacheSpecification(cacheMap);
return cacheManager;
}
// 活动缓存配置
public Caffeine<Object, Object> activityCacheConfig() {
return Caffeine.newBuilder()
.maximumSize(1_000)
.expireAfterWrite(1, TimeUnit.HOURS)
.recordStats();
}
四、Service层缓存使用示例
在领域服务中应用缓存:
// 文件路径: d:\develop\workspace\big-market\big-market-domain\src\main\java\cn\bugstack\domain\service\ProductService.java
package cn.bugstack.domain.service;
import cn.bugstack.domain.model.ProductInfo;
import cn.bugstack.domain.repository.ProductRepository;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class ProductService {
@Resource
private ProductRepository productRepository;
/**
* 查询商品信息(使用商品缓存策略)
*/
@Cacheable(value = "productCache", key = "#productId", unless = "#result == null")
public ProductInfo getProductById(Long productId) {
// 实际业务中可能包含复杂查询或计算
return productRepository.selectById(productId);
}
/**
* 更新商品信息(更新缓存)
*/
@CachePut(value = "productCache", key = "#product.id")
public ProductInfo updateProduct(ProductInfo product) {
return productRepository.update(product);
}
/**
* 删除商品(清除缓存)
*/
@CacheEvict(value = "productCache", key = "#productId")
public void deleteProduct(Long productId) {
productRepository.deleteById(productId);
}
/**
* 清除所有商品缓存
*/
@CacheEvict(value = "productCache", allEntries = true)
public void clearProductCache() {
// 仅用于清除缓存,无需业务逻辑
}
}
五、缓存统计与监控
- 启用统计后获取缓存指标:
@Service
public class CacheStatsService {
@Resource
private CacheManager cacheManager;
public CacheStats getProductCacheStats() {
CaffeineCache cache = (CaffeineCache) cacheManager.getCache("productCache");
return cache.getNativeCache().stats();
}
// 暴露监控端点
public Map<String, Object> getCacheMetrics() {
Map<String, Object> metrics = new HashMap<>();
CacheStats stats = getProductCacheStats();
metrics.put("hitRate", stats.hitRate());
metrics.put("missCount", stats.missCount());
metrics.put("evictionCount", stats.evictionCount());
return metrics;
}
}
- 集成Actuator监控(可选):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
# application.yml
management:
endpoints:
web:
exposure:
include: cache,health,info
六、与Redis多级缓存集成(推荐)
结合项目已有的Redis热点隔离方案,实现多级缓存:
@Service
public class ProductServiceImpl implements ProductService {
@Resource
private RedisTemplate<String, Object> redisTemplate;
@Resource
private ProductRepository productRepository;
@Override
public ProductInfo getProductById(Long productId) {
// 1. 先查本地缓存
ProductInfo product = getProductFromLocalCache(productId);
if (product != null) {
return product;
}
// 2. 再查Redis缓存
product = getProductFromRedis(productId);
if (product != null) {
// 回填本地缓存
putProductToLocalCache(product);
return product;
}
// 3. 最后查数据库
product = productRepository.selectById(productId);
if (product != null) {
// 写入Redis和本地缓存
putProductToRedis(product);
putProductToLocalCache(product);
}
return product;
}
// 本地缓存操作(使用Caffeine API直接操作)
private ProductInfo getProductFromLocalCache(Long productId) {
// 实现略
}
}
七、关键配置参数说明
参数 | 作用 | 推荐值 |
---|---|---|
maximumSize | 最大缓存条目 | 10,000-100,000(根据内存) |
expireAfterWrite | 写入后过期时间 | 5-30分钟(根据数据更新频率) |
expireAfterAccess | 访问后过期时间 | 30-120分钟(不常访问数据) |
recordStats | 启用统计 | true(便于监控调优) |
initialCapacity | 初始容量 | 100-1000(减少扩容开销) |
八、最佳实践
缓存粒度:
- 缓存完整领域对象而非零散数据
- 对大对象进行拆分缓存(如商品基本信息和详情分开)
更新策略:
- 读多写少场景用
@Cacheable
- 写频繁场景考虑Cache Aside Pattern
- 读多写少场景用
防缓存穿透:
@Cacheable(value = "productCache", key = "#productId", unless = "#result == null")
缓存预热:
@PostConstruct public void preloadCache() { List<Long> hotProductIds = Arrays.asList(1001L, 1002L, 1003L); hotProductIds.forEach(this::getProductById); }
该方案已适配您项目的DDD架构,缓存配置放在基础设施层,业务逻辑通过注解使用缓存,既保持了代码整洁又实现了高性能本地缓存。可根据实际业务需求调整缓存策略参数。