0°

Spring 缓存

缓存就是数据交换的缓冲区(称作Cache),当某一硬件要读取数据时,会首先从缓存中查找需要的数据,如果找到了则直接执行,找不到的话则从内存中找。由于缓存的运行速度比内存快得多,故缓存的作用就是帮助硬件更快地运行。

spring从3.1开始支持缓存功能。spring 自带的缓存机制它只在方法上起作用,对于你使用其他持久化层的框架来讲,是没有影响的。


Spring 缓存的特点

  • 通过少量的配置 annotation 注释即可使得既有代码支持缓存。
  • 支持开箱即用 Out-Of-The-Box,即不用安装和部署额外第三方组件即可使用缓存。
  • 支持 Spring Express Language,能使用对象的任何属性或者方法来定义缓存的 key 和 condition。
  • 支持 AspectJ,并通过其实现任何方法的缓存支持。
  • 支持自定义 key 和自定义缓存管理者,具有相当的灵活性和扩展性。

Spring 缓存的基本原理

和 spring 的事务管理类似,spring cache 的关键原理就是 spring AOP,通过 spring AOP,其实现了在方法调用前、调用后获取方法的入参和返回值,进而实现了缓存的逻辑。我们来看一下下面这个图:

上图显示,当客户端“Calling code”调用一个普通类 Plain Object 的 foo() 方法的时候,是直接作用在 pojo 类自身对象上的,客户端拥有的是被调用者的直接的引用。
而 Spring cache 利用了 Spring AOP 的动态代理技术,即当客户端尝试调用 pojo 的 foo()方法的时候,给他的不是 pojo 自身的引用,而是一个动态生成的代理类。

如上图所示,这个时候,实际客户端拥有的是一个代理的引用,那么在调用 foo() 方法的时候,会首先调用 proxy 的 foo() 方法,这个时候 proxy 可以整体控制实际的 pojo.foo() 方法的入参和返回值,比如缓存结果,比如直接略过执行实际的 foo() 方法等,都是可以轻松做到的。

Spring 缓存的注解

@Cacheable

主要针对方法配置,能够根据方法的请求参数对其结果进行缓存
value
缓存的名称,在 spring 配置文件中定义,必须指定至少一个


1
2
3
1@Cacheable(value=”mycache”)
2@Cacheable(value={”cache1”,”cache2”}
3

key
因为缓存都是key-value形式的,value就是返回值,而key可以有各种各样的,所以必须有一个key的生成策略,如果不指定key,Spring会帮我们生成;默认生成策略如下:

  • 如果方法没有参数,则使用SimpleKey.EMPTY作为key。
  • 如果只有一个参数的话则使用该参数作为key。
  • 如果参数多于一个的话则使用所有参数的hashCode作为key。

可以通过Spring的EL表达式来指定我们的key。这里的EL表达式可以使用方法参数及它们对应的属性。使用方法参数时我们可以直接使用“#参数名”或者“#p参数index”。


1
2
3
4
5
1// 将isbn作为key
2@Cacheable(cacheNames="books", key="#isbn")
3//或者 @Cacheable(cacheNames="books", key="#p0")
4public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
5

condition
并不一定每次都需要缓存,可以根据参数来确定是否缓存,Spring提供了condition参数,可以为空,使用 SpEL 编写。通过判断true或者false来决定是否执行方法。


1
2
3
1Cacheable(cacheNames="book", condition="#name.length() < 32", unless="#result.hardback")
2public Book findBook(String name)
3

@CachePut(参数同上)

主要针对方法配置,能够根据方法的请求参数对其结果进行缓存,和 @Cacheable 不同的是,它每次都会触发真实方法的调用

@CacheEvict (参数同上)

主要针对方法配置,能够根据一定的条件对缓存进行清空
allEntries
是否清空所有缓存内容,缺省为 false,如果指定为 true,则方法调用后将立即清空所有缓存。


1
2
1@CachEvict(value=”testcache”,allEntries=true)
2

beforeInvocation
是否在方法执行前就清空,缺省为 false,如果指定为 true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存。


1
2
1@CachEvict(value=”testcache”,beforeInvocation=true)
2

@Caching

当一个类型有多个注解的时候使用


1
2
3
1@Caching(evict = { @CacheEvict("primary"), @CacheEvict(cacheNames="secondary", key="#p0") })
2public Book importBooks(String deposit, Date date)
3

第一个@CacheEvict(“primary”)使用默认值清除名称为primary的缓存,第二个@CacheEvict(cacheNames=”secondary”, key=”#p0”)表示importBooks(String deposit, Date date)的第一个参数作为key清除掉名称为secondary的缓存

@CacheConfig

每次写的时候都需要指定cacheNames,如果都使用同一个cacheNames,可以使用@CacheConfig,将其放在类上


1
2
3
4
5
6
1@CacheConfig("books")
2public class BookRepositoryImpl implements BookRepository {
3    @Cacheable
4    public Book findBook(ISBN isbn) {...}
5}
6

Spring 缓存的配置

  • 开启注解


1
2
1<cache:annotation-driven />
2
  • CacheManager和Cache分别用Bean配置


1
2
3
4
5
6
7
8
9
10
11
12
1<!-- simple cache manager -->
2<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
3    <property name="caches">
4        <set>
5            <!-- 默认的缓存 -->
6            <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="default"/>
7            <!-- 例子中一直使用的"books",也就是那个cacheNames="books" -->
8            <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="books"/>
9        </set>
10    </property>
11</bean>
12

Ehcache

Ehcache简介

EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,是hibernate中默认的CacheProvider。

Ehcache是一种广泛使用的开源Java分布式缓存。主要面向通用缓存,Java EE和轻量级容器。它具有内存和磁盘存储,缓存加载器,缓存扩展,缓存异常处理程序,一个gzip缓存servlet过滤器,支持REST和SOAP api等特点。

Ehcache最初是由Greg Luck于2003年开始开发。2009年,该项目被Terracotta购买。软件仍然是开源,但一些新的主要功能(例如,快速可重启性之间的一致性的)只能在商业产品中使用,例如Enterprise EHCache and BigMemory。维基媒体Foundationannounced目前使用的就是Ehcache技术。

总之Ehcache还是一个不错的缓存技术,我们来看看spring搭配Ehcache是如何实现的。
下载地址:http://www.ehcache.org/generated/2.10.1/html/ehc-all/

ehcache.xml配置


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1<?xml version="1.0" encoding="UTF-8"?>  
2<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">  
3    <diskStore path="java.io.tmpdir/ehcache"/>  
4    <!-- 默认缓存 -->  
5    <defaultCache  
6           maxElementsInMemory="1000"  
7           eternal="false"  
8           timeToIdleSeconds="120"  
9           timeToLiveSeconds="120"  
10           overflowToDisk="false"/>  
11    <!-- 菜单缓存 -->      
12    <cache name="menuCache"  
13           maxElementsInMemory="1000"  
14           eternal="false"  
15           timeToIdleSeconds="120"  
16           timeToLiveSeconds="120"  
17           overflowToDisk="false"  
18           memoryStoreEvictionPolicy="LRU"/>      
19</ehcache>  
20

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的使用

以Java配置的方式设置EhCacheManager


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
1import org.springframework.cache.annotation.EnableCaching;  
2import org.springframework.cache.ehcache.EhCacheCacheManager;  
3import org.springframework.cache.ehcache.EhCacheManagerFactoryBean;  
4import org.springframework.context.annotation.Bean;  
5import org.springframework.context.annotation.Configuration;  
6import org.springframework.core.io.ClassPathResource;  
7import net.sf.ehcache.CacheManager;  
8@Configuration  
9@EnableCaching//启用缓存  
10public class CachingConfig {  
11    /**
12     * @param cacheManager 是 net.sf.ehcache.CacheManager的一个实例
13     * 配置EhCacheCacheManager缓存管理器
14     */  
15    @Bean  
16    public EhCacheCacheManager cacheManager(CacheManager cacheManager) {  
17        return new EhCacheCacheManager(cacheManager);  
18    }  
19    @Bean  
20    public EhCacheManagerFactoryBean ehcache() {  
21        EhCacheManagerFactoryBean ehCacheManagerFactoryBean = new EhCacheManagerFactoryBean();  
22        ehCacheManagerFactoryBean.setConfigLocation(new ClassPathResource(&quot;**/**/ehcache.xml&quot;));  
23        return ehCacheManagerFactoryBean;  
24    }  
25}  
26

以XML配置的方式设置EhCacheManager


1
2
3
4
5
6
7
8
9
10
11
1&lt;!-- 启用缓存 --&gt;  
2    &lt;cache:annotation-driven cache-manager =&quot;cacheManager&quot; /&gt;  
3    &lt;!--声明一个缓存管理器(EhCacheCacheManager) 这里的实现代码是通过传入EhCache的CacheManager实例实现的 --&gt;  
4    &lt;bean id=&quot;cacheManager&quot; class=&quot;org.springframework.cache.ehcache.EhCacheCacheManager&quot; p:cache-manager-ref=&quot;ehcache&quot;/&gt;  
5    &lt;!--这里并不是EhCacheManagerFactoryBean的实例,而是EhCache中CacheManager的一个实例  --&gt;  
6    &lt;!--因为Spring和EhCache都定义了CacheManager类型  --&gt;  
7    &lt;bean id=&quot;ehcache&quot; class=&quot;org.springframework.cache.ehcache.EhCacheManagerFactoryBean&quot;&gt;  
8        &lt;property name=&quot;configLocation&quot; value=&quot;classpath:/ehcache.xml&quot; /&gt;  
9        &lt;property name=&quot;shared&quot; value=&quot;true&quot;/&gt;  
10&lt;/bean&gt;  
11

Spring 整合 Ehcache

1.需要的jar包

  • slf4j-api-1.6.1.jar
  • ehcache-core-2.1.0.jar
  • ehcache-spring-annotations-1.1.2.jar
  • slf4j-log4j12-1.6.1.jar
  • spring-context-support-4.0.6.RELEASE.jar

2.ehcache.xml


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;  
2&lt;ehcache xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; xsi:noNamespaceSchemaLocation=&quot;http://ehcache.org/ehcache.xsd&quot;&gt;  
3    &lt;diskStore path=&quot;java.io.tmpdir/ehcache&quot;/&gt;  
4    &lt;!-- 默认缓存 --&gt;  
5    &lt;defaultCache  
6           maxElementsInMemory=&quot;1000&quot;  
7           eternal=&quot;false&quot;  
8           timeToIdleSeconds=&quot;120&quot;  
9           timeToLiveSeconds=&quot;120&quot;  
10           overflowToDisk=&quot;false&quot;/&gt;  
11    &lt;!-- 菜单缓存 --&gt;      
12    &lt;cache name=&quot;menuCache&quot;  
13           maxElementsInMemory=&quot;1000&quot;  
14           eternal=&quot;false&quot;  
15           timeToIdleSeconds=&quot;120&quot;  
16           timeToLiveSeconds=&quot;120&quot;  
17           overflowToDisk=&quot;false&quot;  
18           memoryStoreEvictionPolicy=&quot;LRU&quot;/&gt;  
19&lt;/ehcache&gt;  
20

3.application_spring_cache.xml


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;  
2&lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;  
3    xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;  
4    xmlns:context=&quot;http://www.springframework.org/schema/context&quot;  
5    xmlns:cache=&quot;http://www.springframework.org/schema/cache&quot;  
6    xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans  
7        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
8        http://www.springframework.org/schema/context  
9        http://www.springframework.org/schema/context/spring-context-3.0.xsd  
10        http://www.springframework.org/schema/cache  
11        http://www.springframework.org/schema/cache/spring-cache-3.2.xsd&quot;&gt;  
12    &lt;cache:annotation-driven cache-manager=&quot;cacheManager&quot;/&gt;  
13     &lt;bean id=&quot;cacheManagerFactory&quot; class=&quot;org.springframework.cache.ehcache.EhCacheManagerFactoryBean&quot;&gt;  
14        &lt;property name=&quot;configLocation&quot; value=&quot;classpath:application/ehcache.xml&quot; /&gt;  
15    &lt;/bean&gt;  
16    &lt;bean id=&quot;cacheManager&quot; class=&quot;org.springframework.cache.ehcache.EhCacheCacheManager&quot;&gt;      
17        &lt;property name=&quot;cacheManager&quot;  ref=&quot;cacheManagerFactory&quot;/&gt;      
18    &lt;/bean&gt;  
19&lt;/beans&gt;  
20

4.使用


1
2
3
4
5
6
7
8
9
1@Cacheable(value=&quot;menuCache&quot;,key=&quot;&#x27;UserMenuKey&#x27;+#userid&quot;)  
2    public List&lt;MenuBean&gt; queryMenuByUserId(String userid)  {  
3        ……  
4    }  
5@CacheEvict(value=&quot;menuCache&quot;,key=&quot;&#x27;UserMenuKey&#x27;+#userRoleBean.getUserId()&quot;)  
6    public boolean delUserRole(UserRoleBean userRoleBean) {  
7        ……  
8    }
9

缓存 的适应场景

数据不会被第三方修改

一般情况下,会被hibernate以外修改的数据最好不要配置二级缓存,以免引起不一致的数据。但是如果此数据因为性能的原因需要被缓存,同时又有可能被第3方比如SQL修改,也可以为其配置二级缓存。只是此时需要在sql执行修改后手动调用cache的清除方法。以保证数据的一致性 。

数据大小在可接收范围之内

如果数据表数据量特别巨大,此时不适合于二级缓存。原因是缓存的数据量过大可能会引起内存资源紧张,反而降低性能。
如果数据表数据量特别巨大,但是经常使用的往往只是较新的那部分数据。此时,也可为其配置二级缓存。但是必须单独配置其持久化类的缓存策略,比如最大缓存数、缓存过期时间等,将这些参数降低至一个合理的范围(太高会引起内存资源紧张,太低了缓存的意义不大)。

数据更新频率低

对于数据更新频率过高的数据,频繁同步缓存中数据的代价可能和 查询缓存中的数据从中获得的好处相当,坏处益处相抵消。此时缓存的意义也不大。

非关键数据

财务数据等是非常重要的数据,绝对不允许出现或使用无效的数据,所以此时为了安全起见最好不要使用二级缓存。

「点点赞赏,手留余香」

    还没有人赞赏,快来当第一个赞赏的人吧!