JSR是Java Specification Requests的缩写,意思是Java 规范提案。它定义了一种对Java对象临时在内存中进行缓存的方法,包括对象的创建、共享访问、假脱机(spooling)、失效、各JVM的一致性等,可被用于缓存JSP内最经常读取的数据。
JSR的目标:
Java Caching定义了5个核心接口,分别是CachingProvider, CacheManager, Cache, Entry 和 Expiry。
CachingProvider定义了创建、配置、获取、管理和控制多个CacheManager。一个应用可以在运行期访问多个CachingProvider。
CacheManager定义了创建、配置、获取、管理和控制多个唯一命名的Cache,这些Cache存在于CacheManager的上下文中。一个CacheManager仅被一个CachingProvider所拥有。
Cache是一个类似Map的数据结构并临时存储以Key为索引的值。一个Cache仅被一个CacheManager所拥有。
Entry是一个存储在Cache中的key-value对。
Expiry 每一个存储在Cache中的条目有一个定义的有效期。一旦超过这个时间,条目为过期的状态。一旦过期,条目将不可访问、更新和删除。缓存有效期可以通过ExpiryPolicy设置。
这五个核心接口的关系如下图:
Spring通过Cache和CacheManager两个接口统一了不同的缓存技术,并支持JSR107,大大简化了缓存需求的开发。
Spring Cache核心思想是这样的:当我们在调用一个缓存方法时会把该方法参数和返回结果作为一个键值对存放在缓存中,等到下次利用同样的参数来调用该方法时将不再执行该方法,而是直接从缓存中获取结果进行返回。所以在使用Spring Cache的时候我们要保证我们缓存的方法对于相同的方法参数要有相同的返回结果。
使用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中的某些元素。
step1:导入spring-boot-starter-cache模块
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
step2:@EnableCaching开启缓存
step3:使用缓存注解
EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点。 http://www.ehcache.org/
主要的特性有:
快速
简单
多种缓存策略
缓存数据有两级:内存和磁盘,因此无需担心容量问题
缓存数据会在虚拟机重启的过程中写入磁盘
可以通过RMI、可插入API等方式进行分布式缓存
具有缓存和缓存管理器的侦听接口
支持多缓存管理器实例,以及一个实例的多个缓存区域
提供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;
}
结果:做查询时,缓存中没有数据则会查询数据库,缓存中有数据时则会跳过查询数据库,直接在缓存中拿数据。