- 添加依赖
在 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 {
@Bean
public 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>() {
@Override
public 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 {
@Autowired
private 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 {
@Autowired
private 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 {
@Bean
public 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 {
@Autowired
private LoadingCache<String, User> userCache;
@Autowired
private UserRepository userRepository; // Spring Data JPA
@Bean
public LoadingCache<String, User> userCache() {
return CacheBuilder.newBuilder()
.maximumSize(1000)
.expireAfterWrite(1, TimeUnit.HOURS)
.build(new CacheLoader<String, User>() {
@Override
public 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 方法中处理空值(如返回默认值或抛异常)。