SpringBoot學習筆記(6) SpringBoot數據緩存Cache [Guava和Redis實現]


https://blog.csdn.net/a67474506/article/details/52608855


 

在不適用任何額外配置的情況下,默認使用SimpleCacheConfiguration

SpringBoot通過spring.cache為前綴來配置緩存


 

使用這些緩存實現的話,只需導入相關緩存的依賴,並在配置類中使用@EnableCaching開啟緩存即可

Guava實現

這里簡單介紹下使用Guava實現

引入的依賴

pom.xml

  1.  
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  2.  
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  3.  
    <modelVersion>4.0.0</modelVersion>
  4.  
    <groupId>com.ibigsea</groupId>
  5.  
    <artifactId>spirngboot-cache-demo</artifactId>
  6.  
    <version>0.0.1-SNAPSHOT</version>
  7.  
     
  8.  
     
  9.  
    <properties>
  10.  
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  11.  
    <boot.version>1.3.5.RELEASE</boot.version>
  12.  
    </properties>
  13.  
     
  14.  
    <dependencies>
  15.  
    <dependency>
  16.  
    <groupId>org.springframework.boot</groupId>
  17.  
    <artifactId>spring-boot-starter-web</artifactId>
  18.  
    <version>${boot.version}</version>
  19.  
    </dependency>
  20.  
    <dependency>
  21.  
    <groupId>org.springframework.boot</groupId>
  22.  
    <artifactId>spring-boot-starter-test</artifactId>
  23.  
    <version>${boot.version}</version>
  24.  
    <scope>test</scope>
  25.  
    </dependency>
  26.  
    <dependency>
  27.  
    <groupId>org.springframework.boot</groupId>
  28.  
    <artifactId>spring-boot-starter-cache</artifactId>
  29.  
    <version>${boot.version}</version>
  30.  
    </dependency>
  31.  
    <dependency>
  32.  
    <groupId>com.google.guava</groupId>
  33.  
    <artifactId>guava</artifactId>
  34.  
    <version>19.0</version>
  35.  
    </dependency>
  36.  
    </dependencies>
  37.  
    </project>

dataCache.java

  1.  
    package com.ibigsea.springboot_cache_demo.cache;
  2.  
     
  3.  
    import java.text.SimpleDateFormat;
  4.  
    import java.util.Date;
  5.  
    import java.util.HashMap;
  6.  
    import java.util.Map;
  7.  
     
  8.  
    import javax.annotation.PostConstruct;
  9.  
     
  10.  
    import org.springframework.cache.annotation.CacheConfig;
  11.  
    import org.springframework.cache.annotation.CacheEvict;
  12.  
    import org.springframework.cache.annotation.CachePut;
  13.  
    import org.springframework.cache.annotation.Cacheable;
  14.  
    import org.springframework.stereotype.Component;
  15.  
     
  16.  
    @Component
  17.  
    public class DataCache {
  18.  
     
  19.  
    private Map<Long, String> dataMap = new HashMap<>();
  20.  
     
  21.  
    /**
  22.  
    * 初始化
  23.  
    */
  24.  
    @PostConstruct
  25.  
    public void init() {
  26.  
    dataMap.put( 1L, "張三");
  27.  
    dataMap.put( 2L, "李四");
  28.  
    dataMap.put( 3L, "王五");
  29.  
    }
  30.  
     
  31.  
    /**
  32.  
    * 查詢
  33.  
    * 如果數據沒有緩存,那么從dataMap里面獲取,如果緩存了,
  34.  
    * 那么從guavaDemo里面獲取
  35.  
    * 並且將緩存的數據存入到 guavaDemo里面
  36.  
    * 其中key 為 #id+dataMap
  37.  
    */
  38.  
    @Cacheable(value="guavaDemo" ,key="#id + 'dataMap'")
  39.  
    public String query(Long id) {
  40.  
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  41.  
    System.out.println(sdf.format( new Date()) + " : query id is " + id);
  42.  
    return dataMap.get(id);
  43.  
    }
  44.  
     
  45.  
    /**
  46.  
    * 插入 或者更新
  47.  
    * 插入或更新數據到dataMap中
  48.  
    * 並且緩存到 guavaDemo中
  49.  
    * 如果存在了那么更新緩存中的值
  50.  
    * 其中key 為 #id+dataMap
  51.  
    */
  52.  
    @CachePut(value="guavaDemo" ,key="#id + 'dataMap'")
  53.  
    public String put(Long id, String value) {
  54.  
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  55.  
    System.out.println(sdf.format( new Date()) + " : add data ,id is "+ id);
  56.  
    dataMap.put(id, value);
  57.  
    // data persistence
  58.  
    return value;
  59.  
    }
  60.  
     
  61.  
    /**
  62.  
    * 刪除
  63.  
    * 刪除dataMap里面的數據
  64.  
    * 並且刪除緩存guavaDemo中的數據
  65.  
    * 其中key 為 #id+dataMap
  66.  
    */
  67.  
    @CacheEvict(value="guavaDemo" , key="#id + 'dataMap'")
  68.  
    public void remove(Long id) {
  69.  
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  70.  
    System.out.println(sdf.format( new Date()) + " : remove id is "+ id + " data");
  71.  
    dataMap.remove(id);
  72.  
    // data remove
  73.  
    }
  74.  
     
  75.  
     
  76.  
    }

關於緩存注解中的value,就是配置文件中的cache-names

關於注解中的key這個值,如果不指定的話 ,那么會取方法參數當做Key

 

application.yml

  1.  
    spring:
  2.  
    cache:
  3.  
    #緩存名稱
  4.  
    cache-names: guavaDemo
  5.  
    #緩存最大數量500條, 緩存失效時間 6個小時
  6.  
    guava.spec: maximumSize=500,expireAfterWrite=360m

App.java

  1.  
    package com.ibigsea.springboot_cache_demo;
  2.  
     
  3.  
    import java.text.SimpleDateFormat;
  4.  
    import java.util.Date;
  5.  
     
  6.  
    import org.springframework.beans.factory.annotation.Autowired;
  7.  
    import org.springframework.boot.SpringApplication;
  8.  
    import org.springframework.boot.autoconfigure.SpringBootApplication;
  9.  
    import org.springframework.cache.annotation.EnableCaching;
  10.  
    import org.springframework.web.bind.annotation.RequestMapping;
  11.  
    import org.springframework.web.bind.annotation.RestController;
  12.  
     
  13.  
    import com.ibigsea.springboot_cache_demo.cache.DataCache;
  14.  
     
  15.  
    /**
  16.  
    * 是Spring Boot項目的核心注解,主要是開啟自動配置
  17.  
    */
  18.  
    @SpringBootApplication // same as @Configuration @EnableAutoConfiguration @ComponentScan
  19.  
    @RestController
  20.  
    // 開啟緩存
  21.  
    @EnableCaching
  22.  
    public class App {
  23.  
     
  24.  
    @Autowired
  25.  
    private DataCache dataCache;
  26.  
     
  27.  
    public static void main(String[] args) {
  28.  
    SpringApplication.run(App.class, args);
  29.  
    }
  30.  
     
  31.  
    @RequestMapping("/put")
  32.  
    public String put(Long id, String value) {
  33.  
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  34.  
    return sdf.format(new Date()) + " : value is " + dataCache.put(id, value) ;
  35.  
    }
  36.  
     
  37.  
    @RequestMapping("/get")
  38.  
    public String query(Long id){
  39.  
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  40.  
    return sdf.format(new Date()) + " : value is " +dataCache.query(id) ;
  41.  
    }
  42.  
     
  43.  
    @RequestMapping("/remove")
  44.  
    public String remove(Long id) {
  45.  
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  46.  
    dataCache.remove(id) ;
  47.  
    return sdf.format(new Date()) + " : success " ;
  48.  
    }
  49.  
     
  50.  
    }

運行結果


關於注解配置:

@Cacheable


@CacheEvict


@CachePut

和上面的一樣,只是這個注解是用來更新或者插入數據到緩存中的,

其中key自己定義,返回值會緩存

 

還有就是SpringBoot會根據你的類路徑里面的依賴jar,來確定使用什么類型進行緩存,所以基本是我們是不用配置spring.cache.type這個屬性的


 

Redis實現

 

Redis緩存:

如果是用redis作為緩存的話

我們只需要引入redis相關依賴,修改yml配置屬性

  1.  
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  2.  
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  3.  
    <modelVersion>4.0.0</modelVersion>
  4.  
    <groupId>com.ibigsea</groupId>
  5.  
    <artifactId>spirngboot-cache-demo</artifactId>
  6.  
    <version>0.0.1-SNAPSHOT</version>
  7.  
     
  8.  
     
  9.  
    <properties>
  10.  
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  11.  
    <boot.version>1.3.5.RELEASE</boot.version>
  12.  
    </properties>
  13.  
     
  14.  
    <dependencies>
  15.  
    <dependency>
  16.  
    <groupId>org.springframework.boot</groupId>
  17.  
    <artifactId>spring-boot-starter-web</artifactId>
  18.  
    <version>${boot.version}</version>
  19.  
    </dependency>
  20.  
    <dependency>
  21.  
    <groupId>org.springframework.boot</groupId>
  22.  
    <artifactId>spring-boot-starter-test</artifactId>
  23.  
    <version>${boot.version}</version>
  24.  
    <scope>test</scope>
  25.  
    </dependency>
  26.  
    <dependency>
  27.  
    <groupId>org.springframework.boot</groupId>
  28.  
    <artifactId>spring-boot-starter-cache</artifactId>
  29.  
    <version>${boot.version}</version>
  30.  
    </dependency>
  31.  
    <dependency>
  32.  
    <groupId>org.springframework.boot</groupId>
  33.  
    <artifactId>spring-boot-starter-redis</artifactId>
  34.  
    <version>${boot.version}</version>
  35.  
    </dependency>
  36.  
    <!-- <dependency> -->
  37.  
    <!-- <groupId>com.google.guava</groupId> -->
  38.  
    <!-- <artifactId>guava</artifactId> -->
  39.  
    <!-- <version>19.0</version> -->
  40.  
    <!-- </dependency> -->
  41.  
    </dependencies>
  42.  
    </project>

application.yml

  1.  
    spring:
  2.  
    cache:
  3.  
    #緩存名稱
  4.  
    cache-names: guavaDemo
  5.  
    #緩存最大數量500條, 緩存失效時間 6個小時
  6.  
    #guava.spec: maximumSize=500,expireAfterWrite=360m
  7.  
    # REDIS (RedisProperties)
  8.  
    redis :
  9.  
    host : localhost # server host
  10.  
    port : 6379 # connection port
  11.  
    pool.max-idle : 8 # pool settings ...
  12.  
    pool.min-idle : 1
  13.  
    pool.max-active : 8
  14.  
    pool.max-wait : -1

就這樣就OK了,代碼什么的都是不用改變的,是不是很方便

測試結果



 

數據都會緩存到redis里面

其他的地方就不測試了 都是差不多的

 

使用其他實現導入對應的依賴,然后添加配置即可

 

注意: 

如果使用guava緩存的時候 ,同時添加了redis的jar依賴,或者其他的依賴,可能會出現異常

這個時候加上 type: GUAVA 就可以

 

版權聲明:本文為博主原創文章,博客地址:http://blog.csdn.net/a67474506?viewmode=contents https://blog.csdn.net/a67474506/article/details/52608855
 

guava cache

 

 

緩存是提高性能的一把利器。
常用到的緩存技術有分布式緩存,像Redis、MC;也有本地緩存,像ehcache、guava cache等。這里說的是本地緩存guava cache。

guava cache剛開始接觸,這就記錄下來。。

復制代碼
    public static void main(String[] args) throws ExecutionException, InterruptedException{
        //緩存接口這里是LoadingCache,LoadingCache在緩存項不存在時可以自動加載緩存
        LoadingCache<Integer,Student> studentCache
                //CacheBuilder的構造函數是私有的,只能通過其靜態方法newBuilder()來獲得CacheBuilder的實例
                = CacheBuilder.newBuilder()
                //設置並發級別為8,並發級別是指可以同時寫緩存的線程數
                .concurrencyLevel(8)
                //設置寫緩存后8秒鍾過期
                .expireAfterWrite(8, TimeUnit.SECONDS)
          //設置寫緩存后1秒鍾刷新
         .refreshAfterWrite(1, TimeUnit. SECONDS)                 //設置緩存容器的初始容量為10                 .initialCapacity(10)                 //設置緩存最大容量為100,超過100之后就會按照LRU最近雖少使用算法來移除緩存項                 .maximumSize(100)                 //設置要統計緩存的命中率                 .recordStats()                 //設置緩存的移除通知                 .removalListener(new RemovalListener<Object, Object>() {                     @Override                     public void onRemoval(RemovalNotification<Object, Object> notification) {                         System.out.println(notification.getKey() + " was removed, cause is " + notification.getCause());                     }                 })                 //build方法中可以指定CacheLoader,在緩存不存在時通過CacheLoader的實現自動加載緩存                 .build(                         new CacheLoader<Integer, Student>() {                             @Override                             public Student load(Integer key) throws Exception {                                 System.out.println("load student " + key);                                 Student student = new Student();                                 student.setId(key);                                 student.setName("name " + key);                                 return student;                             }                         }                 );         for (int i=0;i<20;i++) {             //從緩存中得到數據,由於我們沒有設置過緩存,所以需要通過CacheLoader加載緩存數據             Student student = studentCache.get(1);             System.out.println(student);             //休眠1秒             TimeUnit.SECONDS.sleep(1);         }         System.out.println("cache stats:");         //最后打印緩存的命中率等 情況         System.out.println(studentCache.stats().toString());     }
復制代碼

還有另一種方法

復制代碼
package com;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

import com.google.common.cache.*;

/** 
 * @author  作者 PZhang  E-mail:pzhang@rxhui.com 
 * @date 創建時間:2017-2-15 上午9:58:00 
 * @version 1.0 
 * @parameter   
 * @return  
 */
public class CacheModel {
    public Student getStudent(Integer key){
        System.out.println("load student " + key);
        Student student = new Student();
        student.setId(key);
        student.setName("name " + key);
        return student;
    }
    //load Method
    public void loadCacheA() throws Exception{
         LoadingCache<Integer,Student> studentCache= CacheBuilder.newBuilder().concurrencyLevel(8).
                 expireAfterWrite(8, TimeUnit.SECONDS).refreshAfterWrite(1, TimeUnit. SECONDS).initialCapacity(10).maximumSize(100)
                 .recordStats().removalListener(new RemovalListener<Object, Object>() {
                     public void onRemoval(RemovalNotification<Object, Object> notification) {
                                 System.out.println(notification.getKey() + " was removed, cause is " + notification);}
                     }).build(
                    new CacheLoader<Integer, Student>() {
                        @Override
                        public Student load(Integer key) throws Exception {
                            return getStudent(key);
                           }
                        }
          );

         for (int i=0;i<20;i++) {
           Student student = studentCache.get(1);
           System.out.println(student);
           TimeUnit.SECONDS.sleep(1);
         }

         System.out.println("cache stats:");
         System.out.println(studentCache.stats().toString());
         
    }
    //call back Method
    public void loadCacheB(final Integer key) throws Exception{
         Cache<Integer, Student> cache = CacheBuilder.newBuilder().maximumSize(1000).recordStats().expireAfterWrite(8, TimeUnit.SECONDS).build();  
             
         for (int i=0;i<20;i++) {
                System.out.println(cache.get(key, new Callable<Student>() {  
                     public Student call() {  
                        return getStudent(key);
                    }  
                }));
                TimeUnit.SECONDS.sleep(1);
              }

              System.out.println("cache stats:");
              System.out.println(cache.stats().toString());
    }
    
    public static void main(String[] args) throws Exception {
        CacheModel cache = new CacheModel();
        cache.loadCacheB(2);
    }
    
    
    
}
復制代碼

 

復制代碼
 

  guava Cache數據移除:

  guava做cache時候數據的移除方式,在guava中數據的移除分為被動移除和主動移除兩種。
  被動移除數據的方式,guava默認提供了三種方式:
  1.基於大小的移除:看字面意思就知道就是按照緩存的大小來移除,如果即將到達指定的大小,那就會把不常用的鍵值對從cache中移除。
  定義的方式一般為 CacheBuilder.maximumSize(long),還有一種一種可以算權重的方法,個人認為實際使用中不太用到。就這個常用的來看有幾個注意點,
    其一,這個size指的是cache中的條目數,不是內存大小或是其他;
    其二,並不是完全到了指定的size系統才開始移除不常用的數據的,而是接近這個size的時候系統就會開始做移除的動作;
    其三,如果一個鍵值對已經從緩存中被移除了,你再次請求訪問的時候,如果cachebuild是使用cacheloader方式的,那依然還是會從cacheloader中再取一次值,如果這樣還沒有,就會拋出異常
  2.基於時間的移除:guava提供了兩個基於時間移除的方法
    expireAfterAccess(long, TimeUnit)  這個方法是根據某個鍵值對最后一次訪問之后多少時間后移除
    expireAfterWrite(long, TimeUnit)  這個方法是根據某個鍵值對被創建或值被替換后多少時間移除
  3.基於引用的移除:
  這種移除方式主要是基於java的垃圾回收機制,根據鍵或者值的引用關系決定移除
  主動移除數據方式,主動移除有三種方法:
  1.單獨移除用 Cache.invalidate(key)
  2.批量移除用 Cache.invalidateAll(keys)
  3.移除所有用 Cache.invalidateAll()
  如果需要在移除數據的時候有所動作還可以定義Removal Listener,但是有點需要注意的是默認Removal Listener中的行為是和移除動作同步執行的,如果需要改成異步形式,可以考慮使用RemovalListeners.asynchronous(RemovalListener, Executor)

復制代碼

   

 
 
標簽:  cache


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM