当前位置:   article > 正文

Spring Boot Cache使用Redis自定义单个缓存名称过期时间_springboot整合springcache如何设置指定的过期时间

springboot整合springcache如何设置指定的过期时间

Spring Boot Cache使用Redis自定义单个缓存名称过期时间

spring boot 使用redis作为缓存时,默认只提供了全局缓存过期时间,不能针对某一个缓存name单独设置缓存过期时间,如果需要精确设置每个缓存name 的过期时间,需要按照如下方式配置

1、 添加配置属性

spring:
  cache:
    cache-names: gxf
    redis:
      # 全局缓存过期时间
      time-to-live: 10m
    # 自定义单个缓存name过期时间
    time-to-live:
      # 单个name过期时间
      cache1: 10m
      # 多个name过期时间,key包含特殊符号需要"[]"包裹
      "[cache1,cache2]": 10m
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

2、配置文件接收多个配置属性

注意:使用 @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<>();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

3、添加缓存配置类

@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()));
                }
            }
        };
    }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

接下来就可以直接使用@Cacheable注解了,单使用指定的cacheNames时会自动设置过期时间

4、缺点

以上配置添加完成之后,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)
            );
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

经过以上修改,虽然解决了缓存key生成问题,但是默认的缓存值使用Jdk序列化的,这回导致开发过程中不太清楚缓存到底存了什么,更不能修改缓存值。

5、修改Redis 缓存序列化方式

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);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

上面配置解决了RedisTemplate 序列化方式采用Json格式写入,但对于采用@Cacheable 注解的序列化方式还是没有指定。

6、@Cacheable Redis指定json序列化方式

代码如下:
使用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));
                });
            });
        };
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

经过以上几步,基本解决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)
            );
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/353162
推荐阅读
相关标签
  

闽ICP备14008679号