一、启用对缓存的支持
Spring 的缓存抽象在很大程度上是围绕切面构建的。
Spring 对缓存的支持有两种方式:
- 注解驱动的缓存
- XML 声明的缓存
在方法上添加 @Cacheable
和 CacheEvivt
注解来使用缓存。
启用 Spring 对注解驱动缓存的支持,在配置类上添加 @EnableCaching
,并声明缓存管理器:
@Bean
public CacheManager cacheManager(){
return new ConcurrentMapCacheManager();
}
xml配置:
<cache:annotation-driven />
<bean id="cacheManager" class="org.springframework.cache.concurrent.ConcurrentMapCacheManager" />
启用注解驱动缓存后,会创建一个切面并触发Spring 缓存注解的切点。根据使用的注解及缓存状态,这个切面会从缓存中获取数据,将数据添加到缓存中或者从缓存中移除。
缓存管理器是 Spring 缓存抽象的核心,它能与多个流行的缓存实现进行集成。ConcurrentMapCacheManager
是一个简单的缓存管理器,但它是基于内存的,其生命周期是与应用关联的。
1、配置缓存管理器
Spring 3.1 内置了五个缓存管理器实现:
SimpleCacheManager
NoOpCacheManager
ConcurrentMapCacheManager
CompositeCacheManager
EhCacheManager
Spring 3.2 又引入了另一个缓存管理器,它可以用于基于 JCache 的缓存提供商中。除了核心的Spring 框架,Spring Data又提供了两个缓存管理器:
RedisCacheManger
(来自 Spring Data Redis)GemfireCacheManager
(Spring Data CemFire)
使用 Redis 缓存
通过传递一个 RedisTemplate
实例到其构造器,构建 RedisCacheManger
。
@Configuration
@EnableCaching
public class CachingConfig {
/**
* @author Cedar
* @DESCRIPTION: 声明Redis缓存管理器
* @params: []
* @return: org.springframework.cache.CacheManager
* @Date: 2019/2/27 16:08
*/
@Bean
public CacheManager cacheManager(RedisTemplate redisTemplate) {
return new RedisCacheManager(redisTemplate);
}
/**
* @author Cedar
* @DESCRIPTION: Redis连接工程 bean
* @params: []
* @return: org.springframework.data.redis.connection.jedis.JedisConnectionFactory
* @Date: 2019/2/27 16:37
*/
@Bean
public JedisConnectionFactory redisConnectionFactory() {
JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
jedisConnectionFactory.afterPropertiesSet();
return jedisConnectionFactory;
}
/**
* @author Cedar
* @DESCRIPTION: RedisTemplate
* @params: [redisCF]
* @return: org.springframework.data.redis.core.RedisTemplate<java.lang.String,java.lang.String>
* @Date: 2019/2/27 16:40
*/
@Bean
public RedisTemplate<String,String> redisTemplate (RedisConnectionFactory redisCF){
RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisCF);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
使用多个缓存管理器
CompositeCacheManager
要通过一个或更多的缓存管理器来进行配置,它会迭代这些缓存管理器,以查找之前所缓存的值。
@Bean
public CacheManager cacheManager(net.sf.ehcache.CacheManager cm, javax.cache.CacheManager jcm){
CompositeCacheManager cacheManager = new CompositeCacheManager();
ArrayList<CacheManager> cacheManagers = new ArrayList<>();
cacheManagers.add(new JCacheCacheManager(jcm));
cacheManagers.add(new EhCacheCacheManager(cm));
cacheManagers.add(new RedisCacheManager(redisTemplate()));
cacheManager.setCacheManagers(cacheManagers);
return cacheManager;
}
二、为方法添加注解以支持缓存
在 Spring 中启动缓存时创建一个切面,它触发一个或多个Spring 的缓存注解。Spring 提供了4个注解来声明缓存规则:
注解 | 描述 |
---|---|
@Cacheable | 表明 Spring 在调用方法之前,首先应该在这个缓存中查找方法的返回值。如果这个值能够找到,就会返回缓存的值。否则,调用这个方法,返回值会放到缓存中。 |
@CachePut | 表明 Spring 应该将方法的返回值放到缓存中。在方法的调用前不会检查缓存,方法始终被调用。 |
@CacheEvict | 表明Spring 应该在缓存中清除一个或多个条目 |
@Caching | 这是一个分组的注解,能够同时应用多个其他的缓存注解 |
这些注解可以用在方法或类上。
1、填充缓存
@Cacheale 和 @CachePut 都可以填充缓存,但也有差异,其共同属性:
属性 | 类型 | 描述 |
---|---|---|
value | String[] | 要使用的缓存名称 |
condition | String | SpEl 表达式,如果得到的值是 false,不会将缓存应用到方法调用上 |
key | String | SpEl 表达式,用来计算自定义的缓存 key |
unless | String | SpEl 表达式,值为 true ,返回值不会放到缓存中 |
当为接口方法添加注解后,注解会被所有实现继承。
将值放到缓存中
缓存默认的 key 基于方法的参数确定,如果方法参数为 userId, 则key为 userId 的值,参数为 user 对象,key 也为 user 对象。但是,缓存的值为 user 对象,key 为 user 对象则不是想要的结果,因此需要指定一个key。
自定义缓存 key
通过设置 key
属性,来自定义 key ,它是通过一个 SpEL 表达式计算得到的。如果想要将一个 user id 作为key,但是在保存这个 user 对象之前,还没有 id ,只能通过 save() 方法返回的 user 对象得到id属性。
在为缓存编写 SpEL 表达式的时候,Spring 暴露了一些很有用的元数据,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) |
因此可以自定义注解 key 为 id : @CachePut(value="userCache" key="#result.id")
条件化缓存
unless
和 condition
属性用于实现条件化缓存。unless 属性只能阻止将对象放到缓存,但是这个方法调用时,依然会从缓存中查找,找到匹配的值就会返回该值, 如果 condition 的表达式结果是false,在调用方式时,缓存是被禁用的,不会去缓存中查找,也不会将值放进缓存。
例如:
//message为方法返回的对象属性
@Cacheable(value="userCache" unless= "#result.message.contaions('NoCache')")
User findOne(long id);
@Cacheable(value="userCache" unless= "#result.message.contaions('NoCache')" condition="#id >= 10")
User findOne(long id);
移除缓存条目
@CacheEvict
注解的方法被调用,一个或多个缓存的条目在缓存中移除。
@CacheEvivt("userCache")
void remove(long userId);
与 @Cacheable 和 @CachePut 不同,@CacheEvict 能够应用在返回值为 void 的方法上,另两个则需要非 void 的返回值。
缓存中被删除的条目的 key 与传递进来的 userId 的值相等。
@CacheEvict 的属性,没有 unless 属性,也提供了几个新的属性:
属性 | 类型 | 描述 |
---|---|---|
value | String[] | |
key | String | |
condition | String | |
allEntries | boolean | 如果为 ture ,特定缓存的所有条目都会被移除掉 |
beforeInvocation | boolean | 如果为 true ,在方法调用前移除条目。 false(默认),在方法调用成功后再移除条目。 |