赞
踩
spring boot 使用redis作为缓存时,默认只提供了全局缓存过期时间,不能针对某一个缓存name单独设置缓存过期时间,如果需要精确设置每个缓存name 的过期时间,需要按照如下方式配置
spring:
cache:
cache-names: gxf
redis:
# 全局缓存过期时间
time-to-live: 10m
# 自定义单个缓存name过期时间
time-to-live:
# 单个name过期时间
cache1: 10m
# 多个name过期时间,key包含特殊符号需要"[]"包裹
"[cache1,cache2]": 10m
注意:使用 @Value 注解不能同时接收多个属性,需要配合EL表达式;这里使用@ConfigurationProperties
/** * Redis过期时间配置 * <pre> * spring: * cache: * time-to-live: * cache-name1: 10m * # 多个key 请求使用逗号分割,并且使用"[]"包裹 * "[cache1,cache2]": 20h * </pre> * */ @Getter @Setter @ToString @ConfigurationProperties(prefix = "spring.cache") public class RedisTtlProperties { /** * 缓存过期时间 */ private Map<String, Duration> timeToLive = new HashMap<>(); }
@EnableCaching @Configuration(proxyBeanMethods = false) @EnableConfigurationProperties({RedisTtlProperties.class}) public class CacheConfig { private static final Logger log = LoggerFactory.getLogger(CacheConfig.class); /** * 自定义redis缓存名称过期时间 * @return RedisCacheManagerBuilderCustomizer */ @Bean public RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer(RedisTtlProperties redisTtl) { Map<String, Duration> timeToLiveMap = redisTtl.getTimeToLive(); if(MapUtils.isEmpty(timeToLiveMap)){ return null; } return (builder) -> { for (Map.Entry<String,Duration> cacheTimeToLive: timeToLiveMap.entrySet()){ String[] keys = StringUtils.split(cacheTimeToLive.getKey(), ","); for(String cache: keys){ builder.withCacheConfiguration(cache, RedisCacheConfiguration.defaultCacheConfig().entryTtl(cacheTimeToLive.getValue())); } } }; } }
接下来就可以直接使用@Cacheable注解了,单使用指定的cacheNames时会自动设置过期时间
以上配置添加完成之后,Redis 缓存默认的key 为 cacheName : key。这就会导致不同方法参数如果一致就会使用相同的缓存key。所以需要定制化Redis key 生成策略,只需要如下代码
@Configuration @EnableCaching @EnableConfigurationProperties({RedisTtlProperties.class}) public class RedisConfig extends CachingConfigurerSupport { /** * 设置Redis Key 缓存策略 * * @return 结果 */ @Override @Bean("redisKeyGenerator") public KeyGenerator keyGenerator() { return new RedisKeyGenerator(); } /** * Redis Key 生成策略 * * @author jonk * @date 2022/12/26 23:42 */ public static class RedisKeyGenerator implements KeyGenerator { /** * Generate a key for the given method and its parameters. * * @param target the target instance * @param method the method being called * @param params the method parameters (with any var-args expanded) * @return a generated key */ @Override public Object generate(Object target, Method method, Object... params) { return String.format("%s:%s:%s", method.getDeclaringClass().getSimpleName(), method.getName(), StringUtils.joinWith("_", params) ); } } }
经过以上修改,虽然解决了缓存key生成问题,但是默认的缓存值使用Jdk序列化的,这回导致开发过程中不太清楚缓存到底存了什么,更不能修改缓存值。
spring-boot-starter-data-redis 已经带了 jackson 序列化方式,我们直接使用就好;
代码如下:
@Bean @SuppressWarnings(value = {"unchecked", "rawtypes"}) public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) { RedisTemplate<Object, Object> template = new RedisTemplate<>(); template.setConnectionFactory(connectionFactory); RedisSerializer<Object> redisSerializer = this.getRedisSerializer(); // 使用StringRedisSerializer来序列化和反序列化redis的key值 template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(redisSerializer); // Hash的key也采用StringRedisSerializer的序列化方式 template.setHashKeySerializer(new StringRedisSerializer()); template.setHashValueSerializer(redisSerializer); template.afterPropertiesSet(); return template; } @SuppressWarnings(value = {"unchecked", "rawtypes"}) private RedisSerializer<Object> getRedisSerializer() { ObjectMapper mapper = new ObjectMapper(); // 未知属性反序列化不报错 mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); // null 值序列化配置 GenericJackson2JsonRedisSerializer.registerNullValueSerializer(mapper, null); return new GenericJackson2JsonRedisSerializer(mapper); }
上面配置解决了RedisTemplate 序列化方式采用Json格式写入,但对于采用@Cacheable 注解的序列化方式还是没有指定。
代码如下:
使用application.yml 配置生成标准的RedisCacheConfiguration
/** * 自定义RedisCacheConfiguration 设置值序列化方式,改配置会覆盖默认的缓存配置 * * @param cacheProperties 缓存属性 * @return RedisCacheConfiguration */ @Bean public RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties) { CacheProperties.Redis redisProperties = cacheProperties.getRedis(); RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig(); config = config.serializeValuesWith(SerializationPair.fromSerializer(this.getRedisSerializer())); if (redisProperties.getTimeToLive() != null) { config = config.entryTtl(redisProperties.getTimeToLive()); } if (redisProperties.getKeyPrefix() != null) { config = config.prefixCacheNameWith(redisProperties.getKeyPrefix()); } if (!redisProperties.isCacheNullValues()) { config = config.disableCachingNullValues(); } if (!redisProperties.isUseKeyPrefix()) { config = config.disableKeyPrefix(); } return config; } /** * 自定义redis缓存名称过期时间 * * @return RedisCacheManagerBuilderCustomizer */ @Bean public RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer(RedisCacheConfiguration redisCacheConfiguration, RedisTtlProperties redisTtl) { return (builder) -> { // 自定义缓存过期时间,类中已经初始化 redisTtl.getTimeToLives().forEach((k, v) -> { Arrays.stream(StringUtils.split(k, ",")).forEach(cache -> { builder.withCacheConfiguration(cache, redisCacheConfiguration.entryTtl(v)); }); }); }; }
经过以上几步,基本解决Redis 缓存中使用序列化问题,全代码概览
package com.ruoyi.framework.config; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.ruoyi.framework.config.properties.RedisTtlProperties; import org.apache.commons.lang3.StringUtils; import org.springframework.boot.autoconfigure.cache.CacheProperties; import org.springframework.boot.autoconfigure.cache.RedisCacheManagerBuilderCustomizer; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.interceptor.KeyGenerator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.cache.RedisCacheConfiguration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.script.DefaultRedisScript; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializationContext.SerializationPair; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; import java.lang.reflect.Method; import java.util.Arrays; /** * redis配置 * * @author ruoyi */ @Configuration @EnableCaching @EnableConfigurationProperties({RedisTtlProperties.class}) public class RedisConfig extends CachingConfigurerSupport { @Bean @SuppressWarnings(value = {"unchecked", "rawtypes"}) public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) { RedisTemplate<Object, Object> template = new RedisTemplate<>(); template.setConnectionFactory(connectionFactory); RedisSerializer<Object> redisSerializer = this.getRedisSerializer(); // 使用StringRedisSerializer来序列化和反序列化redis的key值 template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(redisSerializer); // Hash的key也采用StringRedisSerializer的序列化方式 template.setHashKeySerializer(new StringRedisSerializer()); template.setHashValueSerializer(redisSerializer); template.afterPropertiesSet(); return template; } /** * 自定义RedisCacheConfiguration 设置值序列化方式,改配置会覆盖默认的缓存配置 * * @param cacheProperties 缓存属性 * @return RedisCacheConfiguration */ @Bean public RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties) { CacheProperties.Redis redisProperties = cacheProperties.getRedis(); RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig(); config = config.serializeValuesWith(SerializationPair.fromSerializer(this.getRedisSerializer())); if (redisProperties.getTimeToLive() != null) { config = config.entryTtl(redisProperties.getTimeToLive()); } if (redisProperties.getKeyPrefix() != null) { config = config.prefixCacheNameWith(redisProperties.getKeyPrefix()); } if (!redisProperties.isCacheNullValues()) { config = config.disableCachingNullValues(); } if (!redisProperties.isUseKeyPrefix()) { config = config.disableKeyPrefix(); } return config; } /** * 自定义redis缓存名称过期时间 * * @return RedisCacheManagerBuilderCustomizer */ @Bean public RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer(RedisCacheConfiguration redisCacheConfiguration, RedisTtlProperties redisTtl) { return (builder) -> { // 自定义缓存过期时间,类中已经初始化 redisTtl.getTimeToLives().forEach((k, v) -> { Arrays.stream(StringUtils.split(k, ",")).forEach(cache -> { builder.withCacheConfiguration(cache, redisCacheConfiguration.entryTtl(v)); }); }); }; } /** * 设置Redis Key 缓存策略 * * @return 结果 */ @Override @Bean("redisKeyGenerator") public KeyGenerator keyGenerator() { return new RedisKeyGenerator(); } @SuppressWarnings(value = {"unchecked", "rawtypes"}) private RedisSerializer<Object> getRedisSerializer() { ObjectMapper mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); GenericJackson2JsonRedisSerializer.registerNullValueSerializer(mapper, null); return new GenericJackson2JsonRedisSerializer(mapper); } /** * Redis Key 生成策略 * * @author jonk * @date 2022/12/26 23:42 */ public static class RedisKeyGenerator implements KeyGenerator { /** * Generate a key for the given method and its parameters. * * @param target the target instance * @param method the method being called * @param params the method parameters (with any var-args expanded) * @return a generated key */ @Override public Object generate(Object target, Method method, Object... params) { return String.format("%s:%s:%s", method.getDeclaringClass().getSimpleName(), method.getName(), StringUtils.joinWith("_", params) ); } } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。