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 配置文件中定义,必须指定至少一个

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

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

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

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

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

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

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

@CachePut(参数同上)

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

@CacheEvict (参数同上)

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

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

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

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

@Caching

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

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

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

@CacheConfig

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

@CacheConfig("books")
public class BookRepositoryImpl implements BookRepository {
    @Cacheable
    public Book findBook(ISBN isbn) {...}
}

Spring 缓存的配置

  • 开启注解

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

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

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配置

<?xml version="1.0" encoding="UTF-8"?>  
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">  
    <diskStore path="java.io.tmpdir/ehcache"/>  
    <!-- 默认缓存 -->  
    <defaultCache  
           maxElementsInMemory="1000"  
           eternal="false"  
           timeToIdleSeconds="120"  
           timeToLiveSeconds="120"  
           overflowToDisk="false"/>  
    <!-- 菜单缓存 -->      
    <cache name="menuCache"   
           maxElementsInMemory="1000"   
           eternal="false"  
           timeToIdleSeconds="120"  
           timeToLiveSeconds="120"  
           overflowToDisk="false"   
           memoryStoreEvictionPolicy="LRU"/>       
</ehcache>  

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

import org.springframework.cache.annotation.EnableCaching;  
import org.springframework.cache.ehcache.EhCacheCacheManager;  
import org.springframework.cache.ehcache.EhCacheManagerFactoryBean;  
import org.springframework.context.annotation.Bean;  
import org.springframework.context.annotation.Configuration;  
import org.springframework.core.io.ClassPathResource;  
import net.sf.ehcache.CacheManager;  
@Configuration  
@EnableCaching//启用缓存  
public class CachingConfig {  
    /** 
     * @param cacheManager 是 net.sf.ehcache.CacheManager的一个实例 
     * 配置EhCacheCacheManager缓存管理器 
     */  
    @Bean  
    public EhCacheCacheManager cacheManager(CacheManager cacheManager) {  
        return new EhCacheCacheManager(cacheManager);  
    }  
    @Bean  
    public EhCacheManagerFactoryBean ehcache() {  
        EhCacheManagerFactoryBean ehCacheManagerFactoryBean = new EhCacheManagerFactoryBean();  
        ehCacheManagerFactoryBean.setConfigLocation(new ClassPathResource("**/**/ehcache.xml"));  
        return ehCacheManagerFactoryBean;  
    }  
}  

以XML配置的方式设置EhCacheManager

<!-- 启用缓存 -->  
    <cache:annotation-driven cache-manager ="cacheManager" />  
    <!--声明一个缓存管理器(EhCacheCacheManager) 这里的实现代码是通过传入EhCache的CacheManager实例实现的 -->  
    <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager" p:cache-manager-ref="ehcache"/>  
    <!--这里并不是EhCacheManagerFactoryBean的实例,而是EhCache中CacheManager的一个实例  -->  
    <!--因为Spring和EhCache都定义了CacheManager类型  -->  
    <bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">  
        <property name="configLocation" value="classpath:/ehcache.xml" />  
        <property name="shared" value="true"/>  
</bean>  

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

<?xml version="1.0" encoding="UTF-8"?>  
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">  
    <diskStore path="java.io.tmpdir/ehcache"/>  
    <!-- 默认缓存 -->  
    <defaultCache  
           maxElementsInMemory="1000"  
           eternal="false"  
           timeToIdleSeconds="120"  
           timeToLiveSeconds="120"  
           overflowToDisk="false"/>  
    <!-- 菜单缓存 -->      
    <cache name="menuCache"   
           maxElementsInMemory="1000"   
           eternal="false"  
           timeToIdleSeconds="120"  
           timeToLiveSeconds="120"  
           overflowToDisk="false"   
           memoryStoreEvictionPolicy="LRU"/>  
</ehcache>  

3.application_spring_cache.xml

<?xml version="1.0" encoding="UTF-8"?>  
<beans xmlns="http://www.springframework.org/schema/beans"   
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
    xmlns:context="http://www.springframework.org/schema/context"   
    xmlns:cache="http://www.springframework.org/schema/cache"  
    xsi:schemaLocation="http://www.springframework.org/schema/beans   
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
        http://www.springframework.org/schema/context   
        http://www.springframework.org/schema/context/spring-context-3.0.xsd  
        http://www.springframework.org/schema/cache  
        http://www.springframework.org/schema/cache/spring-cache-3.2.xsd">  
    <cache:annotation-driven cache-manager="cacheManager"/>  
     <bean id="cacheManagerFactory" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">  
        <property name="configLocation" value="classpath:application/ehcache.xml" />  
    </bean>  
    <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">      
        <property name="cacheManager"  ref="cacheManagerFactory"/>      
    </bean>  
</beans>  

4.使用

@Cacheable(value="menuCache",key="'UserMenuKey'+#userid")  
    public List<MenuBean> queryMenuByUserId(String userid)  {  
        ……  
    }  
@CacheEvict(value="menuCache",key="'UserMenuKey'+#userRoleBean.getUserId()")  
    public boolean delUserRole(UserRoleBean userRoleBean) {  
        ……  
    }

缓存 的适应场景

数据不会被第三方修改

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

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

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

数据更新频率低

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

非关键数据

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值