从3.1开始,Spring引入了对Cache的支持。其使用方法和原理都相似于Spring对事务管理的支持。Spring Cache是做用在方法上的,其核心思想是这样的:当咱们在调用一个缓存方法时会把该方法参数和返回结果做为一个键值对存放在缓存中,等到下次利用一样的参数来调用该方法时将再也不执行该方法,而是直接从缓存中获取结果进行返回。因此在使用Spring Cache的时候咱们要保证咱们缓存的方法对于相同的方法参数要有相同的返回结果。spring
使用Spring Cache须要咱们作两方面的事:数组
声明某些方法使用缓存缓存
配置Spring对Cache的支持spa
和Spring对事务管理的支持同样,Spring对Cache的支持也有基于注解和基于XML配置两种方式。下面咱们先来看看基于注解的方式。代理
Spring为咱们提供了几个注解来支持Spring Cache。其核心主要是@Cacheable和@CacheEvict。使用@Cacheable标记的方法在执行后Spring Cache将缓存其返回结果,而使用@CacheEvict标记的方法会在方法执行前或者执行后移除Spring Cache中的某些元素。下面咱们未来详细介绍一下Spring基于注解对Cache的支持所提供的几个注解。orm
@Cacheable能够标记在一个方法上,也能够标记在一个类上。当标记在一个方法上时表示该方法是支持缓存的,当标记在一个类上时则表示该类全部的方法都是支持缓存的。对于一个支持缓存的方法,Spring会在其被调用后将其返回值缓存起来,以保证下次利用一样的参数来执行该方法时能够直接从缓存中获取结果,而不须要再次执行该方法。Spring在缓存方法的返回值时是以键值对进行缓存的,值就是方法的返回结果,至于键的话,Spring又支持两种策略,默认策略和自定义策略,这个稍后会进行说明。须要注意的是当一个支持缓存的方法在对象内部被调用时是不会触发缓存功能的。@Cacheable能够指定三个属性,value、key和condition。xml
value属性是必须指定的,其表示当前方法的返回值是会被缓存在哪一个Cache上的,对应Cache的名称。其能够是一个Cache也能够是多个Cache,当须要指定多个Cache时其是一个数组。对象
@Cacheable("cache1")//Cache是发生在cache1上的接口
public User find(Integer id) {事务
returnnull;
}
@Cacheable({"cache1", "cache2"})//Cache是发生在cache1和cache2上的
public User find(Integer id) {
returnnull;
}
key属性是用来指定Spring缓存方法的返回结果时对应的key的。该属性支持SpringEL表达式。当咱们没有指定该属性时,Spring将使用默认策略生成key。咱们这里先来看看自定义策略,至于默认策略会在后文单独介绍。
自定义策略是指咱们能够经过Spring的EL表达式来指定咱们的key。这里的EL表达式可使用方法参数及它们对应的属性。使用方法参数时咱们能够直接使用“#参数名”或者“#p参数index”。下面是几个使用参数做为key的示例。
@Cacheable(value="users", key="#id")
public User find(Integer id) {
returnnull;
}
@Cacheable(value="users", key="#p0")
public User find(Integer id) {
returnnull;
}
@Cacheable(value="users", key="#user.id")
public User find(User user) {
returnnull;
}
@Cacheable(value="users", key="#p0.id")
public User find(User user) {
returnnull;
}
当咱们要使用root对象的属性做为key时咱们也能够将“#root”省略,由于Spring默认使用的就是root对象的属性。如:
@Cacheable(value={"users", "xxx"}, key="caches[1].name")
public User find(User user) {
returnnull;
}
有的时候咱们可能并不但愿缓存一个方法全部的返回结果。经过condition属性能够实现这一功能。condition属性默认为空,表示将缓存全部的调用情形。其值是经过SpringEL表达式来指定的,当为true时表示进行缓存处理;当为false时表示不进行缓存处理,即每次调用该方法时该方法都会执行一次。以下示例表示只有当user的id为偶数时才会进行缓存。
@Cacheable(value={"users"}, key="#user.id", condition="#user.id%2==0")
public User find(User user) {
System.out.println("find user by user " + user);
return user;
}
在支持Spring Cache的环境下,对于使用@Cacheable标注的方法,Spring在每次执行前都会检查Cache中是否存在相同key的缓存元素,若是存在就再也不执行该方法,而是直接从缓存中获取结果进行返回,不然才会执行并将返回结果存入指定的缓存中。@CachePut也能够声明一个方法支持缓存功能。与@Cacheable不一样的是使用@CachePut标注的方法在执行前不会去检查缓存中是否存在以前执行过的结果,而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中。
@CachePut也能够标注在类上和方法上。使用@CachePut时咱们能够指定的属性跟@Cacheable是同样的。
@CachePut("users")//每次都会执行方法,并将结果存入指定的缓存中
public User find(Integer id) {
returnnull;
}
@CacheEvict是用来标注在须要清除缓存元素的方法或类上的。当标记在一个类上时表示其中全部的方法的执行都会触发缓存的清除操做。@CacheEvict能够指定的属性有value、key、condition、allEntries和beforeInvocation。其中value、key和condition的语义与@Cacheable对应的属性相似。即value表示清除操做是发生在哪些Cache上的(对应Cache的名称);key表示须要清除的是哪一个key,如未指定则会使用默认策略生成的key;condition表示清除操做发生的条件。下面咱们来介绍一下新出现的两个属性allEntries和beforeInvocation。
allEntries是boolean类型,表示是否须要清除缓存中的全部元素。默认为false,表示不须要。当指定了allEntries为true时,Spring Cache将忽略指定的key。有的时候咱们须要Cache一下清除全部的元素,这比一个一个清除元素更有效率。
@CacheEvict(value="users", allEntries=true)
public void delete(Integer id) {
System.out.println("delete user by id: " + id);
}
清除操做默认是在对应方法成功执行以后触发的,即方法若是由于抛出异常而未能成功返回时也不会触发清除操做。使用beforeInvocation能够改变触发清除操做的时间,当咱们指定该属性值为true时,Spring会在调用该方法以前清除缓存中的指定元素。
@CacheEvict(value="users", beforeInvocation=true)
public void delete(Integer id) {
System.out.println("delete user by id: " + id);
}
其实除了使用@CacheEvict清除缓存元素外,当咱们使用Ehcache做为实现时,咱们也能够配置Ehcache自身的驱除策略,其是经过Ehcache的配置文件来指定的。因为Ehcache不是本文描述的重点,这里就很少赘述了,想了解更多关于Ehcache的信息,请查看我关于Ehcache的专栏。
@Caching注解可让咱们在一个方法或者类上同时指定多个Spring Cache相关的注解。其拥有三个属性:cacheable、put和evict,分别用于指定@Cacheable、@CachePut和@CacheEvict。
@Caching(cacheable = @Cacheable("users"), evict = { @CacheEvict("cache2"),
@CacheEvict(value = "cache3", allEntries = true) })
public User find(Integer id) {
returnnull;
}
Spring容许咱们在配置可缓存的方法时使用自定义的注解,前提是自定义的注解上必须使用对应的注解进行标注。如咱们有以下这么一个使用@Cacheable进行标注的自定义注解。
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Cacheable(value="users")
public @interface MyCacheable {
}
那么在咱们须要缓存的方法上使用@MyCacheable进行标注也能够达到一样的效果。
@MyCacheable
public User findById(Integer id) {
System.out.println("find user by id: " + id);
User user = new User();
user.setId(id);
user.setName("Name" + id);
return user;
}
配置Spring对基于注解的Cache的支持,首先咱们须要在Spring的配置文件中引入cache命名空间,其次经过<cache:annotation-driven />就能够启用Spring对基于注解的Cache的支持。
<?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: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/cache
http://www.springframework.org/schema/cache/spring-cache.xsd">
<cache:annotation-driven/>
</beans>
<cache:annotation-driven/>有一个cache-manager属性用来指定当前所使用的CacheManager对应的bean的名称,默认是cacheManager,因此当咱们的CacheManager的id为cacheManager时咱们能够不指定该参数,不然就须要咱们指定了。
<cache:annotation-driven/>还能够指定一个mode属性,可选值有proxy和aspectj。默认是使用proxy。当mode为proxy时,只有缓存方法在外部被调用的时候Spring Cache才会发生做用,这也就意味着若是一个缓存方法在其声明对象内部被调用时Spring Cache是不会发生做用的。而mode为aspectj时就不会有这种问题。另外使用proxy时,只有public方法上的@Cacheable等标注才会起做用,若是须要非public方法上的方法也可使用Spring Cache时把mode设置为aspectj。
此外,<cache:annotation-driven/>还能够指定一个proxy-target-class属性,表示是否要代理class,默认为false。咱们前面提到的@Cacheable、@cacheEvict等也能够标注在接口上,这对于基于接口的代理来讲是没有什么问题的,可是须要注意的是当咱们设置proxy-target-class为true或者mode为aspectj时,是直接基于class进行操做的,定义在接口上的@Cacheable等Cache注解不会被识别到,那对应的Spring Cache也不会起做用了。
须要注意的是<cache:annotation-driven/>只会去寻找定义在同一个ApplicationContext下的@Cacheable等缓存注解。
除了使用注解来声明对Cache的支持外,Spring还支持使用XML来声明对Cache的支持。这主要是经过相似于aop:advice的cache:advice来进行的。在cache命名空间下定义了一个cache:advice元素用来定义一个对于Cache的advice。其须要指定一个cache-manager属性,默认为cacheManager。cache:advice下面能够指定多个cache:caching元素,其有点相似于使用注解时的@Caching注解。cache:caching元素下又能够指定cache:cacheable、cache:cache-put和cache:cache-evict元素,它们相似于使用注解时的@Cacheable、@CachePut和@CacheEvict。下面来看一个示例:
CacheManager是Spring定义的一个用来管理Cache的接口。Spring自身已经为咱们提供了两种CacheManager的实现,一种是基于Java API的ConcurrentMap,另外一种是基于第三方Cache实现——Ehcache,若是咱们须要使用其它类型的缓存时,咱们能够本身来实现Spring的CacheManager接口或AbstractCacheManager抽象类。下面分别来看看Spring已经为咱们实现好了的两种CacheManager的配置示例。
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
<property name="caches">
<set>
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="xxx"/>
</set>
</property>
</bean>
上面的配置使用的是一个SimpleCacheManager,其中包含一个名为“xxx”的ConcurrentMapCache。
<!-- Ehcache实现 -->
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager" p:cache-manager-ref="ehcacheManager"/>
<bean id="ehcacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" p:config-location="ehcache-spring.xml"/>
上面的配置使用了一个Spring提供的EhCacheCacheManager来生成一个Spring的CacheManager,其接收一个Ehcache的CacheManager,由于真正用来存入缓存数据的仍是Ehcache。Ehcache的CacheManager是经过Spring提供的EhCacheManagerFactoryBean来生成的,其能够经过指定ehcache的配置文件位置来生成一个Ehcache的CacheManager。若未指定则将按照Ehcache的默认规则取classpath根路径下的ehcache.xml文件,若该文件也不存在,则获取Ehcache对应jar包中的ehcache-failsafe.xml文件做为配置文件。更多关于Ehcache的内容这里就很少说了,它不属于本文讨论的内容,欲了解更多关于Ehcache的内容能够参考我以前发布的Ehcache系列文章,也能够参考官方文档等。
键的生成策略有两种,一种是默认策略,一种是自定义策略。
默认的key生成策略是经过KeyGenerator生成的,其默认策略以下:
若是方法没有参数,则使用0做为key。
若是只有一个参数的话则使用该参数做为key。
若是参数多余一个的话则使用全部参数的hashCode做为key。
若是咱们须要指定本身的默认策略的话,那么咱们能够实现本身的KeyGenerator,而后指定咱们的Spring Cache使用的KeyGenerator为咱们本身定义的KeyGenerator。
使用基于注解的配置时是经过cache:annotation-driven指定的.
<cache:annotation-driven key-generator="userKeyGenerator"/>
<bean id="userKeyGenerator" class="com.xxx.cache.UserKeyGenerator"/>
而使用基于XML配置时是经过cache:advice来指定的。
<cache:advice id="cacheAdvice" cache-manager="cacheManager" key-generator="userKeyGenerator">
</cache:advice>
须要注意的是此时咱们全部的Cache使用的Key的默认生成策略都是同一个KeyGenerator。
自定义策略是指咱们能够经过Spring的EL表达式来指定咱们的key。这里的EL表达式可使用方法参数及它们对应的属性。使用方法参数时咱们能够直接使用“#参数名”或者“#p参数index”。下面是几个使用参数做为key的示例。
@Cacheable(value="users", key="#id")
public User find(Integer id) {
returnnull;
}
@Cacheable(value="users", key="#p0")
public User find(Integer id) {
returnnull;
}
@Cacheable(value="users", key="#user.id")
public User find(User user) {
returnnull;
}
@Cacheable(value="users", key="#p0.id")
public User find(User user) {
returnnull;
}
除了上述使用方法参数做为key以外,Spring还为咱们提供了一个root对象能够用来生成key。经过该root对象咱们能够获取到如下信息。
<cache:advice id="cacheAdvice" cache-manager="cacheManager">
<cache:caching cache="users">
<cache:cacheable method="findById" key="#p0"/>
<cache:cacheable method="find" key="#user.id"/>
<cache:cache-evict method="deleteAll" all-entries="true"/>
</cache:caching>
</cache:advice>
上面配置定义了一个名为cacheAdvice的cache:advice,其中指定了将缓存findById方法和find方法到名为users的缓存中。这里的方法还可使用通配符“*”,好比“find*”表示任何以“find”开始的方法。
有了cache:advice以后,咱们还须要引入aop命名空间,而后经过aop:config指定定义好的cacheAdvice要应用在哪些pointcut上。如:
<aop:config proxy-target-class="false">
<aop:advisor advice-ref="cacheAdvice" pointcut="execution(* com.xxx.UserService.*(..))"/>
</aop:config>
上面的配置表示在调用com.xxx.UserService中任意公共方法时将使用cacheAdvice对应的cache:advice来进行Spring Cache处理。更多关于Spring Aop的内容不在本文讨论范畴内。
前面介绍的内容是Spring内置的对Cache的支持,其实咱们也能够经过Spring本身单独的使用Ehcache的CacheManager或Ehcache对象。经过在Application Context中配置EhCacheManagerFactoryBean和EhCacheFactoryBean,咱们就能够把对应的EhCache的CacheManager和Ehcache对象注入到其它的Spring bean对象中进行使用。
EhCacheManagerFactoryBean是Spring内置的一个能够产生Ehcache的CacheManager对象的FactoryBean。其能够经过属性configLocation指定用于建立CacheManager的Ehcache配置文件的路径,一般是ehcache.xml文件的路径。若是没有指定configLocation,则将使用默认位置的配置文件建立CacheManager,这是属于Ehcache自身的逻辑,即若是在classpath根路径下存在ehcache.xml文件,则直接使用该文件做为Ehcache的配置文件,不然将使用ehcache-xxx.jar中的ehcache-failsafe.xml文件做为配置文件来建立Ehcache的CacheManager。此外,若是不但愿建立的CacheManager使用默认的名称(在ehcache.xml文件中定义的,或者是由CacheManager内部定义的),则能够经过cacheManagerName属性进行指定。下面是一个配置EhCacheManagerFactoryBean的示例。
<!-- 定义CacheManager -->
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<!-- 指定配置文件的位置 -->
<property name="configLocation" value="/WEB-INF/config/ehcache.xml"/>
<!-- 指定新建的CacheManager的名称 -->
<property name="cacheManagerName" value="cacheManagerName"/>
</bean>
EhCacheFactoryBean是用来产生Ehcache的Ehcache对象的FactoryBean。定义EhcacheFactoryBean时有两个很重要的属性咱们能够来指定。一个是cacheManager属性,其能够指定将用来获取或建立Ehcache的CacheManager对象,若未指定则将经过CacheManager.create()获取或建立默认的CacheManager。另外一个重要属性是cacheName,其表示当前EhCacheFactoryBean对应的是CacheManager中的哪个Ehcache对象,若未指定默认使用beanName做为cacheName。若CacheManager中不存在对应cacheName的Ehcache对象,则将使用CacheManager建立一个名为cacheName的Cache对象。此外咱们还能够经过EhCacheFactoryBean的timeToIdle、timeToLive等属性指定要建立的Cache的对应属性,注意这些属性只对CacheManager中不存在对应Cache时新建的Cache才起做用,对已经存在的Cache将不起做用,更多属性设置请参考Spring的API文档。此外还有几个属性是对不论是已经存在仍是新建立的Cache都起做用的属性:statisticsEnabled、sampledStatisticsEnabled、disabled、blocking和cacheEventListeners,其中前四个默认都是false,最后一个表示为当前Cache指定CacheEventListener。下面是一个定义EhCacheFactoryBean的示例。
<!-- 定义CacheManager -->
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<!-- 指定配置文件的位置 -->
<property name="configLocation" value="/WEB-INF/config/ehcache.xml"/>
<!-- 指定新建的CacheManager的名称 -->
<property name="cacheManagerName" value="cacheManagerName"/>
</bean>
<!-- 定义一个Ehcache -->
<bean id="userCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<property name="cacheName" value="user"/>
<property name="cacheManager" ref="cacheManager"/>
</bean>