Spring使用Cache
从3.1开始,spring引入了对Cache的支持,其使用方法和原理都是类似于Spring对事务管理的支持。SpringCache是作用在方法上的,其核心思想是这样的,当我们在调用一个缓存方法时会把该方法参数和返回结果作为一个键值对存放在缓存中。等下次利用同样的参数来调用方法时将不再执行该方法,而是直接从缓存中获取结果进行返回。所以在使用spring Cache的时候我们要保证我们缓存的方法对于相同的方法参数要有相同的返回结果。
使用spring Cache需要我们做两方面的事;
声明Spring对Cache的支持。
spring对事务管理的支持一样,spring对Cache的支持也有基于注解和基于xml配置两种方法。
1,基于注解的支持。
spring为我们提供了几个注解来支持Spring Cache。其核心主要是@Cacheable和@CacheEvict。使用@Cacheable标记的方法在执行后Spring Cache将缓存其返回结果,而使用@CacheEvivt标记的方法会在方法执行前或者执行后移除spring,Cache中的某些元素。
1.@Cacheable
@Cacheable可以标记在一个方法上,也可以标记在一个类上,当标记在一个方法上时表示方法是支持缓存的,当标记在一个类上时则表示该类所有的方法都支持缓存的,对于一个支持缓存的方法,spring会在其被调用后将其返回值缓存起来。以保证下次利用同样的参数来执行该方法时可以直接从缓存中获取结果,而不需要再次执行该方法。spring在缓存方法的返回值时时以键值对进行缓存的,值就是方法的返回结果,spring又支持两种策略,默认策略和自定义策略。
@Cacheable可以指定三个属性,value,key和condition
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;
}
1.1.2 使用key属性自定义key
key属性是用来指定Spring缓存方法的返回结果时对应的key的。该属性支持SpringEL表达式。当我们没有指定该属性时,Spring将使用默认策略生成key。我们这里先来看看自定义策略,至于默认策略会在后文单独介绍。
自定义策略是指我们可以通过Spring的EL表达式来指定我们的key。这里的EL表达式可以使用方法参数及它们对应的属性。使用方法参数时我们可以直接使用“#参数名”或者“#p参数index”。下面是几个使用参数作为key的示例。
@Cacheable(value="users", key="#id")
public User find(Integer id) {
returnnull;
}
除了上述使用方法参数作为key之外,Spring还为我们提供了一个root对象可以用来生成key。通过该root对象我们可以获取到以下信息。
|
属性名称 |
描述 |
示例 |
|
methodName |
当前方法名 |
#root.methodName |
|
method |
当前方法 |
#root.method.name |
|
target |
当前被调用的对象 |
#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;
}
1.1.3 condition属性指定发生的条件
有的时候我们可能并不希望缓存一个方法所有的返回结果。通过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;
}
1.2 @CachePut
在支持Spring Cache的环境下,对于使用@Cacheable标注的方法,Spring在每次执行前都会检查Cache中是否存在相同key的缓存元素,如果存在就不再执行该方法,而是直接从缓存中获取结果进行返回,否则才会执行并将返回结果存入指定的缓存中。@CachePut也可以声明一个方法支持缓存功能。与@Cacheable不同的是使用@CachePut标注的方法在执行前不会去检查缓存中是否存在之前执行过的结果,而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中。
@CachePut也可以标注在类上和方法上。使用@CachePut时我们可以指定的属性跟@Cacheable是一样的。
@CachePut("users")//每次都会执行方法,并将结果存入指定的缓存中
public User find(Integer id) {
returnnull;
}
1.3 @CacheEvict
@CacheEvict是用来标注在需要清除缓存元素的方法或类上的。当标记在一个类上时表示其中所有的方法的执行都会触发缓存的清除操作。@CacheEvict可以指定的属性有value、key、condition、allEntries和beforeInvocation。其中value、key和condition的语义与@Cacheable对应的属性类似。即value表示清除操作是发生在哪些Cache上的(对应Cache的名称);key表示需要清除的是哪个key,如未指定则会使用默认策略生成的key;condition表示清除操作发生的条件。下面我们来介绍一下新出现的两个属性allEntries和beforeInvocation。
1.3.1 allEntries属性
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);
}
1.3.2 beforeInvocation属性
清除操作默认是在对应方法成功执行之后触发的,即方法如果因为抛出异常而未能成功返回时也不会触发清除操作。使用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的专栏。
1.4 @Caching
@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;
}
1.5 使用自定义注解
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;
}
下面就用项目讲解使用:
2 配置Spring对Cache的支持
2.1 声明对Cache的支持
2.1.1 基于注解
新建一个Maven项目 spring-ehcache
配置pom.xml文件
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.demo.ehcache</groupId> <artifactId>spring-ehcache</artifactId> <packaging>war</packaging> <version>0.0.1-SNAPSHOT</version> <properties> <!-- spring版本号 --> <spring.version>3.2.8.RELEASE</spring.version> <!-- junit版本号 --> <junit.version>4.10</junit.version> </properties> <dependencies> <!-- 添加Spring依赖 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${spring.version}</version> </dependency> <!--单元测试依赖 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> <!--spring单元测试依赖 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> <scope>test</scope> </dependency> <!-- ehcache 相关依赖 --> <dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache</artifactId> <version>2.8.2</version> </dependency> </dependencies> <build> <finalName>spring-ehcache</finalName> </build> </project>
2.1.2 基于注解
ehcache-setting.xml
<?xml version="1.0" encoding="UTF-8"?> <ehcache> <!-- 指定一个文件目录,当EhCache把数据写到硬盘上时,将把数据写到这个文件目录下 --> <diskStore path="java.io.tmpdir"/> <!-- 设定缓存的默认数据过期策略 --> <defaultCache maxElementsInMemory="10000" eternal="false" overflowToDisk="true" timeToIdleSeconds="10" timeToLiveSeconds="20" diskPersistent="false" diskExpiryThreadIntervalSeconds="120"/> <cache name="cacheTest" maxElementsInMemory="1000" eternal="false" overflowToDisk="true" timeToIdleSeconds="10" timeToLiveSeconds="20"/> </ehcache>
这里我们配置了cacheTest策略,10秒过期。
cache元素的属性:
name:缓存名称
maxElementsInMemory:内存中最大缓存对象数
maxElementsOnDisk:硬盘中最大缓存对象数,若是0表示无穷大
eternal:true表示对象永不过期,此时会忽略timeToIdleSeconds和timeToLiveSeconds属性,默认为false
overflowToDisk:true表示当内存缓存的对象数目达到了
maxElementsInMemory界限后,会把溢出的对象写到硬盘缓存中。注意:如果缓存的对象要写入到硬盘中的话,则该对象必须实现了Serializable接口才行。
diskSpoolBufferSizeMB:磁盘缓存区大小,默认为30MB。每个Cache都应该有自己的一个缓存区。
diskPersistent:是否缓存虚拟机重启期数据,是否持久化磁盘缓存,当这个属性的值为true时,系统在初始化时会在磁盘中查找文件名 为cache名称,后缀名为index的文件,这个文件中存放了已经持久化在磁盘中的cache的index,找到后会把cache加载到内存,要想把 cache真正持久化到磁盘,写程序时注意执行net.sf.ehcache.Cache.put(Element element)后要调用flush()方法。
diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认为120秒
timeToIdleSeconds: 设定允许对象处于空闲状态的最长时间,以秒为单位。当对象自从最近一次被访问后,如果处于空闲状态的时间超过了timeToIdleSeconds属性 值,这个对象就会过期,EHCache将把它从缓存中清空。只有当eternal属性为false,该属性才有效。如果该属性值为0,则表示对象可以无限 期地处于空闲状态
timeToLiveSeconds:设定对象允许存在于缓存中的最长时间,以秒为单位。当对象自从被存放到缓存中后,如果处于缓存中的时间超过了 timeToLiveSeconds属性值,这个对象就会过期,EHCache将把它从缓存中清除。只有当eternal属性为false,该属性才有 效。如果该属性值为0,则表示对象可以无限期地存在于缓存中。timeToLiveSeconds必须大于timeToIdleSeconds属性,才有 意义
memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。
2.3、spring配置文件application.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:cache="http://www.springframework.org/schema/cache" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-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.1.xsd"> <!-- 自动扫描注解的bean --> <context:component-scan base-package="com.demo.service" /> <cache:annotation-driven cache-manager="cacheManager" /> <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager"> <property name="cacheManager" ref="ehcache"></property> </bean> <bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"> <property name="configLocation" value="classpath:ehcache-setting.xml"></property> </bean> </beans>
2.4、接口和接口的实现类
package com.demo.service; public interface EhCacheTestService { public String getTimestamp(String param); } package com.demo.service.impl; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import com.demo.service.EhCacheTestService; /** * * @author huojg21442 *这里注解中value=”cacheTest”与ehcache-setting.xml中的cache名称属性值一致。 */ @Service public class EhCacheTestServiceImpl implements EhCacheTestService { @Cacheable(value="cacheTest",key="#param") public String getTimestamp(String param) { Long timestamp = System.currentTimeMillis(); return timestamp.toString(); } }
2.5开始测试(Junit)
package com.demo; import org.junit.runner.RunWith; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; //指定bean注入的配置文件 @ContextConfiguration(locations = { "classpath:application.xml" }) //使用标准的JUnit @RunWith注释来告诉JUnit使用Spring TestRunner @RunWith(SpringJUnit4ClassRunner.class) public class SpringTestCase extends AbstractJUnit4SpringContextTests { }
package com.demo; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import com.demo.service.EhCacheTestService; public class EhCacheTestServiceTest extends SpringTestCase { @Autowired private EhCacheTestService ehCacheTestService; @Test public void getTimestampTest() throws InterruptedException{ System.out.println("第一次调用:" + ehCacheTestService.getTimestamp("param")); Thread.sleep(2000); System.out.println("2秒之后调用:" + ehCacheTestService.getTimestamp("param")); Thread.sleep(3000); System.out.println("3秒之后调用:" + ehCacheTestService.getTimestamp("param")); Thread.sleep(11000); System.out.println("再过11秒之后调用:" + ehCacheTestService.getTimestamp("param")); } }
输出结果:
信息: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@49d7fb83: defining beans [ehCacheTestServiceImpl,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.aop.config.internalAutoProxyCreator,org.springframework.cache.annotation.AnnotationCacheOperationSource#0,org.springframework.cache.interceptor.CacheInterceptor#0,org.springframework.cache.config.internalCacheAdvisor,cacheManager,ehcache,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor]; root of factory hierarchy 十二月 25, 2017 11:35:10 上午 org.springframework.cache.ehcache.EhCacheManagerFactoryBean afterPropertiesSet 信息: Initializing EhCache CacheManager SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder". SLF4J: Defaulting to no-operation (NOP) logger implementation SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details. 第一次调用:1514172916097 2秒之后调用:1514172916097 3秒之后调用:1514172916097 再过11秒之后调用:1514172934731
我们根据定义的Cache策略十秒缓存策略。通过Dubug模式跟踪代码,我们可以看出。在第一次执行代码的时候。2秒与3秒钟的结果时从缓存中获取得,11秒任然继续执行代码程序。

浙公网安备 33010602011771号