spring-boot的spring-cache中的扩展redis缓存的ttl和key名
原文地址:spring-boot的spring-cache中的扩展redis缓存的ttl和key名
前提
spring-cache大家都用过,其中使用redis-cache大家也用过,至于如何使用怎么配置,本篇就不重点描述了。本篇主要解决2个问题,第一个问题使用redis做缓存时对每个key进行自定义的过期时间配置,第二个使用redis做缓存时
@Cacheable(value = "value", key = "#p0"),最后生成的key会在value和p0中间的有(::)2个冒号,与redis的key名一个冒号间隔的风格不符。
本篇以spring-boot 2.1.2和 spirng 5.1.4为基础来讲解。RedisCacheManage在spring-data-redis 2.x中相对于1.x的变动很大,本篇即在2.x的版本中实现。
redis cache的过期时间
我们都知道redis的过期时间,是用它做缓存或者做业务操作的灵性。在使用@Cacheable(value = "value", key = "#p0")注解时即可。具体的使用方法参考网上。
RedisCacheManager
我们先来看看RedisCacheManager,RedisCacheWriter接口是对redis操作进行包装的一层低级的操作。defaultCacheConfig是redis的默认配置,在下一个选项卡中详细介绍。initialCacheConfiguration是对各个单独的缓存进行各自详细的配置(过期时间就是在此配置的),allowInFlightCacheCreation是否允许创建不事先定义的缓存,如果不存在即使用默认配置。RedisCacheManagerBuilder使用桥模式,我们可以用它构建RedisCacheManager。
public class RedisCacheManager extends AbstractTransactionSupportingCacheManager {
private final RedisCacheWriter cacheWriter;
private final RedisCacheConfiguration defaultCacheConfig;
private final Map<String, RedisCacheConfiguration> initialCacheConfiguration;
private final boolean allowInFlightCacheCreation;
public static class RedisCacheManagerBuilder {}
}
AbstractTransactionSupportingCacheManager
AbstractTransactionSupportingCacheManager加入事务概念,将操作与事务绑定,包装了一层事务。
public abstract class AbstractTransactionSupportingCacheManager extends AbstractCacheManager {
private boolean transactionAware = false;
public void setTransactionAware(boolean transactionAware) {
this.transactionAware = transactionAware;
}
public boolean isTransactionAware() {
return this.transactionAware;
}
@Override
protected Cache decorateCache(Cache cache) {
return (isTransactionAware() ? new TransactionAwareCacheDecorator(cache) : cache);
}
}
RedisCacheConfiguration
ttl是过期时间,cacheNullValues是否允许存null值,keyPrefix缓存前缀规则,usePrefix是否允许使用前缀。keySerializationPair缓存key序列化,valueSerializationPair缓存值序列化此处最好自己使用jackson的序列号替代原生的jdk序列化,conversionService做转换用的。
public class RedisCacheConfiguration {
private final Duration ttl;
private final boolean cacheNullValues;
private final CacheKeyPrefix keyPrefix;
private final boolean usePrefix;
private final SerializationPair<String> keySerializationPair;
private final SerializationPair<Object> valueSerializationPair;
private final ConversionService conversionService;
}
RedisCacheManager
再来看看如何配置RedisCacheManager
RedisCacheAutoConfiguration
配置前通过RedisAutoConfiguration配置可以获取到redis相关配置包括redisTemplate,因为spring-boot2中redis使用Lettuce作为客户端,相关配置在LettuceConnectionConfiguration中。
在去加载CacheProperties和CustomCacheProperties配置。
通过RedisCacheManagerBuilder去构造RedisCacheManager,使用非加锁的redis缓存操作,redis默认配置使用的是cacheProperties中的redis,最后根据我们自定义的customCacheProperties阔以针对单个的key设置单独的redis缓存配置。
getDefaultRedisCacheConfiguration主要先通过RedisCacheConfiguration的默认创建方法defaultCacheConfig创建默认的配置,在通过getJackson2JsonRedisSerializer创建默认value格式化(使用jackson代替jdk序列化),然后通过redis缓存配置的是spring-cache的CacheProperties去修改配置项。
最后根据配置构建出RedisCacheConfiguration。
@Slf4j
@EnableCaching
@Configuration
@AutoConfigureAfter(RedisAutoConfiguration.class)
@EnableConfigurationProperties({CacheProperties.class, CustomCacheProperties.class})
@ConditionalOnClass({Redis.class, RedisCacheConfiguration.class})
public class RedisCacheAutoConfiguration {
@Autowired
private CacheProperties cacheProperties;
@Bean
public RedisCacheManager redisCacheManager(CustomCacheProperties customCacheProperties,
RedisConnectionFactory redisConnectionFactory) {
RedisCacheConfiguration defaultConfiguration = getDefaultRedisCacheConfiguration();
RedisCacheManager.RedisCacheManagerBuilder builder = RedisCacheManager.RedisCacheManagerBuilder
.fromCacheWriter(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory))
.cacheDefaults(defaultConfiguration);
Map<String, RedisCacheConfiguration> map = Maps.newHashMap();
Optional.ofNullable(customCacheProperties)
.map(p -> p.getCustomCache())
.ifPresent(customCache -> {
customCache.forEach((key, cache) -> {
RedisCacheConfiguration cfg = handleRedisCacheConfiguration(cache, defaultConfiguration);
map.put(key, cfg);
});
});
builder.withInitialCacheConfigurations(map);
return builder.build();
}
private RedisCacheConfiguration getDefaultRedisCacheConfiguration() {
Redis redisProperties = cacheProperties.getRedis();
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = getJackson2JsonRedisSerializer();
config = config.serializeKeysWith(SerializationPair.fromSerializer(new StringRedisSerializer()));
config = config.serializeValuesWith(SerializationPair.fromSerializer(jackson2JsonRedisSerializer));
config = handleRedisCacheConfiguration(redisProperties, config);
return config;
}
private Jackson2JsonRedisSerializer getJackson2JsonRedisSerializer() {
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
om.setSerializationInclusion(Include.NON_NULL);
om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
jackson2JsonRedisSerializer.setObjectMapper(om);
return jackson2JsonRedisSerializer;
}
private RedisCacheConfiguration handleRedisCacheConfiguration(Redis redisProperties,
RedisCacheConfiguration config) {
if (Objects.isNull(redisProperties)) {
return config;
}
if (redisProperties.getTimeToLive() != null) {
config = config.entryTtl(redisProperties.getTimeToLive());
}
if (redisProperties.getKeyPrefix() != null) {
config = config.computePrefixWith(cacheName -> cacheName + redisProperties.getKeyPrefix());
}
if (!redisProperties.isCacheNullValues()) {
config = config.disableCachingNullValues();
}
if (!redisProperties.isUseKeyPrefix()) {
config = config.disableKeyPrefix();
}
return config;
}
}
CustomCacheProperties
我们自定的缓存的配置,使用了现有的CacheProperties.Redis作为配置类。
@Data
@ConfigurationProperties(prefix = "damon.cache")
public class CustomCacheProperties {
private Map<String, CacheProperties.Redis> customCache;
}
Redis
Redis的key配置,过期时间,是否允许缓存空值默认可以,key的前缀,是否允许使用key前缀
public static class {
private Duration timeToLive;
private boolean cacheNullValues = true;
private String keyPrefix;
private boolean useKeyPrefix = true;
}
yml配置
再来看看配置项
spring.cache.redis就为当前redis-cache的默认配置
底下的damon.cache就为自定义配置(默认20秒),如下配置了testA和 testB2个自定义key的过期时间(一个40秒,一个50秒)
spring:
redis:
host: localhost
port: 6379
cache:
redis:
time-to-live: 20s
damon:
cache:
custom-cache:
testA:
time-to-live: 40s
testB:
time-to-live: 50s
redis-cache的key名调整
从上述我们可以看出使用后,缓存过期时间可以自定义配置了,但是key名中间有2个冒号。
RedisCache
RedisCache中的createCacheKey方法是生成redis的key,从中可以看出是否使用prefix,使用的话通过prefixCacheKey方法生成,借用了redisCache配置项来生成。
private final RedisCacheConfiguration cacheConfig;
protected String createCacheKey(Object key) {
String convertedKey = convertKey(key);
if (!cacheConfig.usePrefix()) {
return convertedKey;
}
return prefixCacheKey(convertedKey);
}
private String prefixCacheKey(String key) {
// allow contextual cache names by computing the key prefix on every call.
return cacheConfig.getKeyPrefixFor(name) + key;
}
RedisCacheConfiguration
在redisCache配置项中使用getKeyPrefixFor方法来生成完整的redis的key名,通过 keyPrefix.compute来生成。
private final CacheKeyPrefix keyPrefix;
public String getKeyPrefixFor(String cacheName) {
Assert.notNull(cacheName, "Cache name must not be null!");
return keyPrefix.compute(cacheName);
}
CacheKeyPrefix
这里就看到我们使用处,而且看到了默认实现有2个冒号的实现。
其实是在RedisCacheConfiguration中有个默认实现方法,里面用的就是CacheKeyPrefix的默认实现。我们只有覆盖此处即可。
@FunctionalInterface
public interface CacheKeyPrefix {
//计算在redis中的缓存名
String compute(String cacheName);
//默认实现,中间用的就是::
static CacheKeyPrefix simple() {
return name -> name + "::";
}
}
总结
参考上文,使用RedisCacheConfiguration的computePrefixWith(cacheName -> cacheName + redisProperties.getKeyPrefix())实现key调整。
题外话
我们再来聊聊spring-cache,实际上其实它就是把缓存的使用给抽象了,在对缓存的具体实现的过程中给抽出来。其实最重要的就是Cache和CacheManager2个接口,简单的实现如SimpleCacheManager。
欢迎关注我的微信公众号
spring-boot的spring-cache中的扩展redis缓存的ttl和key名的更多相关文章
- Spring Boot demo系列(十):Redis缓存
1 概述 本文演示了如何在Spring Boot中将Redis作为缓存使用,具体的内容包括: 环境搭建 项目搭建 测试 2 环境 Redis MySQL MyBatis Plus 3 Redis安装 ...
- Spring Boot 揭秘与实战(二) 数据缓存篇 - Redis Cache
文章目录 1. Redis Cache 集成 2. 源代码 本文,讲解 Spring Boot 如何集成 Redis Cache,实现缓存. 在阅读「Spring Boot 揭秘与实战(二) 数据缓存 ...
- Spring Boot 揭秘与实战(二) 数据缓存篇 - Guava Cache
文章目录 1. Guava Cache 集成 2. 个性化配置 3. 源代码 本文,讲解 Spring Boot 如何集成 Guava Cache,实现缓存. 在阅读「Spring Boot 揭秘与实 ...
- Spring Boot 入门之 Cache 篇(四)
博客地址:http://www.moonxy.com 一.前言 Spring Cache 对 Cahce 进行了抽象,提供了 @Cacheable.@CachePut.@CacheEvict 等注解. ...
- SpringBoot系列:Spring Boot集成Spring Cache,使用EhCache
前面的章节,讲解了Spring Boot集成Spring Cache,Spring Cache已经完成了多种Cache的实现,包括EhCache.RedisCache.ConcurrentMapCac ...
- SpringBoot系列:Spring Boot集成Spring Cache,使用RedisCache
前面的章节,讲解了Spring Boot集成Spring Cache,Spring Cache已经完成了多种Cache的实现,包括EhCache.RedisCache.ConcurrentMapCac ...
- Spring Boot 2.x基础教程:进程内缓存的使用与Cache注解详解
随着时间的积累,应用的使用用户不断增加,数据规模也越来越大,往往数据库查询操作会成为影响用户使用体验的瓶颈,此时使用缓存往往是解决这一问题非常好的手段之一.Spring 3开始提供了强大的基于注解的缓 ...
- Spring Boot:在Spring Boot中使用Mysql和JPA
本文向你展示如何在Spring Boot的Web应用中使用Mysq数据库,也充分展示Spring Boot的优势(尽可能少的代码和配置).数据访问层我们将使用Spring Data JPA和Hiber ...
- Spring Boot 监听 Activemq 中的特定 topic ,并将数据通过 RabbitMq 发布出去
1.Spring Boot 和 ActiveMQ .RabbitMQ 简介 最近因为公司的项目需要用到 Spring Boot , 所以自学了一下, 发现它与 Spring 相比,最大的优点就是减少了 ...
随机推荐
- DRC错误解决办法
一.WARNING(ORCAP-1589): Net has two or more aliases - possible short? 错误原因:一个网络有两个网络标号,可能造成短路! 问题本质:原 ...
- vue学习笔记:在vue项目里面使用引入公共方法
首先新建一个文件夹:commonFunction ,然后在里面建立 一个文件common.js 建立好之后,在main.js里面引入这个公共方法 最后是调用这个公共方法 测试一下,我在公共方法里面写了 ...
- 简单工厂模式demo
1. 简单工厂模式 domain的接口 public interface Color{ public void display(); } red public Class Red implements ...
- python语法_模块_os_sys
os模块:提供对此操作系统进行操作的接口 os.getcwd() 获取python运行的工作目录. os.chdir(r'C:\USERs') 修改当前工作目录. os.curdir 返回当前目录 ( ...
- 【RL-TCPnet网络教程】第9章 RL-TCPnet网络协议栈移植(uCOS-III)
第9章 RL-TCPnet网络协议栈移植(uCOS-III) 本章教程为大家讲解RL-TCPnet网络协议栈的uCOS-III操作系统移植方式,学习了第6章讲解的底层驱动接口函数之后,移 ...
- 版本号严格遵守semver语义化标准
地址:http://semver.org/lang/zh-CN/?spm=a219a.7629140.0.0.GUJMXE 语义化版本 2.0.0 摘要 版本格式:主版本号.次版本号.修订号,版本号递 ...
- #Java学习之路——基础阶段(第七篇)
我的学习阶段是跟着CZBK黑马的双源课程,学习目标以及博客是为了审查自己的学习情况,毕竟看一遍,敲一遍,和自己归纳总结一遍有着很大的区别,在此期间我会参杂Java疯狂讲义(第四版)里面的内容. 前言: ...
- [Swift]LeetCode99. 恢复二叉搜索树 | Recover Binary Search Tree
Two elements of a binary search tree (BST) are swapped by mistake. Recover the tree without changing ...
- [Swift]LeetCode161. 一次编辑距离 $ One Edit Distance
Given two strings S and T, determine if they are both one edit distance apart. 给定两个字符串S和T,确定它们是否都是是一 ...
- Linux 下源码编译FFMEG
目录 1. 安装依赖关系 2. 源码安装 yasm 3. 源码安装 NASM 4. 源码安装libx264 5. 源码安装x265 6. 源码安装 libmp3lame 7. 源码安装 libopus ...