Spring Boot缓存

作者: ruoshy | 来源:发表于2019-11-19 22:20 被阅读0次

    概述

      Spring提供了对应用程序添加缓存的支持。从本质上讲,将缓存应用于方法上,从而根据缓存中的信息减少执行次数。当开发者调用一个方法时,将方法的参数和返回值作为Key/Value缓存起来,当再次调用这个方法时,如果缓存中存在对应数据,就从缓存中获取数据,否则再去执行该方法。

    支持

    Spring并没有提供缓存的实现,而是提供了一套Api,可以自由选择缓存的实现。目前Spring Boot支持的缓存有如下几种(Spring Boot会按顺序检测以下提供的程序):

    • Generic
    • JCache(JSR-107)(EhCache 3, Hazelcast, Infinispan, and others)
    • EhCache 2.x
    • Hazelcast
    • Infinispan
    • Couchbase
    • Readis
    • Caffeine
    • Simple

    可以通过设置属性来指定提供缓存的程序。

    无论使用哪种缓存实现不同的只是缓存配置,使用的缓存注解是一致的

    注解 介绍
    @EnableCaching 启用S​​pring的注释驱动的缓存管理功能
    @CacheConfig 当此批注出现在给定的类上时,它为该类中定义的任何缓存操作提供一组默认设置。
    @Cacheable 表示可以缓存调用方法(或类中的所有方法)结果的注释。每次调用指定方法时,将进行缓存行为,检查是否已缓存该方法参数和结果。如果在缓存中找不到键的值,则将调用目标方法并将返回的值存储在关联的缓存中。
    @CacheEvict 表示方法(或类上的所有方法)触发缓存逐出操作的注解。
    @CachePut 表示方法(或类上的所有方法)触发缓存放置操作的注释。
    @Caching 此批注可用作元批注,以创建具自定义组合批注。

    一、Ehcache 2.x 缓存

    在项目的pom.xml文件中添加以下依赖:
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-cache</artifactId>
            </dependency>
    
            <dependency>
                <groupId>net.sf.ehcache</groupId>
                <artifactId>ehcache</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-jdbc</artifactId>
            </dependency>
    
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>2.1.0</version>
            </dependency>
    
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>1.2.58</version>
            </dependency>
    
    添加缓存配置文件

    如果存在Ehcache的依赖,并且在classpath下有名为ehcache.xml的文件,那么EhCacheCacheManager将会自动作为缓存的提供者。因此,在resources目录下创建ehcache.xml文件作为Ehcach缓存的配置文件。

    如果未配置ehcache.xml,则Ehcache依赖包下的ehcache-failsafe.xml是ehcache的默认配置。

    <ehcache>
        <!--diskStore元素是可选的。-->
        <!--如果为任何缓存启用了overflowToDisk或diskPersistent,则必须对其进行配置。-->
        <!--diskStore只有一个属性 - path。这是将在其中创建.data和.index文件的目录路径。-->
        <!--如果路径是Java系统属性,则将其替换为正在运行的VM中的值-->
        <!--user.home - 用户的主目录-->
        <!--user.dir - 用户的当前工作目录-->
        <!--java.io.tmpdir - 默认临时文件路径-->
        <!--ehcache.disk.store.dir - 在命令行上指定的系统属性(例:java -Dehcache.disk.store.dir=/u01/myapp/diskdir)-->
        <!--子目录可以在属性后指定 例:java.io.tmpdir/cache-->
        <diskStore path="java.io.tmpdir/cache"/>
    
        <!--强制性默认缓存配置-->
        <!--这些设置将应用于使用CacheManager.add(String cacheName)创建的缓存。-->
        <defaultCache
                maxElementsInMemory="10000"
                eternal="false"
                timeToIdleSeconds="120"
                timeToLiveSeconds="120"
                maxElementsOnDisk="10000000"
                diskExpiryThreadIntervalSeconds="120"
                memoryStoreEvictionPolicy="LRU">
            <!--localTempSwap - 该策略允许缓存在缓存操作期间使用本地磁盘。磁盘存储是临时的,并在重新启动后清除。-->
            <!--当策略为"none"时,所有缓存都保留在内存中(磁盘无溢出,磁盘无持久性)。-->
            <persistence strategy="localTempSwap"/>
        </defaultCache>
    
        <cache name="student"
               maxElementsInMemory="10000"
               eternal="false"
               timeToIdleSeconds="120"
               timeToLiveSeconds="120"
               overflowToDisk="true"
               diskPersistent="true"
               diskExpiryThreadIntervalSeconds="600"/>
    </ehcache>
    
    缓存配置 <cache/>:
    属性 介绍
    name 设置缓存的名称,用于标识缓存。
    maxElementsInMemory 设置在内存中创建的最大对象数(0 ==无限制)。
    maxElementsOnDisk 设置将在DiskStore中维护的最大对象数。默认值为零,表示无限制。
    eternal 设置元素是否一直存在。如果为true,超时将被忽略。
    overflowToDisk 设置当内存中的缓存达到maxInMemory限制时元素是否可以溢出到磁盘。
    timeToIdleSeconds 设置元素过期之前的空闲时间,即缓存创建以后最后一次访问缓存的时间到超时失效时的时间间隔。值为0表示无穷大,默认值为0。
    timeToLiveSeconds 设置元素过期之前的生存时间,即从创建时间到元素过期之间的间隔。值为0表示一直存在,默认值为0。
    diskPersistent 磁盘存储在虚拟机重新启动后是否仍然存在,默认值为false。
    diskExpiryThreadIntervalSeconds 做元素失效监测以及清除工作的线程运行间隔时间,默认值为120秒。
    diskSpoolBufferSizeMB 磁盘缓冲区大小,默认30MB(内部以字节为单位,设置的值转换为字节后不可超过正整数表示范围)。
    memoryStoreEvictionPolicy 达到maxElementsInMemory限制后,将强制执行清除策略。默认策略是最近最少使用(LRU),其他可用策略先进先出(指定为FIFO)和不常用(指定为LFU)。
    开启缓存

    在项目的入口类上添加@EnableCaching注解开启缓存。

    @EnableCaching
    @SpringBootApplication
    public class EhcacheApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(EhcacheApplication.class, args);
        }
    
    }
    
    创建数据接口
    @Service
    @CacheConfig(cacheNames = "student")
    public class StudentService {
    
        @Resource
        private StudentMapper studentMapper;
    
        @Cacheable
        public Student findById(Integer id) {
            Student student = studentMapper.findById(id);
            System.out.println("查询 :" + JSON.toJSONString(student));
            return student;
        }
    
        @CachePut(key = "#student.id")
        public Student insert(Student student) throws Exception {
            int status = studentMapper.insert(student);
            if (status == 0) {
                throw new Exception("Insert failed");
            }
            System.out.println("插入 :" + JSON.toJSONString(student));
            return student;
        }
    
        @CacheEvict(key = "#student.id")
        public Student delete(Student student) throws Exception {
            int status = studentMapper.delete(student);
            if (status == 0) {
                throw new Exception("Delete failed");
            }
            System.out.println("删除 :" + JSON.toJSONString(student));
            return student;
        }
    
        @CachePut(key = "#student.id")
        public Student update(Student student) throws Exception {
            int status = studentMapper.update(student);
            if (status == 0) {
                throw new Exception("Update failed");
            }
            System.out.println("更新 :" + JSON.toJSONString(student));
            return student;
        }
    
    }
    
    创建请求接口
    @RestController
    @RequestMapping("/std")
    public class StudentController {
    
        @Resource
        private StudentService studentService;
    
        /**
         * 查询
         */
        @RequestMapping("/sel")
        public String findById(Student student) {
            Map<String, Object> msg = new HashMap<>();
            try {
                student = studentService.findById(student.getId());
                msg.put("status", 200);
                msg.put("result", student);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return JSON.toJSONString(msg);
        }
    
        /**
         * 插入
         */
        @RequestMapping("/ins")
        public String insert(Student student) {
            Map<String, Object> msg = new HashMap<>();
            try {
                studentService.insert(student);
                msg.put("status", 200);
                msg.put("message", "插入成功");
            } catch (Exception e) {
                msg.put("status", 400);
                msg.put("message", "插入失败");
            }
            return JSON.toJSONString(msg);
        }
    
    
        /**
         * 删除
         */
        @RequestMapping("/del")
        public String delete(Student student) {
            Map<String, Object> msg = new HashMap<>();
            try {
                studentService.delete(student);
                msg.put("status", 200);
                msg.put("message", "删除成功");
            } catch (Exception e) {
                msg.put("status", 400);
                msg.put("message", "删除失败");
            }
            return JSON.toJSONString(msg);
        }
    
        /**
         * 更新
         */
        @RequestMapping("/upd")
        public String update(Student student) {
            Map<String, Object> msg = new HashMap<>();
            try {
                studentService.update(student);
                msg.put("status", 200);
                msg.put("message", "更新成功");
            } catch (Exception e) {
                msg.put("status", 400);
                msg.put("message", "更新失败");
            }
            return JSON.toJSONString(msg);
        }
    
    }
    
    测试

    向数据库中添加数据:

    (一) 查询

    连续访问查询接口页面并观察输出信息:

    查询 控制台

    根据输出信息发现,在浏览器中多次获取数据,数据查询方法只执行了一遍。

    (二) 更新

    访问更新接口:

    更新

    再次访问查询接口:

    查询

    观察控制台:


    控制台

    发现访问更新接口后再次访问查询接口,查询接口并没有再次从数据库获取数据,而是从缓存中获取,所以更新接口返回的结果会覆盖指定参数键的缓存。

    (三) 插入

    访问插入接口:

    插入

    访问查询接口:

    查询

    观察控制台:

    控制台

    发现插入数据后再次查询并没有从数据库中获取数据,而是从缓存中获取。因为插入时对参数和返回的结果进行了缓存。

    (四) 删除

    访问删除接口:

    删除

    访问查询接口:

    查询

    观察控制台:

    控制台

    发现访问删除接口后数据库中的数据被删除,再次访问查询接口进行查询发现触发了查询方法,说明删除数据时缓存同时也被删除了。

    二、Redis 单机缓存

    和Ehcache一样,如果在classpath下存在Redis并且Redis已经配置好,此时会默认使用RedisCacheManager作为缓存的提供者。

    在项目的pom.xml文件中添加以下依赖(与之前的依赖相比不同的只是缓存的提供者):

            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-cache</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-jdbc</artifactId>
            </dependency>
    
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>2.1.0</version>
            </dependency>
    
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>1.2.58</version>
            </dependency>
    

    添加项目配置:

    spring:
      datasource:
        url: jdbc:mysql://xxx.xxx.xxx.xxx/user
        username: xxxx
        password: xxxxxx
    
      # 缓存配置
      cache:
        # 配置缓存名,多个缓存可使用逗号分隔(one,two)
        cache-names: student
        # 缓存存在时间
        redis:
          time-to-live: 120s
      redis:
        host: xxx.xxx.xxx.xxx
        port: xxxx
    

    之后的流程与之前一致就不在阐述了。

    相关文章

      网友评论

        本文标题:Spring Boot缓存

        本文链接:https://www.haomeiwen.com/subject/ukiiictx.html