前言

在系统中,有些数据,访问十分频繁,往往把这些数据放入分布式缓存中,但为了减少网络传输,加快响应速度,缓存分布式缓存读压力,会把这些数据缓存到本地JVM中,大多是先取本地缓存中,再取分布式缓存中的数据,Caffeine是一个高性能Java 缓存库,使用Java8对Guava缓存重写版本,在Spring Boot 2.0中将取代Guava。

本文讲解SpringBoot缓存注解的理论和整合Caffeine的基本使用。

Caffeine简介

Caffeine是基于JAVA 1.8 Version的高性能缓存库。Caffeine提供的内存缓存使用参考Google guava的API。Caffeine是基于Google Guava Cache设计经验上改进的成果。

官方性能比较:

SpringBoot使用内存高性能缓存Caffeine详解_java

SpringBoot使用内存高性能缓存Caffeine详解_java_02

可以清楚的看到Caffeine效率明显的高于其他缓存。

Springboot 对缓存的支持

Spring Framework 支持透明地向应用程序添加缓存。从本质上讲,抽象将缓存应用于方法,从而根据缓存中可用的信息减少执行次数。缓存逻辑是透明应用的,不会对调用者造成任何干扰。

SpringBoot缓存注解相关内容

1. @Cacheable:

@Cacheable可以标记在一个方法上,也可以标记在一个类上。当标记在一个方法上时表示该方法是支持缓存的,当标记在一个类上时则表示该类所有的方法都是支持缓存的。对于一个支持缓存的方法,Spring会在其被调用后将其返回值缓存起来,以保证下次利用同样的参数来执行该方法时可以直接从缓存中获取结果,而不需要再次执行该方法。Spring在缓存方法的返回值时是以键值对进行缓存的,值就是方法的返回结果,至于键的话,Spring又支持两种策略,默认策略和自定义策略,这个稍后会进行说明。需要注意的是当一个支持缓存的方法在对象内部被调用时是不会触发缓存功能的。@Cacheable可以指定三个属性,value、key和condition。

使用案例

// key 是指传入时的参数
@Cacheable(value="users", key="#id")
public Integer find(Integer id) {
    return id;
// 表示User中的id值
@Cacheable(value="users", key="#user.id")
public User find(User user) {
     return user;
}

condition属性指定发生的条件

有的时候我们可能并不希望缓存一个方法所有的返回结果。通过condition属性可以实现这一功能。condition属性默认为空,表示将缓存所有的调用情形。其值是通过SpringEL表达式来指定的,当为true时表示进行缓存处理;当为false时表示不进行缓存处理,即每次调用该方法时该方法都会执行一次。如下示例表示只有当user的id为偶数时才会进行缓存。

 // 根据条件判断是否缓存
@Cacheable(value="users", key="#user.id", condition="#user.id%2==0")
public User find(User user) {
   return user;
}
2. CacheEvict

@CacheEvict是用来标注在需要清除缓存元素的方法或类上的。当标记在一个类上时表示其中所有的方法的执行都会触发缓存的清除操作。@CacheEvict可以指定的属性有value、key、condition、allEntries和beforeInvocation。其中value、key和condition的语义与@Cacheable对应的属性类似。即value表示清除操作是发生在哪些Cache上的(对应Cache的名称);key表示需要清除的是哪个key,如未指定则会使用默认策略生成的key;condition表示清除操作发生的条件。下面我们来介绍一下新出现的两个属性allEntries和beforeInvocation。

allEntries属性

allEntries是boolean类型,表示是否需要清除缓存中的所有元素。默认为false,表示不需要。当指定了allEntries为true时,Spring Cache将忽略指定的key。有的时候我们需要Cache一下清除所有的元素,这比一个一个清除元素更有效率。

@CacheEvict(value="user", allEntries=true)
  public void delete(Integer id) {
     System.out.println(id);
  }
3. @Caching

@Caching注解可以让我们在一个方法或者类上同时指定多个Spring Cache相关的注解。其拥有三个属性:cacheable、put和evict,分别用于指定@Cacheable、@CachePut和@CacheEvict。

@Caching(
       cacheable = @Cacheable("user"),
       evict = {
               @CacheEvict(value = "user1", key = "#id"),
               @CacheEvict(value = "user", allEntries = true)})
  public Integer find(Integer id) {
     return id;
  }
Caffeine相关知识点

Caffeine常用配置说明:

  • initialCapacity=[integer]: 初始的缓存空间大小

  • maximumSize=[long]: 缓存的最大条数

  • maximumWeight=[long]: 缓存的最大权重

  • expireAfterAccess=[duration]: 最后一次写入或访问后经过固定时间过期

  • expireAfterWrite=[duration]: 最后一次写入后经过固定时间过期

  • refreshAfterWrite=[duration]: 创建缓存或者最近一次更新缓存后经过固定的时间间隔,刷新缓存

注意点:

  • expireAfterWrite和expireAfterAccess同事存在时,以expireAfterWrite为准

  • maximumSize和maximumWeight不可以同时使用

Springboot 集成 SpringCache

缓存逻辑是透明应用的,不会对调用者造成任何干扰。只要通过 @EnableCaching 批注启用了缓存支持,Spring Boot 就会自动配置缓存基础结构。

比如:

import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;

@Component
public class MathService {

@Cacheable("piDecimals")
public int computePiDecimal(int i) {
// ...
}

}

SpringCache缓存的实现

SpringCache缓存抽象不提供实际存储,而是依赖于 org.springframework.cache.Cache 和org.springframework.cache.CacheManager 接口实现的抽象。

Springboot集成Caffeine

Caffeine 是 Java 8重写的 Guava 缓存,取代了对 Guava 的支持。如果存在 Caffeine,则会自动配置 CaffeineCacheManager(由 spring-boot-starter-cache "Starter"提供)。

pom.xml 中引入Caffeine:

<dependency>
           <groupId>com.github.ben-manes.caffeine</groupId>
           <artifactId>caffeine</artifactId>
 </dependency>
定义 CacheManager 来管理缓存

需要关注内容关于Caffeine配置了相关属性内容。

import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.caffeine.CaffeineCache;
import org.springframework.cache.support.SimpleCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import java.util.ArrayList;
import java.util.concurrent.TimeUnit;

@Configuration
@EnableCaching
public class CaffeineConfig {
   public static final int DEFAULT_MAXSIZE = 10000;
   public static final int DEFAULT_TTL = 600;
   /**
    * 定义cache名称、超时时长(秒)、最大容量
    * 每个cache缺省:10秒超时、最多缓存10000条数据,需要修改可以在构造方法的参数中指定。
    */
   public enum Caches{
       /**
        * 空域接口数据缓存策略,有效期2个小时
        */
       airspaces(7200),
       ;
       Caches() {
       }
       Caches(int ttl) {
           this.ttl = ttl;
       }
       Caches(int ttl, int maxSize) {
           this.ttl = ttl;
           this.maxSize = maxSize;
       }
       private int maxSize=DEFAULT_MAXSIZE; //最大數量
       private int ttl=DEFAULT_TTL; //过期时间(秒)
       public int getMaxSize() {
           return maxSize;
       }
       public int getTtl() {
           return ttl;
       }
   }

   /**
    * 创建基于Caffeine的Cache Manager
    * @return
    */
   @Bean
   @Primary
   public CacheManager caffeineCacheManager() {
       SimpleCacheManager cacheManager = new SimpleCacheManager();
       ArrayList<CaffeineCache> caches = new ArrayList<CaffeineCache>();
       for(Caches c : Caches.values()){
           caches.add(new CaffeineCache(c.name(),
                   Caffeine.newBuilder().recordStats()
                           .expireAfterWrite(c.getTtl(), TimeUnit.SECONDS)
                           .maximumSize(c.getMaxSize())
                           .build())
           );
       }
       cacheManager.setCaches(caches);
       return cacheManager;
   }
}

方法中调用:

@Cacheable(value="airspaces", key="#airspaceTypeEnum.type")
public JSONObject doGetAirspaceInterfaceSet(AirspaceTypeEnum airspaceTypeEnum, String date) {
 return null;
}
总结

通过配置可知,SpringCache可以说是透明的集成了Caffeine,整个集成操作也比较便捷,操作起来非常方便。

以上为全部内容。