spring cache设置指定Key过期时间

今天是2019年最后一天,回想年初立下的小目标,貌似都完成的不理想-_- 还得继续努力啊!!!生活还在继续,加油。

最近刚好在使用spring cache做缓存的时候,有需要针对不同的key设置过期时间的场景,找了下资料,实现这个功能,做个总结。

spring cache 集成redis是基于spring-data-redis组件开发的一直通过注解就能轻松实现redis缓存操作的框架。
但原生的spring cache不支持缓存过期,默认都是没有过期时间的,以下实现自定义缓存过期时间。

spring-data-redis版本是1.8.11,所以需要使用此版本或者兼容此版本的其他版本。

先看spring的RedisCacheManager类代码:

	protected RedisCache createCache(String cacheName) {
		long expiration = computeExpiration(cacheName);
		return new RedisCache(cacheName, (usePrefix ? cachePrefix.prefix(cacheName) : null), redisOperations, expiration,
				cacheNullValues);
	}

	protected long computeExpiration(String name) {
		Long expiration = null;
		if (expires != null) {
			expiration = expires.get(name);
		}
		return (expiration != null ? expiration.longValue() : defaultExpiration);
	}

其中expiration变量其实就是过期时间(单位是秒),如果我们不做任何事情,那么expiration 就是defaultExpiration,这个值是0,也就是永不过期。所以我们可以从这个入手,自定义一个RedisCacheManager,覆盖createCache方法。

自定义类:CustomizerRedisCacheManager

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.cache.Cache;
import org.springframework.data.redis.cache.RedisCache;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.util.CollectionUtils;

import java.util.*;

/**
 * @Description RedisCacheManager 
 * @Author huangd
 * @Date 2019-12-31 16:09
 **/
@Slf4j
public class CustomizerRedisCacheManager extends RedisCacheManager {

    private static final char SEPARATOR = '#';  // 过期时间分隔符,可以用其他符号

    public CustomizerRedisCacheManager(RedisOperations redisOperations) {
        super(redisOperations);
    }

    @Override
    protected RedisCache createCache(String cacheName) {
        long defaultExpiration = computeExpiration(cacheName);
        int expireIndex = cacheName.indexOf(SEPARATOR);
        if (expireIndex > 0) {
            defaultExpiration = getExpirationTime(cacheName, expireIndex, defaultExpiration);
            cacheName = cacheName.substring(0, expireIndex);
        }

        return new RedisCache(cacheName, (isUsePrefix() ? getCachePrefix().prefix(cacheName) : null),
                getRedisOperations(), defaultExpiration);
    }

    /**
     * 缓存时间
     * @param name cacheName cache#3600
     * @param expireIndex
     * @param defaultExpiration 默认过期时间
     * @return
     */
    protected long getExpirationTime(String name, int expireIndex, long defaultExpiration) {
        String expirationAsString = name.substring(expireIndex + 1);
        try {
            Long expiration = NumberUtils.toLong(expirationAsString, defaultExpiration);
            return expiration.longValue();
        } catch (Exception e) {
            log.error("获取指定cacheName:{},缓存失效时间异常:{}", name, e.getMessage());
        }

        return defaultExpiration;
    }

}

设置redis的key序列化

@Configuration
public class CustomCachingConfigurerSupport extends CachingConfigurerSupport {

    @Bean
    public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        template.setKeySerializer(new StringRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());
        template.afterPropertiesSet();
        return template;
    }

    @Bean
    public CustomizerRedisCacheManager redisCacheManager(RedisTemplate redisTemplate) {
        CustomizerRedisCacheManager redisCacheManager = new CustomizerRedisCacheManager(redisTemplate);
        redisCacheManager.setUsePrefix(true); // 写入redis中保留cacheName前缀
        return redisCacheManager;

    }
}

使用@Cacheable验证缓存失效时间:

@Cacheable(value = "ota.vehicle.series#20", key = "#k + '_' + 123")
    public String testExpire(String k) {
        return "test expire";
    }

缓存20秒后过期,效果如下:
在这里插入图片描述
20秒后过期,key被删除
在这里插入图片描述

这里需要注意一点:因为cacheName带上了#20,这样写到redis中也会带上这个前缀,所以需要去掉这个前缀,上面CustomizerRedisCacheManager 类中cacheName = cacheName.substring(0, expireIndex);
就是将此部分截断再写入。

你可能感兴趣的:(开发笔记)