说明
@Cacheable 注解在方法上,表示该方法的返回结果是可以缓存的。也就是说,该方法的返回结果会放在缓存中,以便于以后使用相同的参数调用该方法时,会返回缓存中的值,而不会实际执行该方法。
属性名称 | 属性描述 | 举例 |
---|---|---|
value/cacheNames | 指定缓存组件的名字 | @Cacheable(value = "test") |
key | 缓存数据使用的 key,可以用它来指定。默认是使用方法参数的值 | 这个 key 你可以使用 spEL 表达式来编写,如: @Cacheable(value = "test", key = "#userId+'-'+#id") |
keyGenerator | key 的生成器,可以自己指定 key 的生成器的组件 id | key 与 keyGenerator 二选一使用 |
cacheManager | 指定缓存管理器;或者是 cacheResolver | |
condition | 判断条件,指定符合条件的情况下才缓存 | 如 condition="#id!= null",就是在id不 为null的时候触发 |
unless | 否定,unless 指定的条件为 true,方法的返回值就不会被缓存 | 如 unless="#id == null",就是在结果为null的 时候触发 |
sync | 是否使用异步模式 |
引入Redis的Maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
在启动类上写上这个注解,不然缓存是不会得到支持的。
@SpringBootApplication
@EnableCaching
public class JyhInterfaceServerApplication {
public static void main(String[] args) {
SpringApplication.run(JyhInterfaceServerApplication.class, args);
}
}
使用
个人习惯,一般 @Cacheable 注解都使用在impl层的接口类上,这样其他任何Controller类调用次接口都会用到,岂不美哉。(根据自己业务需求判断)
@Cacheable(key = "#id")
public String get(Long id) {
assert id != null;
return "成功";
}
实际业务中,往往我们的入参比较复杂,会遇到入参是实体类对象的情况,还有缓存的超时时间,这些都需要很灵活项目才方便使用。
方案一
批量设置接口超时时间、及生成key的序列化
创建Key的序列化方案
/**
* Redis缓存时Key的序列化方案
* 主要处理StringRedisSerializer是将Object强转为String
* 这样当Object是Integer和Long进强转会报错
*/
public class CustomStringRedisSerializer implements RedisSerializer {
@Override
public byte[] serialize(Object o) throws SerializationException {
if (o == null) {
return null;
}
return String.valueOf(o).getBytes(Charset.forName("UTF-8"));
}
@Override
public Object deserialize(byte[] bytes) throws SerializationException {
if (bytes == null) {
return null;
}
return new String(bytes, Charset.forName("UTF-8"));
}
}
RedisConfig 配置中使用上面的序列化方案
@EnableCaching
@Configuration
public class RedisConfig {
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
return RedisCacheManager.builder(RedisCacheWriter.nonLockingRedisCacheWriter(factory))
.cacheDefaults(this.redisCacheConfiguration(Duration.ofHours(1).getSeconds()))
.withInitialCacheConfigurations(this.initialCacheConfigurations()).build();
}
/**
* 这里设置过期时间,默认是1小时,@Cacheable(value = "jyhInterface") 匹配到的就是120秒
*/
private Map<String, RedisCacheConfiguration> initialCacheConfigurations() {
Map<String, RedisCacheConfiguration> redisCacheConfigurationMap = new HashMap<>();
redisCacheConfigurationMap.put("jyhInterface", this.redisCacheConfiguration(120));
return redisCacheConfigurationMap;
}
private RedisCacheConfiguration redisCacheConfiguration(long seconds) {
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
return redisCacheConfiguration.entryTtl(Duration.ofSeconds(seconds))
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer()));
}
private RedisSerializer<String> keySerializer() {
return new StringRedisSerializer();
}
/**
* 配置自定义redisTemplate
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
CustomStringRedisSerializer customStringRedisSerializer = new CustomStringRedisSerializer();
template.setConnectionFactory(connectionFactory);
//使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(customStringRedisSerializer);
template.setHashKeySerializer(customStringRedisSerializer);
template.setHashValueSerializer(jackson2JsonRedisSerializer());
template.setValueSerializer(jackson2JsonRedisSerializer());
template.afterPropertiesSet();
return template;
}
/**
* json序列化
*/
@Bean
public RedisSerializer<Object> jackson2JsonRedisSerializer() {
//使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_ARRAY);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
serializer.setObjectMapper(mapper);
return serializer;
}
}
方案二
除了方案一批量设置超时时间之外,我们还可以在注解上自定义超时时间
添加扩展注解类
/**
* @desc 扩展注解Cacheable 使用过期时间
* @author zhangl
* @create 2023/3/30
**/
public class CustomRedisCacheManager extends RedisCacheManager {
public CustomRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) {
super(cacheWriter, defaultCacheConfiguration);
}
/**
* 针对@Cacheable设置缓存过期时间
* @param name
* @param cacheConfig
* @return
*/
@Override
protected RedisCache createRedisCache(String name, RedisCacheConfiguration cacheConfig) {
String[] array = StringUtils.delimitedListToStringArray(name, "#");
name = array[0];
// 解析TTL
if (array.length > 1) {
long ttl = Long.parseLong(array[1]);
cacheConfig = cacheConfig.entryTtl(Duration.ofMinutes(ttl)); // 注意单位我此处用的是分钟
}
return super.createRedisCache(name, cacheConfig);
}
}
修改RedisConfig
注意这里时间单位是用的分钟,方案一是用的秒
@EnableCaching
@Configuration
public class RedisConfig {
/**
* 实例化自定义的缓存管理器
* @param redisTemplate
* @return
*/
@Bean
@SuppressWarnings(value = {"unchecked", "rawtypes"})
public RedisCacheManager redisCacheManager(RedisTemplate redisTemplate) {
RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(Objects.requireNonNull(redisTemplate.getConnectionFactory()));
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getValueSerializer()));
return new CustomRedisCacheManager(redisCacheWriter, redisCacheConfiguration);
}
private RedisCacheConfiguration redisCacheConfiguration(long seconds) {
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
return redisCacheConfiguration.entryTtl(Duration.ofSeconds(seconds))
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer()));
}
private RedisSerializer<String> keySerializer() {
return new StringRedisSerializer();
}
/**
* 配置自定义redisTemplate
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
CustomStringRedisSerializer customStringRedisSerializer = new CustomStringRedisSerializer();
template.setConnectionFactory(connectionFactory);
//使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(customStringRedisSerializer);
template.setHashKeySerializer(customStringRedisSerializer);
template.setHashValueSerializer(jackson2JsonRedisSerializer());
template.setValueSerializer(jackson2JsonRedisSerializer());
template.afterPropertiesSet();
return template;
}
/**
* json序列化
*/
@Bean
public RedisSerializer<Object> jackson2JsonRedisSerializer() {
//使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_ARRAY);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
serializer.setObjectMapper(mapper);
return serializer;
}
}
使用
这个时候我们就可以在使用注解的时候默认带上超时时间就可以拉
// 表示超时时间为5分钟
@Cacheable(value = "jyhInterface#5")
public String get(Long id) {
assert id != null;
return "成功";
}
标签:缓存,return,Redis,Cacheable,template,new,序列化,public
From: https://www.cnblogs.com/zlnp/p/17276662.html