Spring仅仅是提供了对缓存的支持,但它并无任何的缓存功能的实现,spring使用的是第三方的缓存框架来实现缓存的功能。其中,Spring对EHCache提供了很好的支持。html
Spring的缓存机制是基于Spring的AOP,那么在Spring Cache中应该存在着一个Advice。Spring并非直接使用org.springframework.cache.Cache,spring把Cache对象交给org.springframework.cache.CacheManager来管理。java
主要是经过相似于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。spring
<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>
完整的例子:框架
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:cache="http://www.springframework.org/schema/cache" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-3.1.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd "> <context:component-scan base-package="com.sin90lzc" /> <bean id="ehCacheManagerFactory" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" p:configLocation="classpath:ehcache.xml"/> <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager" p:cacheManager-ref="ehCacheManagerFactory"/> <cache:advice id="cacheAdvice" cache-manager="cacheManager"> <cache:caching> <cache:cacheable cache="dao.select" method="select" key="#id" /> <cache:cache-evict cache="dao.select" method="save" key="#obj" /> </cache:caching> </cache:advice> <aop:config> <aop:advisor advice-ref="cacheAdvice" pointcut="execution(* com.sin90lzc.train.spring_cache.simulation.DaoImpl.*(..))"/> </aop:config></beans>
参考:http://www.mincoder.com/article/2096.shtmlide
@Cacheable能够标记在一个方法上,也能够标记在一个类上。当标记在一个方法上时表示该方法是支持缓存的,当标记在一个类上时则表示该类全部的方法都是支持缓存的。spa
对于一个支持缓存的方法,Spring会在其被调用后将其返回值缓存起来,以保证下次利用一样的参数来执行该方法时能够直接从缓存中获取结果,而不须要再次执行该方法。Spring在缓存方法的返回值时是以键值对进行缓存的,值就是方法的返回结果,至于键的话,Spring又支持两种策略,默认策略和自定义策略,这个稍后会进行说明。须要注意的是当一个支持缓存的方法在对象内部被调用时是不会触发缓存功能的。@Cacheable能够指定三个属性,value、key和condition。code
value属性是必须指定的,其表示当前方法的返回值是会被缓存在哪一个Cache上的,对应Cache的名称。其能够是一个Cache也能够是多个Cache,当须要指定多个Cache时其是一个数组。component
例如:
@Cacheable("cache1")//Cache是发生在cache1上的 public User find(Integer id) { return null; } @Cacheable({"cache1", "cache2"})//Cache是发生在cache1和cache2上的 public User find(Integer id) { return null; }
自定义策略是指咱们能够经过Spring的EL表达式来指定咱们的key。这里的EL表达式可使用方法参数及它们对应的属性。使用方法参数时咱们能够直接使用“#参数名”或者“#p参数的索引”。
例如:
@Cacheable(value="users", key="#id") public User find(Integer id) { return null; } @Cacheable(value="users", key="#p0") public User find(Integer id) { return null; } @Cacheable(value="users", key="#user.id") public User find(User user) { return null; } @Cacheable(value="users", key="#p0.id") public User find(User user) { return null; }
除了上述使用方法参数做为key以外,Spring还为咱们提供了一个root对象能够用来生成key。经过该root对象咱们能够获取到如下信息。
当咱们要使用root对象的属性做为key时咱们也能够将“#root”省略,由于Spring默认使用的就是root对象的属性。
例如:
@Cacheable(value={"users", "xxx"}, key="caches[1].name") public User find(User user) { return null; }
有的时候咱们可能并不但愿缓存一个方法全部的返回结果。经过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) { return null; }
@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的配置文件来指定的。
@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) { return null; }
<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。
CacheManager是Spring定义的一个用来管理Cache的接口。Spring自身已经为咱们提供了两种CacheManager的实现,一种是基于Java API的ConcurrentMap,另外一种是基于第三方Cache实现——Ehcache,若是咱们须要使用其它类型的缓存时,咱们能够本身来实现Spring的CacheManager接口或AbstractCacheManager抽象类。
<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的默认规则取classpath根路径下的ehcache.xml文件,若该文件也不存在,则获取Ehcache对应jar包中的ehcache-failsafe.xml文件做为配置文件。
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:cache="http://www.springframework.org/schema/cache" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-3.1.xsd"> <context:component-scan base-package="com.sin90lzc" /> <!-- 该Bean是一个org.springframework.cache.CacheManager对象 属性cacheManager是一个net.sf.ehcache.CacheManager对象 --> <bean id="ehCacheManagerFactory" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" p:configLocation="classpath:ehcache.xml"/> <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager" p:cacheManager-ref="ehCacheManagerFactory"/> <cache:annotation-driven /> </beans>
import org.springframework.cache.annotation.CacheEvict;import org.springframework.cache.annotation.Cacheable;import org.springframework.stereotype.Component; @Componentpublic class DaoImpl implements Dao { /** * value定义了缓存区(缓存区的名字),每一个缓存区能够看做是一个Map对象 * key做为该方法结果缓存的惟一标识, */ @Cacheable(value = { "dao.select" },key="#id") @Override public Object select(int id) { System.out.println("do in function select()"); return new Object(); } @CacheEvict(value = { "dao.select" }, key="#obj") @Override public void save(Object obj) { System.out.println("do in function save(obj)"); } }