缓存数据

简介

缓存(Caching)可以存储经常用到的信息,这样每次需要的时候,这些信息都可以立即使用.尽管Spring自身没有实现缓存解决方案,但是它对缓存功能提供了声明式的支持,能够与多种流行的缓存进行集成.

启用对缓存的支持

Spring对缓存有两种支持方式:

  1. 注解驱动的缓存
  2. XML声明的缓存
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?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.xsd
http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache.xsd">

<!-- 缓存管理器(有很多,可以配置其他的) -->
<!-- ConcurrentMapCacheManager是把缓存存在内存中的,不适合生产环境,适合自己搞着玩 -->
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheManager" id="cacheManager"/>

<!-- 启用注解 -->
<!-- cache-manager默认值为cacheManager,如果名字不是cacheManager,则需要手动设置 -->
<cache:annotation-driven cache-manager="cacheManager"/>

</beans>

缓存管理器

Spring3.1中有5个:

  • SimpleCacheManager
  • NoOpCacheManger
  • ConcurrentMapCacheManager
  • CompositeCacheManager
  • EhCacheCacheManager

Spring3.2后加入了:

  • RedisCacheManager
  • GemfireCacheManager

对与缓存管理器,有很多种方案可以选择,这里介绍EhCacheCacheManager.

使用Ehcache缓存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?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.xsd
http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache.xsd">
<!-- 利用EhCacheManagerFactoryBean产生一个EhCacheManager -->
<bean id="EhCacheCacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<!-- EhCache需要一个配置文件 -->
<property name="configLocation" value="classpath:ehcache.xml"/>
</bean>

<!-- spring 包装装Ehcache缓存管理器 -->
<bean id="EhCacheManager"
class="org.springframework.cache.ehcache.EhCacheCacheManager">
<property name="cacheManager" ref="EhCacheCacheManager"/>
</bean>

<!-- 激活spring 缓存注解(这个注解是可以让工程中其他类中,通过注解可以快速方便使用缓存技术 -->
<cache:annotation-driven cache-manager="EhCacheManager"/>

</beans>

注意: 这里可能有这样的疑问,EhCacheManager的class是一个EhCacheManagerFactoryBean类型,为什么可以注入到EhCacheCacheManager中的cacheManager属性中去?

解释为:Spring提供了EhCacheManagerFactoryBean来产生CacheManager,因为它是一个工厂Bean ,它的ehcache()方法(我看源码是getObject()方法,<<Spring实战>>写错了???)可以创建并返回一个CacheManger实例,所以注册在Spring应用上下文中的并不是EhCacheManagerFactoryBean对象,而是CacheManger对象,因此适合注入到EhCacheCacheManager中.

ehcache.xml

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
27
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">

<diskStore path="java.io.tmpdir"/>
<!-- 默认的配置 -->
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
maxElementsOnDisk="10000000"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
<persistence strategy="localTempSwap"/>
</defaultCache>
<!-- 自定义的配置(指定name属性) -->
<cache name="bos"
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
maxElementsOnDisk="10000000"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
<persistence strategy="localTempSwap"/>
</cache>
</ehcache>

使用Redis缓存

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
27
28
29
30
31
32
33
34
35
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
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.xsd">
<!-- Redis池配置 -->
<bean id="redisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="300"/>
<property name="maxWaitMillis" value="3000"/>
<property name="testOnBorrow" value="true"/>
</bean>
<!-- Redis连接工厂 -->
<bean id="redisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="localhost"/>
<property name="port" value="6379"/>
<property name="database" value="0"/>
<property name="password" value="root"/>
<property name="poolConfig" ref="redisPoolConfig"/>
</bean>
<!-- Reids模板 -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="redisConnectionFactory"/>
<property name="keySerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
</property>
<property name="valueSerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
</property>
</bean>
<!-- Redis缓存管理器 -->
<bean class="org.springframework.data.redis.cache.RedisCacheManager" id="reidsCacheManager">
<constructor-arg ref="redisTemplate"/>
</bean>

<cache:annotation-driven cache-manager="reidsCacheManager"/>
</beans>

使用多个缓存管理器

Spring还支持配置多个缓存管理器,一起工作,通过迭代的方式,来查找之前所缓存的值,它会迭代JCacheCacheManager,EhCacheManager和RedisCacheManager.

1
2
3
4
5
6
7
8
9
10
<bean id="manyCacheManager" class="org.springframework.cache.support.CompositeCacheManager">
<property name="cacheManagers">
<list>
<ref bean="EhCacheManager"/>
<ref bean="reidsCacheManager"/>
<!--等等,Spring会按照顺序来检查缓存-->
</list>
</property>
</bean>
<cache:annotation-driven cache-manager="manyCacheManager"/>

为方法添加注解来支持缓存

四个注解:

  1. @Cacheable 表明Spring在调用方法前,首先应该在缓存中找该方法的返回值,如果这个值能够被找到,就会返回缓存的值,否则,会调用方法
  2. @CachePut 表明Spring应该讲方法的返回值放到缓存中,在方法的调用前不会去检查缓存,方法始终会被调用.
  3. @CacheEvict 表明Spring应该在缓存中清除一个或多个条目
  4. @Caching 这是一个分组的注解,能够同时应用多个其他缓存注解(没用过,不知道)

解释一下2 : 就是该方法永远会被调用,不会走缓存,但是它会把返回值放到缓存中,给其他的方法用(无私奉献者)

填充注解

@Cacheable@CachePut 两个相似,它们的参数有:

  1. value—-缓存名,多个方法返回相同,业务允许的情况下,可以共用缓存名.
  2. condition—-spEL表达式,如果是false,不会将缓存放到方法调用上
  3. key—-spEL表达式,计算自定义的缓存key(就是查找缓存的索引)
  4. unless—–spEL表达式,如果是true,返回值不会放到缓存中

2表示,如果为false,该注解相当与没写,

4表示,如果为true,返回值不会赋给缓存

另外: key默认为参数(不写的话),所以有时候使用默认并不合适,手动设置key是很有必要的.

对于有复杂参数的方法,必须自定义key属性

自定义缓存key

Spring提供了多个用来定义缓存规则的SpEL拓展:

表达式 描述
#root.args 传递给缓存方法的参数,形式为数组
#root.caches 该方法执行时对应的缓存,形式为数组
#root.target 目标对象
#root.targetClass 目标对象的类,等于#root.target.class
#root.method 缓存方法
#root.methodName 缓存方法的名字,等于#root.method.name
#result 方法的返回值(不能用于@Cacheable)
#Argument 任意的方法参数名(#argName)或参数索引(#a0或者#p0)

条件化缓存

unless属性:如果是true,返回值不会放到缓存中,但是还是会在缓存中查找,找到就会走缓存.例如

1
2
3
4
@Cacheable(value="name"
unless="#result.message.contains('NoCache')"
condition="#id >= 10")
Spittle findOne(long id);

如果传入的id大于10,这个注解就相当于没写,如果返回结果中的message属性中包含"NoCache" 返回的结果就不会存到缓存中,如果不包含就会存到缓存中,当下次调用方式时,还是会走缓存,查询到缓存的值.

移出缓存条目

可用于移出指定的缓存,例如当执行remove()方法时就可以使用.

@CacheEvict 属性为:

  1. value—-缓存名称
  2. key—删除哪一个缓存
  3. condition—-不解释
  4. allEntries—boolean类型,如果为true,就删除所有目录
  5. beforeInvocation—boolean类型,如果为true,方法调用前删除条目,如果为false(默认),方法成功后再删除条目.

使用XML声明缓存

  1. 如果在自己代码上使用注解让你很不爽,

  2. 或者你需要在没有源码的bean上使用缓存功能.

    这时就需要使用XML的方式.具体的XML写法,略(不想写,感觉用注解挺好的,结合aop自己猜怎么写XML).

元素 描述
<cache:annotation-driven> 启用缓存
<cache:advice> 定义缓存通知,结合<aop:advisor> ,将通知应用到切点上.
<cache:caching> 在缓存通知中,定义一组特定的缓存规则
<cache:cacheable> @Cacheable
<cache:cache-put> @CachePut
<cache:cache-evict> @CacheEvict