spring-boot中配置和使用Caffeine Cache

时间:2022-06-26 20:34:35

转自:http://lib.csdn.net/article/java/65506

题外话: Spring 5中已经摘除了对Guava caching的使用,转而使用Caffeine.详见官方信息SPR-13797

本地缓存,之前一直用Guava Cache,最近spring-boot推荐使用Caffeine Cache。
主要的三种本地缓存性能对比:

简单几步,就可以在spring-boot中配置和使用Caffeine Cache:

  1. 引入依赖:

    <!-- local 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>
  2. 配置:
    有两种方法:

    • application.yml配置文件中配置:
      • 优点:简单
      • 缺点:无法针对每个cache配置不同的参数,比如过期时长、最大容量
    • 配置类中配置

      • 优点:可以针对每个cache配置不同的参数,比如过期时长、最大容量
      • 缺点:要写一点代码
    • 配置文件中直接配置:

      spring:
      cache:
      type: CAFFEINE
      cache-names:
      - getPersonById
      - name2
      caffeine:
      spec: maximumSize=500,expireAfterWrite=5s

      然后还要在主类中加上@EnableCaching注解:

      @SpringBootApplication
      @EnableScheduling
      @EnableCaching
      public class MySpringBootApplication
    • 另外一种更灵活的方法是在配置类中配置:

      package com.xjj.config;
      import java.util.ArrayList;
      import java.util.concurrent.TimeUnit;

      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 com.github.benmanes.caffeine.cache.Caffeine;

      /**
      * Cache配置類,用于缓存数据
      * @author XuJijun
      *
      */
      @Configuration
      @EnableCaching
      public class CacheConfig {

      public static final int DEFAULT_MAXSIZE = 50000;
      public static final int DEFAULT_TTL = 10;

      /**
      * 定義cache名稱、超時時長(秒)、最大容量
      * 每个cache缺省:10秒超时、最多缓存50000条数据,需要修改可以在 构造方法的参数中指定。
      */
      public enum Caches{
      getPersonById(5), //有效期5秒
      getSomething, //缺省10秒
      getOtherthing(300, 1000), //5分钟,最大容量1000
      ;

      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;
      }

      }
    • 代码中使用:
      package com.xjj.service;

      import org.slf4j.Logger;
      import org.slf4j.LoggerFactory;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.cache.annotation.Cacheable;
      import org.springframework.stereotype.Service;

      import com.xjj.dao.PersonDAO;
      import com.xjj.entity.Person;

      @Service
      public class PersonService {
      protected final Logger logger = LoggerFactory.getLogger(this.getClass());

      @Autowired
      private PersonDAO personDao;


      /**
      * 根据id获取Person对象,使用缓存
      * @param id
      * @return Person对象
      */
      @Cacheable(value="getPersonById", sync=true)
      public Person getPersonById(int id){
      logger.debug("getting data from database, personId={}", id);
      return personDao.getPersonById(id);
      }

      }
    • 测试:

      @Autowired
      PersonService personService;

      @Test
      public void localCacheTest() throws JsonProcessingException, InterruptedException{
      System.out.println("第一次:"); //从数据库中获取
      Person p = personService.getPersonById(2);
      logger.info("1st time: {}", objectMapper.writeValueAsString(p));

      System.out.println("第二次:"); //从缓存中获取
      p = personService.getPersonById(2);
      logger.info("2nd time: {}", objectMapper.writeValueAsString(p));

      Thread.sleep(5000);

      System.out.println("第三次:"); //5秒钟超时后,从数据库中获取
      p = personService.getPersonById(2);
      logger.info("3rd time: {}", objectMapper.writeValueAsString(p));

      System.out.println("第四次:"); //从缓存中获取
      p = personService.getPersonById(2);
      logger.info("4th time: {}", objectMapper.writeValueAsString(p));

      }

      测试结果:
      第一次:

      2016-11-02 17:11:13,105:DEBUG main (PersonService.java:27) - getting data from database, personId=2
      2016-11-02 17:11:13,150:INFO main (HikariDataSource.java:93) - HikariPool-1 - Started.
      2016-11-02 17:11:13,523:DEBUG main (BaseJdbcLogger.java:145) - ==> Preparing: SELECT id, first_name AS firstName, last_name AS lastName, birth_date AS birthDate, sex, phone_no AS phoneNo FROM test.t_person WHERE id=?;
      2016-11-02 17:11:13,554:DEBUG main (BaseJdbcLogger.java:145) - ==> Parameters: 2(Integer)
      2016-11-02 17:11:13,572:TRACE main (BaseJdbcLogger.java:151) - <== Columns: id, firstName, lastName, birthDate, sex, phoneNo
      2016-11-02 17:11:13,573:TRACE main (BaseJdbcLogger.java:151) - <== Row: 2, 八, 李, 2015-08-07, F, 13625896321
      2016-11-02 17:11:13,582:DEBUG main (BaseJdbcLogger.java:145) - <== Total: 1
      2016-11-02 17:11:13,665:INFO main (MySpringBootApplicationTests.java:149) - 1st time: {"id":2,"firstName":"八","lastName":"李","birthDate":1438876800000,"sex":"F","phoneNo":"13625896321"}

      第二次:

      2016-11-02 17:11:13,666:INFO main (MySpringBootApplicationTests.java:153) - 2nd time: {"id":2,"firstName":"八","lastName":"李","birthDate":1438876800000,"sex":"F","phoneNo":"13625896321"}

      第三次:

      2016-11-02 17:11:18,668:DEBUG main (PersonService.java:27) - getting data from database, personId=2
      2016-11-02 17:11:18,669:DEBUG main (BaseJdbcLogger.java:145) - ==> Preparing: SELECT id, first_name AS firstName, last_name AS lastName, birth_date AS birthDate, sex, phone_no AS phoneNo FROM test.t_person WHERE id=?;
      2016-11-02 17:11:18,670:DEBUG main (BaseJdbcLogger.java:145) - ==> Parameters: 2(Integer)
      2016-11-02 17:11:18,671:TRACE main (BaseJdbcLogger.java:151) - <== Columns: id, firstName, lastName, birthDate, sex, phoneNo
      2016-11-02 17:11:18,672:TRACE main (BaseJdbcLogger.java:151) - <== Row: 2, 八, 李, 2015-08-07, F, 13625896321
      2016-11-02 17:11:18,672:DEBUG main (BaseJdbcLogger.java:145) - <== Total: 1
      2016-11-02 17:11:18,673:INFO main (MySpringBootApplicationTests.java:159) - 3rd time: {"id":2,"firstName":"八","lastName":"李","birthDate":1438876800000,"sex":"F","phoneNo":"13625896321"}

      第四次:

      2016-11-02 17:11:18,674:INFO main (MySpringBootApplicationTests.java:163) - 4th time: {"id":2,"firstName":"八","lastName":"李","birthDate":1438876800000,"sex":"F","phoneNo":"13625896321"}

Perfect!!!
完整的源代码:https://github.com/xujijun/my-spring-boot