Guava Cache 本地项目缓存
- 添加依赖
在 Spring Boot 项目中,Guava Cache 是 Google Guava 库的一部分,因此需要添加 Guava 的依赖。
在 pom.xml(Maven)中添加以下依赖:
xml
<dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>33.3.1-jre</version>
</dependency>
注意: 请根据需要选择最新版本,检查 Maven Central 获取最新版本号。
如果使用 Gradle,可以在 build.gradle 中添加:
gradle
implementation 'com.google.guava:guava:33.3.1-jre'
- 创建 Guava Cache 配置
Guava Cache 需要手动配置缓存实例,包括缓存大小、过期策略等。以下是一个简单的配置类,展示如何在 Spring Boot 中定义 Guava Cache。
java
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.concurrent.TimeUnit;@Configuration
public class GuavaCacheConfig {@Beanpublic LoadingCache<String, String> guavaCache() {return CacheBuilder.newBuilder().maximumSize(1000) // 最大缓存条目数.expireAfterWrite(10, TimeUnit.MINUTES) // 写入后10分钟过期.expireAfterAccess(5, TimeUnit.MINUTES) // 访问后5分钟过期.recordStats() // 开启统计信息(如命中率).build(new CacheLoader<String, String>() {@Overridepublic String load(String key) throws Exception {// 当缓存未命中时,调用此方法加载数据return loadDataFromSource(key);}});}// 模拟从数据源加载数据private String loadDataFromSource(String key) {// 实际场景中,这里可能从数据库或外部服务加载数据return "Value for " + key;}
}
说明:
- maximumSize: 设置缓存最大条目数,超出时按 LRU(最近最少使用)策略淘汰。
- expireAfterWrite: 写入后指定时间过期。
- expireAfterAccess: 最后访问后指定时间过期。
- recordStats: 开启统计功能,可用于监控缓存命中率。
- CacheLoader: 定义未命中时的加载逻辑(例如从数据库查询)。
- 在 Service 层中使用 Guava Cache
将配置好的 Guava Cache 注入到业务逻辑中,并在需要的地方使用缓存。
以下是一个简单的 Service 示例:
java
import com.google.common.cache.LoadingCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class DataService {@Autowiredprivate LoadingCache<String, String> guavaCache;public String getData(String key) {try {// 从缓存中获取数据,如果未命中则通过 CacheLoader 加载return guavaCache.get(key);} catch (Exception e) {// 处理异常return "Error retrieving data for key: " + key;}}public void putData(String key, String value) {// 手动放入缓存guavaCache.put(key, value);}public void invalidate(String key) {// 移除缓存中的键guavaCache.invalidate(key);}public void printCacheStats() {// 打印缓存统计信息System.out.println("Cache Stats: " + guavaCache.stats());}
}
说明:
- guavaCache.get(key): 尝试从缓存中获取数据,未命中时会触发 CacheLoader 的 load 方法。
- guavaCache.put(key, value): 手动将数据放入缓存。
- guavaCache.invalidate(key): 移除指定键的缓存。
- guavaCache.stats(): 查看缓存的命中率、加载时间等统计信息。
- 在 Controller 中调用
创建一个简单的 REST Controller 来测试缓存功能。
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;@RestController
@RequestMapping("/cache")
public class CacheController {@Autowiredprivate DataService dataService;@GetMapping("/get/{key}")public String getData(@PathVariable String key) {return dataService.getData(key);}@PostMapping("/put")public String putData(@RequestParam String key, @RequestParam String value) {dataService.putData(key, value);return "Data stored in cache";}@DeleteMapping("/invalidate/{key}")public String invalidate(@PathVariable String key) {dataService.invalidate(key);return "Cache invalidated for key: " + key;}@GetMapping("/stats")public String getStats() {dataService.printCacheStats();return "Check server logs for cache stats";}
}
说明:
- /get/{key}: 获取缓存数据。
- /put: 手动存入缓存。
- /invalidate/{key}: 移除缓存。
- /stats: 查看缓存统计信息(输出到日志)。
- 测试缓存功能
运行 Spring Boot 应用后,可以通过以下方式测试:
- 启动应用: 确保 Spring Boot 应用已启动(默认端口 8080)。
- 测试接口: 使用 curl 或 Postman 进行测试:
- 获取数据:curl http://localhost:8080/cache/get/key1
- 第一次请求会触发 loadDataFromSource,后续请求直接从缓存返回。
- 存入数据:curl -X POST “http://localhost:8080/cache/put?key=key1&value=Hello”
- 清除缓存:curl -X DELETE http://localhost:8080/cache/invalidate/key1
- 查看统计:curl http://localhost:8080/cache/stats
- 获取数据:curl http://localhost:8080/cache/get/key1
- 验证缓存行为:
- 检查缓存是否按配置的过期时间(如 10 分钟)失效。
- 使用 guavaCache.stats() 查看命中率等信息。
- 集成 Spring Cache(可选)
如果希望结合 Spring 的缓存抽象(@Cacheable 等注解),可以创建一个自定义 CacheManager 来使用 Guava Cache。
java
import com.google.common.cache.CacheBuilder;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.guava.GuavaCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.concurrent.TimeUnit;@Configuration
@EnableCaching
public class SpringCacheConfig {@Beanpublic CacheManager cacheManager() {GuavaCacheManager cacheManager = new GuavaCacheManager("myCache");cacheManager.setCacheBuilder(CacheBuilder.newBuilder().maximumSize(1000).expireAfterWrite(10, TimeUnit.MINUTES));return cacheManager;}
}
然后在 Service 中使用注解:
java
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;@Service
public class DataService {@Cacheable(value = "myCache", key = "#key")public String getData(String key) {// 模拟从数据库加载return "Value for " + key;}
}
说明:
- @Cacheable: 自动缓存方法结果,缓存名为 myCache。
- Spring Cache 抽象简化了开发,但需要额外配置 CacheManager。
-
项目实践中的注意事项
-
缓存淘汰策略:
- 根据业务需求调整 maximumSize 和过期时间。
- 如果数据量较大,考虑 expireAfterAccess 以避免内存溢出。
-
线程安全:
- Guava Cache 是线程安全的,无需额外同步。
-
监控缓存性能:
- 使用 cache.stats() 监控命中率和加载时间,优化缓存配置。
-
异常处理:
- 在 CacheLoader.load 中处理数据源异常,避免缓存失败影响业务。
-
分布式场景的局限:
- Guava Cache 是本地缓存,数据不共享。如果需要分布式缓存,考虑 Redis 或 J2Cache。
-
日志和调试:
- 开启 Guava 的统计功能,记录缓存命中率、加载时间等,分析性能瓶颈。
- 示例项目结构
src
├── main
│ ├── java
│ │ ├── com.example.demo
│ │ │ ├── config
│ │ │ │ ├── GuavaCacheConfig.java
│ │ │ │ ├── SpringCacheConfig.java (可选)
│ │ │ ├── controller
│ │ │ │ ├── CacheController.java
│ │ │ ├── service
│ │ │ │ ├── DataService.java
│ │ │ ├── DemoApplication.java
│ ├── resources
│ │ ├── application.properties
- 扩展:与数据库结合的实践
假设你有一个用户查询场景,需要缓存数据库查询结果:
java
@Service
public class UserService {@Autowiredprivate LoadingCache<String, User> userCache;@Autowiredprivate UserRepository userRepository; // Spring Data JPA@Beanpublic LoadingCache<String, User> userCache() {return CacheBuilder.newBuilder().maximumSize(1000).expireAfterWrite(1, TimeUnit.HOURS).build(new CacheLoader<String, User>() {@Overridepublic User load(String userId) throws Exception {return userRepository.findById(userId).orElseThrow(() -> new RuntimeException("User not found"));}});}public User getUser(String userId) {try {return userCache.get(userId);} catch (Exception e) {throw new RuntimeException("Failed to get user", e);}}
}
说明:
- 缓存 User 对象,load 方法从数据库查询。
- 缓存命中时直接返回,减少数据库访问。
- 性能优化建议
- 调整缓存大小: 根据 JVM 内存和数据规模设置 maximumSize。
- 异步加载: 使用 CacheBuilder.buildAsync 实现异步加载,提升性能。
- 监控命中率: 定期检查 cache.stats().hitRate(),优化缓存策略。
- 避免缓存穿透: 在 load 方法中处理空值(如返回默认值或抛异常)。