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
原文地址:https://www.cnblogs.com/bigben0123/p/9294872.html