后端开发——缓存篇

文章目录

      • 一、JSR107缓存规范
      • 二、Spring Cache
      • 三、Spring使用缓存
      • 四、SpringBoot整合EhCache

一、JSR107缓存规范

   JSR是Java Specification Requests的缩写,意思是Java 规范提案。它定义了一种对Java对象临时在内存中进行缓存的方法,包括对象的创建、共享访问、假脱机(spooling)、失效、各JVM的一致性等,可被用于缓存JSP内最经常读取的数据。

JSR的目标

  • 为应用程序提供缓存Java对象的功能。
  • 定义了一套通用的缓存概念和工具。
  • 最小化开发人员使用缓存的学习成本。
  • 最大化应用程序在使用不同缓存实现之间的可移植性。
  • 支持进程内和分布式的缓存实现。
  • 支持by-value和by-reference的缓存对象。
  • 定义遵照JSR-175的缓存注解。
  • 定义一套Java编程语言的元数据。

Java Caching定义了5个核心接口,分别是CachingProvider, CacheManager, Cache, Entry 和 Expiry。

  1. CachingProvider定义了创建、配置、获取、管理和控制多个CacheManager。一个应用可以在运行期访问多个CachingProvider。

  2. CacheManager定义了创建、配置、获取、管理和控制多个唯一命名的Cache,这些Cache存在于CacheManager的上下文中。一个CacheManager仅被一个CachingProvider所拥有。

  3. Cache是一个类似Map的数据结构并临时存储以Key为索引的值。一个Cache仅被一个CacheManager所拥有。

  4. Entry是一个存储在Cache中的key-value对。

  5. Expiry 每一个存储在Cache中的条目有一个定义的有效期。一旦超过这个时间,条目为过期的状态。一旦过期,条目将不可访问、更新和删除。缓存有效期可以通过ExpiryPolicy设置。

    这五个核心接口的关系如下图:

后端开发——缓存篇_第1张图片

二、Spring Cache

  Spring通过CacheCacheManager两个接口统一了不同的缓存技术,并支持JSR107,大大简化了缓存需求的开发。

  Spring Cache核心思想是这样的:当我们在调用一个缓存方法时会把该方法参数和返回结果作为一个键值对存放在缓存中,等到下次利用同样的参数来调用该方法时将不再执行该方法,而是直接从缓存中获取结果进行返回。所以在使用Spring Cache的时候我们要保证我们缓存的方法对于相同的方法参数要有相同的返回结果。

  使用Spring Cache仅仅需要注意两点

  • 确定方法需要被缓存以及他们的缓存策略(Cache接口下定义了各种缓存策略,如 RedisCache,ConcurrentMapCache )
  • 配置Spring对Cache的支持(通过注解配置)

缓存注解

Cache 缓存接口,用于定义缓存操作。其实现有:RedisCache、ConcurrentMapCache、EhCacheCache等
CacheManager 缓存管理器,用于管理各种缓存组件(见上图)
@EnableCaching 开启基于注解的缓存
@Cacheable 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存
@CacheEvict 清空缓存
@CachePut 保证缓存被调用,且将结果缓存
keyGenerator 缓存数据时key的生成策略
serilalize 缓存数据时value的序列化策略

  Spring为我们提供这几个注解来支持Spring Cache。用的最多的是 @Cacheable@CacheEvict。使用@Cacheable标记的方法在执行后Spring Cache将缓存其返回结果,而使用@CacheEvict标记的方法会在方法执行前或者执行后移除Spring Cache中的某些元素。

  • @Cacheable 可以标记在一个方法上,也可以标记在一个类上。当标记在一个方法上时表示该方法是支持缓存的,当标记在一个类上时则表示该类所有的方法都是支持缓存的。对于一个支持缓存的方法,Spring会在其被调用后将其返回值缓存起来,以保证下次利用同样的参数来执行该方法时可以直接从缓存中获取结果,而不需要再次执行该方法。Spring在缓存方法的返回值时是以键值对进行缓存的,值就是方法的返回结果,至于键的话,Spring又支持两种策略,默认策略和自定义策略。需要注意的是当一个支持缓存的方法在对象内部被调用时是不会触发缓存功能的。@Cacheable可以指定三个属性,value、key和condition。
  • @CacheEvict是用来标注在需要清除缓存元素的方法或类上的。当标记在一个类上时表示其中所有的方法的执行都会触发缓存的清除操作。@CacheEvict可以指定的属性有value、key、condition、allEntries和beforeInvocation。其中value、key和condition的语义与@Cacheable对应的属性类似。即value表示清除操作是发生在哪些Cache上的(对应Cache的名称);key表示需要清除的是哪个key,如未指定则会使用默认策略生成的key;condition表示清除操作发生的条件。

三、Spring使用缓存

  • step1:导入spring-boot-starter-cache模块

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-cache</artifactId>
    </dependency>
    
  • step2:@EnableCaching开启缓存

  • step3:使用缓存注解

四、SpringBoot整合EhCache

EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点。 http://www.ehcache.org/

主要的特性有:

  1. 快速

  2. 简单

  3. 多种缓存策略

  4. 缓存数据有两级:内存和磁盘,因此无需担心容量问题

  5. 缓存数据会在虚拟机重启的过程中写入磁盘

  6. 可以通过RMI、可插入API等方式进行分布式缓存

  7. 具有缓存和缓存管理器的侦听接口

  8. 支持多缓存管理器实例,以及一个实例的多个缓存区域

  9. 提供Hibernate的缓存实现

ehcache.xml 文档的使用,其参数如下:

<diskStore>: 当内存缓存中对象数量超过maxElementsInMemory时,将缓存对象写到磁盘缓存中(需对象实现序列化接口)  
<diskStore path="">: 用来配置磁盘缓存使用的物理路径,Ehcache磁盘缓存使用的文件后缀名是*.data和*.index  
name : "缓存名称,cache的唯一标识(ehcache会把这个cache放到HashMap里)  
maxElementsInMemory: 缓存最大个数。
eternal="false": 对象是否永久有效,一但设置了,timeout将不起作用。 (必须设置)
maxEntriesLocalHeap="1000": 堆内存中最大缓存对象数,0没有限制(必须设置)
maxEntriesLocalDisk= "1000": 硬盘最大缓存个数。 
overflowToDisk="false": 当缓存达到maxElementsInMemory值是,是否允许溢出到磁盘(必须设置)(内存不足时,是否启用磁盘缓存。)
diskSpoolBufferSizeMB: 这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。 
diskPersistent="false": 磁盘缓存在JVM重新启动时是否保持(默认为false)
timeToIdleSeconds="0": 导致元素过期的访问间隔(秒为单位),即当缓存闲置n秒后销毁。 当eternal为false时,这个属性才有效,0表示可以永远空闲,默认为0
timeToLiveSeconds="600": 元素在缓存里存在的时间(秒为单位),即当缓存存活n秒后销毁. 0 表示永远存在不过期
memoryStoreEvictionPolicy="LFU": 当达到maxElementsInMemory时,如何强制进行驱逐默认使用"最近使用(LRU)"策略,其它还有先入先出FIFO,最少使用LFU,较少使用LRU
diskExpiryThreadIntervalSeconds :磁盘失效线程运行时间间隔,默认是120秒。
clearOnFlush: 内存数量最大时是否清除。

如:

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="ehcache.xsd"
         updateCheck="true" monitoring="autodetect"
         dynamicConfig="true">   
    <diskStore path="java.io.tmpdir" />
    <cache name="ServerListCache"
           maxEntriesLocalHeap="1000"
           maxEntriesLocalDisk="10000"
           eternal="false"
           diskSpoolBufferSizeMB="20"
           timeToIdleSeconds="300"
           timeToLiveSeconds="360"
           memoryStoreEvictionPolicy="LFU"
           transactionalMode="off">
        <persistence strategy="localTempSwap" />
    </cache>
    <cache name="ServerQueryCache"
           maxEntriesLocalHeap="1000"
           maxEntriesLocalDisk="10000"
           eternal="false"
           diskSpoolBufferSizeMB="20"
           timeToIdleSeconds="300"
           timeToLiveSeconds="360"
           memoryStoreEvictionPolicy="LFU"
           transactionalMode="off">
        <persistence strategy="localTempSwap" />
    </cache>
</ehcache>

接着在service的实现类使用缓存注解:

    // key指定时,即为方法中的指定参数
    @Cacheable(value = "ServerListCache", key = "#userId")
    public List<ServerListDto> getServerList(String userId) {
        ...
        return serverList;
    }
    // key默认时,即为方法中的所有参数
    @Cacheable(value = "ServerQueryCache")
    public List<ServerQueryDto> queryServer(String userId, Integer currentPage, Integer pageSize, String searchString) {
        ...
        return serverQueryDtos;
    }

结果:做查询时,缓存中没有数据则会查询数据库,缓存中有数据时则会跳过查询数据库,直接在缓存中拿数据。

你可能感兴趣的:(我的工作笔记,四.Java项目篇)