1. 程式人生 > 其它 >Spring Cache + Caffeine實現本地快取

Spring Cache + Caffeine實現本地快取

Caffeine簡介

Caffeine是一個高效能,高命中率,低記憶體佔用,near optimal 的本地快取,簡單來說它是 Guava Cache 的優化加強版

依賴

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
   <groupId>com.github.ben-manes.caffeine</groupId>
   <artifactId>caffeine</artifactId>
</dependency>

開啟快取

@EnableCaching註解開啟使用快取管理功能

@SpringBootApplication
@EnableCaching
public class Application {

   public static void main(String[] args) {
       SpringApplication.run(Application.class, args);
  }

}

注入

方式一

  1. 新建一個列舉類

public enum Caches {
   CACHE_ACCESS_TOKEN(10, 7200);

   /** 最大數量 */
   private Integer maxSize;

   /** 過期時間 秒 */
   private Integer ttl;

   Caches() {
  }

   Caches(Integer maxSize, Integer ttl) {
       this.maxSize = maxSize;
       this.ttl = ttl;
  }

   public Integer getMaxSize() {
       return maxSize;
  }

   public Integer getTtl() {
       return ttl;
  }

}
  1. 注入到IOC容器

    /**
    * 本地快取
    * @return
    */
   @Bean
   @Primary
   public CacheManager cacheManager() {
       SimpleCacheManager simpleCacheManager = new SimpleCacheManager();

       ArrayList<CaffeineCache> caffeineCaches = new ArrayList<>();

       for (Caches c : Caches.values()) {
           caffeineCaches.add(new CaffeineCache(c.name(),
                           Caffeine.newBuilder()
                                  .recordStats()
                                  .expireAfterWrite(c.getTtl(), TimeUnit.SECONDS)
                                  .maximumSize(c.getMaxSize())
                                  .build()
                  )
          );
      }

       simpleCacheManager.setCaches(caffeineCaches);
       return simpleCacheManager;

  }

方式二

@Bean
@Primary
public CacheManager cacheManager() {

   CaffeineCacheManager caffeineCacheManager = new CaffeineCacheManager();
   Caffeine<Object, Object> caffeine = Caffeine.newBuilder().expireAfterWrite(60, TimeUnit.MINUTES);
   caffeineCacheManager.setCaffeine(caffeine);
   return caffeineCacheManager;

}

使用

可以使用spring提供的@Cacheable、@CachePut、@CacheEvict等註解來方便的使用caffeine快取

@Cacheable(cacheNames = "CACHE_ACCESS_TOKEN", key = "#root.methodName")
public String getAccessToken(String corpid, String corpsecret) {

  //todo something...
  return "";

}

問題

使用@Cacheable快取不起作用

失效場景

  • 在私有方法上加快取

  • 類內部方法呼叫加快取

失效原因

Spring cache 的實現原理是基於 AOP 的動態代理實現的:即都在方法呼叫前後去獲取方法的名稱、引數、返回值,然後根據方法名稱、引數生成快取的key(自定義的key例外),進行快取。

AOP 不支援對 private 私有方法的攔截,所以也就不支援私有方法上的 Spring Cache 註解。

this 呼叫不是代理物件的呼叫, 所以 AOP 失效,註解失效。

解決辦法

  1. 方法用 public 限定符修飾;

  2. 類內部方法呼叫加快取時可以用 SpringContextUtil 獲取當前 Bean ,由它來呼叫

工具類

SpringContextUtil

@Component
public class SpringContextUtil implements ApplicationContextAware {

   public static ApplicationContext applicationContext;

   public void setApplicationContext(ApplicationContext applicationContext) {
       SpringContextUtil.applicationContext = applicationContext;
  }

   public static Object getBean(String name) {
       return applicationContext.getBean(name);
  }

   public static <T> T getBean(Class<T> clazz) {
       return applicationContext.getBean(clazz);
  }

   public static <T> T getBean(String name, Class<T> clazz) {
       return applicationContext.getBean(name, clazz);
  }

   public static Boolean containsBean(String name) {
       return applicationContext.containsBean(name);
  }

   public static Boolean isSingleton(String name) {
       return applicationContext.isSingleton(name);
  }

   public static Class<? extends Object> getType(String name) {
       return applicationContext.getType(name);
  }


}