注解
@Cacheable
作用于方法或者类上。作用于类上表示该类中所有方法都会走缓存操作
cacheNames/value
缓存名称,可配置多个,调用方法前,会检查是否有命中的缓存,如果有命中至少一个缓存,则返回缓存值,不执行方法操作。更新缓存时,value中所有的缓存名称对应的缓存都会更新。
@Cacheable(value={"a","b"})
public String a(){.....}
@Cacheable(value="b")
public String b(){.....}
// 当调用a方法更新了缓存时,b方法的缓存也会同时更新
keyGenerator
缓存本质上是键值存储,所以缓存方法每次都需要同key进行缓存访问。
默认算法:(实现: org.springframework.cache.interceptor.KeyGenerator )
- 方法没有参数时,返回SimpleKey.EMPTY
- 只有一个参数时,返回该参数
- 有多个参数时,返回所有参数
默认算法只适合参数实现了有效的hashCode()和equals()方法的场景,如果不满足,需要自定义keyGenerator,实现自定义keyGenerator后,在属性中声明要使用的keyGenerator名称即可
key
声明式key,通过SpEL表达式声明key,key属性与 KeyGenerator 属性互斥,设置的时候需要进行二选一
@Cacheable(cacheNames="books", key="#isbn")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
@Cacheable(cacheNames="books", key="#isbn.rawNumber")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
@Cacheable(cacheNames="books", key="T(someType).hash(#isbn)")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
SpEL还提供了缓存相关的元数据用于key和后续condition,unless的使用
name | lacation | description | example |
---|---|---|---|
methodName |
Root object | The name of the method being invoked | #root.methodName |
method |
Root object | The method being invoked | #root.method.name |
target |
Root object | The target object being invoked | #root.target |
targetClass |
Root object | The class of the target being invoked | #root.targetClass |
args |
Root object | The arguments (as array) used for invoking the target | #root.args[0] |
caches |
Root object | Collection of caches against which the current method is run | #root.caches[0].name |
Argument name | Evaluation context | Name of any of the method arguments. If the names are not available (perhaps due to having no debug information), the argument names are also available under the #a<#arg> where #arg stands for the argument index (starting from 0 ). |
#iban or #a0 (you can also use #p0 or #p<#arg> notation as an alias). |
result |
Evaluation context | The result of the method call (the value to be cached). Only available in unless expressions, cache put expressions (to compute the key ), or cache evict expressions (when beforeInvocation is false ). For supported wrappers (such asOptional ), #result refers to the actual object, not the wrapper. |
#result |
cacheManage
缓存管理器,用于管理(检索)一个类型缓存,如Redis相关的缓存通过RedisCacheManage来进行管理
cacheResolve
缓存解析器,用于管理缓存管理器,一个CacheResolve拥有一个CacheManage的引用,通过它来进行缓存检索。cacheManage与cacheResolve互斥
sync
是否同步,默认为false,多线程环境下,某些操作以相同的参数并发调用,则对应方法就会被多次执行,达不到缓存的目的,通过该属性,就可以控制只有线程可以通过调用方法获取到结果,其他线程进行等待。
使用该属性时,cacheNames/value只能有一个值,否则会异常,提示only allows a single cache
condition
条件判断,类似于@Conditional注解,condition接收一个结果为boolean类型的表达式,支持SpEL, 当表达式结果为true时才会进行缓存操作
unless
执行后判断,不缓存的条件, 接收一个结果为boolean类型的表达式,支持SpEL,当表达式结果为true时不进行缓存操作
condition 默认为 true,unless 默认为 false
当 condition = false,一定不会缓存;
当 condition = true,且 unless = true,不缓存;
当 condition = true,且 unless = false,缓存;
@CachePut
始终调用该方法并将其结果放入缓存中,主要用于更新缓存
同一个方法上不建议同时使用@Cacheable与@CachePut,@Cacheable会在某些条件下不放入缓存,@CachePut会强制刷新缓存,一起使用可能会出现极端情况
@CacheEvict
删除缓存,需要制定一个或多个收到影响的缓存名称,
allEntries
用于表示是否需要将缓存名称下的所有缓存清楚,不指定表示基于key或keyGeneration条件进行清除,如果配置了allEntries属性,key和 keyGeneration将被忽略
beforeInvocation
表示清除缓存操作在方法执行前还是执行后进行操作,默认为在方法执行之后进行清除操作
@Caching
有时,需要指定多个相同类型的注解(例如@CacheEvict
或 @CachePut
)——例如,因为不同缓存之间的条件或键表达式不同。@Caching
允许在同一方法上使用多个嵌套的 @Cacheable
、@CachePut
和@CacheEvict
注释。
@Caching(evict = { @CacheEvict("primary"), @CacheEvict(cacheNames="secondary", key="#p0") })
public Book importBooks(String deposit, Date date)
@CacheConfig
该注解只能作用于类上,允许该类下的操作注解共享缓存名称, keyGenerator,cacheManage,cacheResolve。该注解在类上不会执行任何缓存操作
配置优先级从低到高:
- 全局配置:cacheManage、keyGenerator
- 类级别配置 @CacheConfig
- 操作级别 @Cacheable、@CachePut、@CacheEvict
依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
-->
<!-- spring-boot-start-web中整合了cache,为方便测试,使用web依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
配置
application.yaml
增加redis配置
spring:
redis:
host: xxxxxxx
password: xxxxxx
......
配置类
@EnableCaching
@Configuration
public class RedisCachingConfiguration extends CachingConfigurerSupport {
/**
* RedisTemplate配置
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
this.initDomainRedisTemplate(redisTemplate, redisConnectionFactory);
return redisTemplate;
}
/**
* 使用Jackson序列化与反序列化value
*/
private void initDomainRedisTemplate(RedisTemplate<String, Object> redisTemplate, RedisConnectionFactory factory) {
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
redisTemplate.setKeySerializer(stringRedisSerializer);
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.setHashKeySerializer(stringRedisSerializer);
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.setConnectionFactory(factory);
}
/**
* 注册RedisCacheManage
*/
@Bean("cacheManage")
public CacheManager cacheManage(RedisConnectionFactory redisConnectionFactory) {
//缓存配置对象
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
redisCacheConfiguration = redisCacheConfiguration
// 设置超时时间
.entryTtl(Duration.ofSeconds(10))
// 设置前缀
.computePrefixWith(new ProjectCacheKeyPrefix(environment.getProjectName()))
//如果是空值,不缓存
.disableCachingNullValues()
//设置key序列化器
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
//设置value序列化器
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new Jackson2JsonRedisSerializer<>(Object.class)));
return RedisCacheManager
.builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory))
// 开启与事务同步管理,仅在事务成功时缓存或删除缓存
.setTransactionAware(true)
// 默认缓存配置
.cacheDefaults(redisCacheConfiguration)
// 不同的缓存名称使用使用指定的缓存配置,可指定多个
//withCacheConfiguration()
.build();
}
// 缓存key前缀生成规则,默认采用缓存名称加::
class ProjectCacheKeyPrefix implements CacheKeyPrefix {
private final String projectName;
ProjectCacheKeyPrefix(String projectName) {
this.projectName = projectName;
}
public String compute(String cacheName) {
String delimiter = ":";
return projectName != null ? projectName.concat(delimiter).concat(cacheName).concat(delimiter) : cacheName.concat(delimiter);
}
}
/**
* 没有指定key时默认,最好使用时指定key值
*/
@Bean
@Override
public KeyGenerator keyGenerator() {
return (target, method, params) -> {
StringBuilder sb = new StringBuilder();
sb.append(target.getClass().getName());
sb.append(":").append(method.getName());
for (Object obj : params) {
sb.append(":").append(obj.toString());
}
return sb.toString();
};
}
}