前言
缓存可以存储经常用到的数据到内存或者落地到文件中,这样在用的时候就可以快速的、不用经过计算就能拿到数据。
缓存有利也有弊,因为数据被缓存起来了,所以在时效性方面就不是很好了,所以在使用缓存的时候,应该分析一下适合的场景,并且要有一定的管理策略。
spring自身并没有实现缓存解决方案,但是它对缓存功能提供了声明式的支持,能够与多种流行的缓存实现集成。
特点:
1. 支持XML和annotation注解,使用方便。
2. 支持开箱即用 Out-Of-The-Box,不用安装其它应用服务。
3. 支持 Spring Express Language,能使用对象的任何属性或者方法来定义缓存的 key 和 condition
4. 具有相当的灵活性和扩展性。
了解SpringCache
Spring对缓存的支持有两种方式:
- 注解驱动的缓存:这种方式是当前最流行的、也是使用起来比较便捷的一种方式。
- XML配置的缓存:xml方式的配置虽然比较古老了,但是它也有自身的一些优点,比如在一些第三方jar包,我们不能在它的代码里面添加注解,就可以使用xml对相应的路径、类进行一些匹配。
缓存管理器是Spring缓存抽象的核心,它能够与多个流行的缓存实现集成。
Spring3.1内置了五个缓存管理器实现:
- SimpleCacheManager
- NoOpCacheManager
- ConcurrentMapCacheManager
- CompositeCacheManager
- EhCacheCacheManager
Spring3.2引入了另外一个缓存管理器,这个管理器可以用在基于Jcache(JSR-107)的缓存供应商之中。除了核心的spring框架,spring data又提供了两个缓存管理器:
- RedisCacheManager
- GemfireCacheManager
JSR是Java Specification Requests的缩写,意思是Java 规范提案。是指向JCP(Java Community Process)提出新增一个标准化技术规范的正式请求。任何人都可以提交JSR,以向Java平台增添新的API和服务。JSR已成为Java界的一个重要标准。
spring3.2在线源码
可以看到,在为spring的缓存抽象选择缓存管理器时,我们有很多可选方案。具体的选择要看我们需要的应用场景,根据我们的需求选择合适的供应商,然后在使用匹配的缓存管理器。
为方法添加注解
spring提供了4个注解来声明缓存规则
注解 | 描述 |
---|---|
@Cacheable | 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存 |
@CachePut | 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存,和 @Cacheable 不同的是,它每次都会触发真实方法的调用 |
@CacheEvict | 主要针对方法配置,能够根据一定的条件对缓存进行清空 |
@Caching | 这是一个分组的注解,能够同时应用多个其他的缓存注解 |
1. @Cacheable
- value:缓存名,必填。
- key:可选属性,可以使用SPEL标签自定义缓存的key。
- condition:属性指定发生的条件。
@Cacheable(value="cache1",key=#id)
public User find(int id) {
return getUser(id);
}
<!--只保存object_type=0的返回信息-->
@Cacheable(value="cache1",key="#id",condition="#object_type==0")
public JsonArray getCredit(int id,String object_type) {
return getUser(id);
}
2. @CachePut
- value:缓存名,必填。
- key:可选属性,可以使用SPEL标签自定义缓存的key。
//每次都会执行方法,并将结果存入指定的缓存中,当用户注册完,在另一个方法就可以直接从相应的缓存中取出用户信息而不需要再查询数据库
@CachePut(value="users",key="#user.id")
public User register(User user) {
return user;
}
@Cacheable(value="users",key="#id")
public User getUserInfo(String id) {
return find(id);
}
3. CacheEvict
- value:必填
- key:可选(默认是所有参数的组合)
- condition:缓存的条件
- allEntries:是否清空所有缓存内容,默认为 false,如果指定为 true,则方法调用后将立即清空所有缓存。
- beforeInvocation:是否在方法执行前就清空,缺省为 false,如果指定为 true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存。
注释来标记要清空缓存的方法,当这个方法被调用后,即会清空缓存。
@CacheEvict(value="accountCache",key="#account.getName()")// 清空accountCache 中指定key值的缓存
public void updateAccount(Account account) {
updateDB(account);
}
@CacheEvict(value="accountCache",allEntries=true)// 清空accountCache 缓存
public void reload() {
reloadAll()
}
4. Caching
在同一个方法上可以同时使用多种注解
@Caching(evict={@CacheEvict(“a1”),@CacheEvict(“a2”,allEntries=true)})
caching注解的源码如下,可以看到我们可以同时使用(cacheable/put/evict方法)
public @interface Caching {
Cacheable[] cacheable() default {};
CachePut[] put() default {};
CacheEvict[] evict() default {};
}
Spring还为我们提供了一个root对象可以用来生成key。通过该root对象我们可以获取到以下信息。
属性名称 | 描述 | 示例 |
---|---|---|
methodName | 当前方法名 | root.methodName |
method | 当前被调用的对象 | root.method.name |
target | 当前被调用的对象的class | root.target |
targetClass | 当前被调用的对象的class | root.targetClass |
args | 当前方法参数组成的数组 | root.args[0] |
caches | 当前被调用的方法使用的Cache | root.caches[0].name |
当我们要使用root对象的属性作为key时我们也可以将“#root”省略,因为Spring默认使用的就是root对象的属性。如:
@Cacheable(value={"users", "xxx"}, key="caches[1].name")
public User find(User user) {
returnnull;
}
使用Ehcache缓存
EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认的CacheProvider,也是JAVA领域应用最为广泛的缓存。
使用配置
需要的依赖包
spring依赖:spring-context、spring-context-support
ehcache:ehcache
<!-- 缓存配置 -->
<cache:annotation-driven cache-manager="cacheManager" proxy-target-class="true"/>
<!-- Ehcache实现 -->
<bean id="cacheManagerFactory" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="classpath:ehcache.xml"/>
</bean>
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
<property name="cacheManager" ref="cacheManagerFactory"/>
</bean>
缓存配置:
<!-- Ehcache2.x的变化(取自https://github.com/springside/springside4/wiki/Ehcache) -->
<!-- 1)最好在ehcache.xml中声明不进行updateCheck -->
<!-- 2)为了配合BigMemory和Size Limit,原来的属性最好改名 -->
<!-- maxElementsInMemory->maxEntriesLocalHeap -->
<!-- maxElementsOnDisk->maxEntriesLocalDisk -->
<ehcache>
<diskStore path="c:/temp"/><!--默认路径java.io.tmpdir-->
<defaultCache
maxElementsInMemory="1000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="false"/>
<cache name="corporate"
maxElementsOnDisk="20000"
maxElementsInMemory="2000"
eternal="true"
overflowToDisk="true"
diskPersistent="true"/>
<cache name="bm"
maxElementsOnDisk="20000"
maxElementsInMemory="2000"
eternal="true"
overflowToDisk="true"
diskPersistent="true"/>
<cache name="resource_info"
maxElementsOnDisk="20000"
maxElementsInMemory="2000"
eternal="true"
overflowToDisk="true"
diskPersistent="true"
timeToLiveSeconds="1800"
/>
</ehcache>
<!--
<diskStore>==========当内存缓存中对象数量超过maxElementsInMemory时,将缓存对象写到磁盘缓存中(需对象实现序列化接口)
<diskStore path="">==用来配置磁盘缓存使用的物理路径,Ehcache磁盘缓存使用的文件后缀名是*.data和*.index
name=================缓存名称,cache的唯一标识(ehcache会把这个cache放到HashMap里)
maxElementsOnDisk====磁盘缓存中最多可以存放的元素数量,0表示无穷大
maxElementsInMemory==内存缓存中最多可以存放的元素数量,若放入Cache中的元素超过这个数值,则有以下两种情况
1)若overflowToDisk=true,则会将Cache中多出的元素放入磁盘文件中
2)若overflowToDisk=false,则根据memoryStoreEvictionPolicy策略替换Cache中原有的元素
eternal==============缓存中对象是否永久有效,即是否永驻内存,true时将忽略timeToIdleSeconds和timeToLiveSeconds
timeToIdleSeconds====缓存数据在失效前的允许闲置时间(单位:秒),仅当eternal=false时使用,默认值是0表示可闲置时间无穷大,此为可选属性
即访问这个cache中元素的最大间隔时间,若超过这个时间没有访问此Cache中的某个元素,那么此元素将被从Cache中清除
timeToLiveSeconds====缓存数据在失效前的允许存活时间(单位:秒),仅当eternal=false时使用,默认值是0表示可存活时间无穷大
即Cache中的某元素从创建到清楚的生存时间,也就是说从创建开始计时,当超过这个时间时,此元素将从Cache中清除
overflowToDisk=======内存不足时,是否启用磁盘缓存(即内存中对象数量达到maxElementsInMemory时,Ehcache会将对象写到磁盘中)
会根据标签中path值查找对应的属性值,写入磁盘的文件会放在path文件夹下,文件的名称是cache的名称,后缀名是data
diskPersistent=======是否持久化磁盘缓存,当这个属性的值为true时,系统在初始化时会在磁盘中查找文件名为cache名称,后缀名为index的文件
这个文件中存放了已经持久化在磁盘中的cache的index,找到后会把cache加载到内存
要想把cache真正持久化到磁盘,写程序时注意执行net.sf.ehcache.Cache.put(Element element)后要调用flush()方法
diskExpiryThreadIntervalSeconds==磁盘缓存的清理线程运行间隔,默认是120秒
diskSpoolBufferSizeMB============设置DiskStore(磁盘缓存)的缓存区大小,默认是30MB
memoryStoreEvictionPolicy========内存存储与释放策略,即达到maxElementsInMemory限制时,Ehcache会根据指定策略清理内存
共有三种策略,分别为LRU(最近最少使用)、LFU(最常用的)、FIFO(先进先出)
-->
启动后加载缓存
从上面的ehcache缓存配置我们知道ehcache支持将缓存落地成文件,这样启动的时候我们就可以先把落地的文件加载到内存里面。
要配置个监听器,在服务关闭的时候将缓存落地成文件
web.xml配置
<!-- ehcache 磁盘缓存 监控,持久化恢复 -->
<listener>
<listenerclass>
net.sf.ehcache.constructs.web.ShutdownListener
</listener-class>
</listener>
在系统启动时,应该添加如下属性
System.setProperty("net.sf.ehcache.enableShutdownHook", "true");
系统关闭的时候会生成一个.index后缀的文件,启动的时候会从这个文件读出数据到内存。
如果直接在IDEA工具按下关闭的按钮是没用的,可能这个按钮不是调用服务正常的关闭命令,移动到tomcat关闭的时候是可以的。