Guava Cache 本地项目缓存

发布于:2025-06-28 ⋅ 阅读:(15) ⋅ 点赞:(0)
  1. 添加依赖

在 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'

  1. 创建 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: 定义未命中时的加载逻辑(例如从数据库查询)。

  1. 在 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(): 查看缓存的命中率、加载时间等统计信息。

  1. 在 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: 查看缓存统计信息(输出到日志)。

  1. 测试缓存功能

运行 Spring Boot 应用后,可以通过以下方式测试:

  1. 启动应用: 确保 Spring Boot 应用已启动(默认端口 8080)。
  2. 测试接口: 使用 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
  3. 验证缓存行为:
    • 检查缓存是否按配置的过期时间(如 10 分钟)失效。
    • 使用 guavaCache.stats() 查看命中率等信息。

  1. 集成 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。

  1. 项目实践中的注意事项

  2. 缓存淘汰策略:

    • 根据业务需求调整 maximumSize 和过期时间。
    • 如果数据量较大,考虑 expireAfterAccess 以避免内存溢出。
  3. 线程安全:

    • Guava Cache 是线程安全的,无需额外同步。
  4. 监控缓存性能:

    • 使用 cache.stats() 监控命中率和加载时间,优化缓存配置。
  5. 异常处理:

    • 在 CacheLoader.load 中处理数据源异常,避免缓存失败影响业务。
  6. 分布式场景的局限:

    • Guava Cache 是本地缓存,数据不共享。如果需要分布式缓存,考虑 Redis 或 J2Cache。
  7. 日志和调试:

    • 开启 Guava 的统计功能,记录缓存命中率、加载时间等,分析性能瓶颈。

  1. 示例项目结构
src
├── main
│   ├── java
│   │   ├── com.example.demo
│   │   │   ├── config
│   │   │   │   ├── GuavaCacheConfig.java
│   │   │   │   ├── SpringCacheConfig.java (可选)
│   │   │   ├── controller
│   │   │   │   ├── CacheController.java
│   │   │   ├── service
│   │   │   │   ├── DataService.java
│   │   │   ├── DemoApplication.java
│   ├── resources
│   │   ├── application.properties

  1. 扩展:与数据库结合的实践

假设你有一个用户查询场景,需要缓存数据库查询结果:

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 方法从数据库查询。
  • 缓存命中时直接返回,减少数据库访问。

  1. 性能优化建议
  • 调整缓存大小: 根据 JVM 内存和数据规模设置 maximumSize。
  • 异步加载: 使用 CacheBuilder.buildAsync 实现异步加载,提升性能。
  • 监控命中率: 定期检查 cache.stats().hitRate(),优化缓存策略。
  • 避免缓存穿透: 在 load 方法中处理空值(如返回默认值或抛异常)。

网站公告

今日签到

点亮在社区的每一天
去签到