zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    python-IO编程
    DNS解析流程
    python-模块
    HTTP协议
    python-函数式编程
    nmap扫描结果的6种端口状态
    python-高级特性
    python-函数
    python-基础
    上传之路
  • 原文地址:https://www.cnblogs.com/bigben0123/p/9294872.html
Copyright © 2011-2022 走看看